关于psv某文字游戏汉化的研究


首先想提醒大家的是,根据现行法律,未经著作权人等许可的游戏汉化是一种侵权行为,我曾经听说过一种说法,即只要不以营利为目的进行汉化则没问题,但我认为这是个有问题的说法。当然自我学习之用则可以。本心得以psv文字游戏《可塑性记忆》为例,向大家展示我汉化的流程。以下内容仅为个人观点,若有欠妥之处我会及时修改。下面是干货。

汉化总步骤:
    |---- 解包
    |      (然后分析解包出来的文件,你至少得搞清楚要汉化的东西都在哪些文件里吧)
    |                             |--- 提取文本
    |       |---文本汉化- |    翻译         
    |       |                     |---替换文本
    |---- |
    |       |                    |---P图
   |        |---图片汉化-|
    |                            |---保存(是的,这一步非常重要而且让人掉头发)
    |
    |---- 制作字库
    |
    |
    |---- 视频汉化(如果你想的话)
    |
    |---- 打包



解包:
想必第一步就让好多人望而却步。就一个psv游戏而言:

  1. 首先准备一个mai版的游戏文件,一般是压缩包,解压后游戏文件尽收眼底。

接着初步分析目录:

  • mai_moe文件夹:不用管,是安装mai用的
  • movie文件夹:无需多说,该游戏内的一些视频
  • sce_module文件夹:不知道干什么用的,里面文件后缀为.suprx,是psv的系统文件
  • sce_sys文件夹:里面有些图片,随便看看你就知道是什么了,psv标志的贴纸和奖杯等

下面的文件都是未知格式xxx_body.bin和xxx_info.psb.bin等,而且是成组的,可以看到有几个占了很大内存,猜测游戏本体就是由这些文件组成。

注:解包这些未知格式文件就是最棘手的问题了,你可能需要大量时间在网上寻找是否有前辈提供解包工具,或者自己造轮子,但是我想大部分人是没有能力自己造的,所以只能碰运气去找,找到了就有汉化的可能,找不到就只能放弃。

这次运气好,在github上找到了某大神的一个工具FreeMote,貌似该工具的初衷并非用于汉化,但客观上确实能解包该游戏的重要文件,其详细信息在此不做赘述。(请正确上网,不FQ,github无需FQ)

        2. 用FreeMote解包xxx_body.bin和xxx_info.psb.bin文件(解包方法略)

注:需要说明的是解包这类文件需要一个key,若想获得该游戏的key需要在未解包时的bin文件源码中找,总之挺麻烦。

以font_body.bin和font_info.psb.bin为例:解出来一个文件夹和两个json文件

文件夹内也是json文件。先在psv上打开游戏看一下游戏第一句话,记下来。用notepad随便打开一个json文件,search一下全文件(指定搜索路径可以提高效率)是否存在第一句话,有的话则说明文本已经解出来了。接下来只要找到文本文件即可。



文本汉化

虽可以直接在源文件上翻译文本,但效率过低,如能提取出来做成excel就可以批量翻译了,我选择python解决此问题。对于翻译这种小项目,无需使用面向对象的方式编程,简单的函数足以应付:

1. 提取文本小程序

 1 import json    #用于解析json文件
 2 import csv      #用于保存csv文件
 3 
 4 def savefile(filename,words,name,nikname):
 5     path2 = r"C:\Users\xxxxxx\scenariotest\get_word\{0}".format(filename)
 6     with open(path2,'a+',encoding='utf-8') as csvfile:
 7         writer = csv.writer(csvfile)
 8         writer.writerow([name,nikname,words])
 9     csvfile.close()         #该函数用于写入csv文件并保存
10 
11 def get_word(path):
12     file = path.split("\\")[-1]
13     change = file.split(".")[0]
14     filename = change + ".csv"
15     fp = open(path,encoding='utf-8')      #注意解码用utf-8
16     json_data = fp.read()                           #读取json文件
17     data = json.loads(json_data)
18     data01 = data["scenes"]                       #定位到一级标签
19     for m in data01:
20         if ('texts' in m.keys()):            #定位到text标签
21             print("loading.......................")
22             for i in m['texts']:
23                 words = i[2]                     #获取文本
24                 name = i[0]
25                 nikname = i[1]
26                 if name == 'NULL':
27                     name = ' '
28                 if nikname == 'NULL':
29                     nikname = ' '
30                 savefile(filename,words,name,nikname)
31         else:
32             print("NULL") 
33             continue
34 
35 if __name__ == '__main__':
36     file_path = r"C:\Users\xxxxxx\pm1-01.txt.scn.m.json"      # 改文件名
37     get_word(file_path)

 

思路:观察得知每个文件中“text”标签后即一堆数组,文本就是数组中的一个元素,用json包直接定位到文本即可,然后保存为csv格式。

使用方法:改一下程序中要提取的文件名后运行

运行结果:以示例文件名为例,生成一个文件:pm1-01.csv,用excel打开即可

2. 翻译小程序

虽可逐句手动翻译,但奈何我日语5级都难过,不如先让度娘翻译一遍,再修修补补即可:

 1 import requests
 2 import hashlib
 3 import json
 4 import csv
 5 import time
 6 # python3.0 已经取消了MD5,需要使用hashlib
 7 # 该程序最后输出的文件为xxxfanyi.csv   文件只包含翻译结果,每隔一句空一行
 8 
 9 # 获取csv中文本
10 def getword(path):
11     csvfile = csv.reader(open(path,'r',encoding='utf-8'))
12     words = []
13     i = 1
14     for line in csvfile:
15         if i%2 == 1:
16             words.append(line[2])
17         else:
18             pass
19         i = i + 1
20     return words
21 
22 # 翻译函数,具体使用方法在api申请页面有教程
23 def fanyi(str):
24     q = str  # 句子
25     ifrom = 'jp'  # 翻译源:日语
26     to = 'zh'  # 翻译到:中文
27     appid = 'xxxxxxxx'  # id号
28     key = 'xxxxxxxx'  # 密钥
29     salt = 'xxxxxxxx'  # salt随便取一个
30     # appid+q+salt+密钥 的MD5值
31     sign_1 = appid + q + salt + key
32     sign = hashlib.md5(sign_1.encode(encoding='utf-8')).hexdigest()
33     # 产生url
34     url = 'https://fanyi-api.baidu.com/api/trans/vip/translate?q={0}&from={1}&to={2}&appid={3}&salt={4}&sign={5}'.format(
35         q, ifrom, to, appid, salt, sign)
36     try:
37         res = requests.get(url=url)
38         words = json.loads(res.text)
39         words = words['trans_result']
40         words = words[0]
41         words = words['dst']
42         return words
43     except BaseException as e:
44         print(e)
45 
46 # 最后输出
47 def pack(list01):
48      path = r'C:\Users\xxxxxxxx\pm1-01fanyi.csv'             # 改文件名,这是最终生成的文件名
49      f = open(path,'w+',encoding='utf-8')
50      writer = csv.writer(f)
51      for n in list01:
52          writer.writerow([n])
53 
54 if __name__ == '__main__':
55     # 获取word
56     path = r'C:\Users\xxxxxx\pm1-01.csv'       # 也改一下文件名,这是之前生成的那个csv文件
57     list01 = []
58     m = getword(path)
59     num = 0                        # 计数用于调试
60     for j in m:
61         print('waiting.......')
62         a = fanyi(j)
63         list01.append(a)
64         time.sleep(1.5)
65         num = num+1
66     pack(list01)

 

思路:百度翻译有个api接口,自己申请一个就可以用了,实名后貌似可以每秒翻译10句,但网速实在不给力,而且我也懒得做错误处理了,就写成个半成品。直接利用之前生成的csv翻译再生成一个翻译版csv,就这么简单。

使用方法:在程序中改需要翻译的文件名后运行

运行结果:以示例文件名为例,生成一个文件:pm1-01fanyi.csv,用excel打开即可

3. 导入小程序

 1 import csv
 2 import json
 3 # 本文件需要03.csv和03mid.csv两个文件
 4 # 生成文件即03.csv,需要翻译名字
 5 
 6 def csv_read_word(path):
 7     strword01 = []
 8     flag = 1
 9     file = csv.reader(open(path, 'r', encoding='gbk'))
10     for line in file:
11         if flag%2 == 1:
12 #             print(line)
13             strword01.append(line[0])
14         flag = flag + 1
15     return strword01
16 
17 def csv_read_name(path,h):
18     strword02 = []
19     flag = 1
20     flag2 = 0
21     file = csv.reader(open(path, 'r', encoding='utf-8'))
22     for line in file:
23         if flag%2 == 1:
24             strword02.append(line[h])
25         flag = flag + 1
26     return strword02
27 
28 def recsv(word,name,nikname,csv_path):
29     with open(csv_path,'w+',encoding='utf-8') as csvfile:
30         writer = csv.writer(csvfile)
31         for i in range(0,len(word)-1):
32 #             print([name[i],nikname[i],word[i]])
33             writer.writerow([name[i],nikname[i],word[i]])
34     csvfile.close()
35 
36 if __name__ == '__main__':
37     mid_path = r'C:\Users\xxxxxxx\pm1-01mid.csv'       # 改之前的pm1-01fanyi.csv文件名为这里的文件名
38     csv_path = r'C:\Users\xxxxxxx\pm1-01.csv'          # 改文件名,为源文件名
39     word = csv_read_word(mid_path)
40     name = csv_read_name(csv_path,0)
41     nikname = csv_read_name(csv_path,1)
42     recsv(word,name,nikname,csv_path)

 

思路:这个程序就有点无厘头了,不知道我当时怎么想的。。。总之有很大的修改空间。具体来说就是将之前翻译好的pm1-01fanyi.csv导入到源文件中,即第一个程序的逆向,没有什么技术难度。

注:这个程序一个大问题是,因为galgame的文本有对应的人物名字,我在第二个翻译程序中忘了翻译人名,导致这里想再翻译人名就很麻烦。

使用方法:修修补补完成pm1-01fanyi.csv文件翻译后,将该文件的文件名改为pm1-01mid.csv,然后改一下程序中的文件名,运行

运行结果:导入完成。

至此,文本翻译告一段落。



图片汉化

 1. 该游戏中,游戏菜单等内容并非以文本形式存储,而是图片,因此还要翻译图片。和解包文本相同,解包图片后是一堆png格式图片,用photoshop进行修图翻译即可。(这里要感谢FreeMote工具的大佬帮忙更新了工具,能够打包了,否则只能解包,图片汉化就不可能了)

 2. 保存:保存之所以成为一个问题,是因为如果保存格式不正确,放回游戏后显示会有问题,这就好比你用txt打开一个exe文件,改几下后再保存,虽然它可能还是一个exe后缀的文件名,但电脑可能完全运行不了它了。同理,虽然你用ps保存为png格式,但与原来的那个png已经完全不同了。这个问题我暂时没有找到好的解决办法,只能尝试改变保存时的各种参数来碰运气。最难过的是,游戏中不同的图片,保存格式也不同,而有一些图片你试完了所有ps提供的可变项后,还是无法正确运行,好在只有少数不重要的图片,不然你可能要气炸了(忙了大半个月了就因为这玩意,毁了所有努力啊有木有!!!)有了解图片格式的大佬可以自行研究解决。


 

视频汉化

关于视频汉化,就是加字幕了,该游戏的一些剧情是以视频形式表现的,但没有字幕,日语听力还是饶了我吧。。。我还未进行实践,自然不知道结果如何,用pr加字幕是没有问题,但是不是和图片一样有保存的问题就不得而知了。



字库

字库也一度让我止步不前,但只要理解了原理,造轮子不是问题。

1. 字库是什么:游戏中的文本本质上是图片,字库就是一堆字的图片拼在一起而组成一张大图,有点像排好的活字印刷术的活字。当需要显示时,将映射表中的文字映射到大图中的某个坐标从而调取到字图,得以显示。

2. 怎么做字库:我找了很多生成字库的工具,但总有各种问题,如字图大小不一,顺序混乱等,最终只能自己解决了。

  • 到网上找常用字集合,我找了一个3500字集合,做成txt

注:应当为一个矩形,不要一行多一行少

  • 在解包的文件中找到字库,该游戏字库在font文件夹中,且有好几个字号的字库,不知道为什么要这么多字号,实际只需改其中一个即可,直观感受一下最终需要达到的成果

其中一个字号的解包结果

  • 编程生成字库

字库小程序:

 1 import os
 2 from PIL import ImageFont,ImageDraw,Image
 3 
 4 def Read():
 5     fp = open(r'F:\pycharm\xxxxxx\3500字_new.txt','r',True,encoding='utf-8-sig')
 6     while True:
 7         word = fp.read(1)
 8         if not word:
 9             break
10         yield word
11     fp.close()
12 
13 if __name__ == '__main__':
14     m = 5                 # x轴
15     n = 5                 # y轴
16     wordlist = []
17     text = Read()
18     im = Image.new("RGB", (2048, 2048), (255, 255, 255))
19     dr = ImageDraw.Draw(im)
20     font = ImageFont.truetype(os.path.join("fonts", "goodfont01.ttf"), 22)
21     for word in text:
22         wordlist.append(word)
23     # 逐字写入
24     for i in range(1, 46):            # i列
25         for j in range(1, 79):        # j行
26             flag = (i-1)*78+j         # 第flag个字
27             if flag <= 3500:
28                 dr.text((m, n), text=wordlist[flag-1], fill="#000000", font=font)
29                 m = m + 26
30             else:
31                 break
32         print("{0}层完成".format(i))
33         m = 5                         # 换行定位
34         n = n + 26
35     im.show()
36     im.save('font_new.png')

 

思路:制作字库就三步,读取文字、定位、写入,读取文字即读取txt文件中的3500字中的一个,定位即定位到这个文字应该放在哪里,写入不说了。我使用了PIL包,先生成一张底图,再逐字写入即可,这个包还可以设定底图的颜色,字号,字体颜色等。但问题是我需要透明底图,PIL貌似没有透明这个选项。因此只能先白底黑字再ps反色抠图解决了,实际效果强差人意,因为我不知如何将字调为单色透明度,如该游戏字库图实际上是白色字且有透明度的,抠图的结果是一堆狗牙,但至少做到了排列整齐。

  • 修改映射表

你可以在和字库图一起解包的json文件中找到,有很明显的规律,只要将里面文字的部分用3500字重新写一遍,覆盖原内容即可,程序很简单就不做解释了。

映射表小程序:

 1 if __name__ == '__main__':
 2     alljs = []
 3     jstext = []
 4     x = 1
 5     y = 213
 6 
 7     # get words
 8     fp = open(r'F:\pycharm\xxxxxxx\3500字_new.txt','r',True,encoding='utf-8-sig')
 9     while True:
10         word = fp.read(1)
11         if not word:
12             break
13         unit = '"first": {"a": 0,"b": 20,"d": 24.0,"h": 24,"height": 24,"id": 0,"w": 24.0,"width": 24.0,"x": second,"y": third}'
14         unit2 = unit.replace('first',word)
15         jstext.append(unit2)
16     fp.close()
17 
18     # change x,y
19     for i in range(1,46):
20         for j in range(1,79):
21             flag = (i-1)*78+j
22             if flag <= 3500:
23                 str01 = jstext[flag-1].replace('second',str(x))
24                 str02 = str01.replace('third',str(y))
25                 alljs.append(str02)
26             x = x + 26
27         print('完成{0}行'.format(i))
28         x = 1
29         y = y + 26
30     print('完成')
31 
32     # write file
33     fp02 = open(r'F:\pycharm\xxxxxxx\font24.txt','w+',encoding='utf-8')
34     for m in alljs:
35         m = m + ',' + '\n'
36         fp02.write(m)
37     fp02.close()

最终结果无需追求源码的缩进,只要符合json格式即可,如可以做成这样:

  • 如果翻译中有些字在3500字中没有怎么办?先在映射表里找一个不常用的字,改掉它。再到字库图里把相应的字图改掉。(或者重新生成)


打包

用FreeMote反向操作一下即可。这一步的问题是为了方便安装,究竟是制作汉化补丁,还是整体打包替换,但该问题已经超出我想讨论的话题了。



结语

若完成以上所有工作,该游戏即汉化完成。但可知这其中仍有很多问题有待解决,如对于人名的汉化我并未完成自动化,图片的汉化尚存漏洞,字库的颜色问题,当然还有丑的不行的代码 T _ T。总之尽善尽美是很困难的。另外,上述方法也不具有通用性。最后希望这些看似已经脱离时代的东西能帮助到有需要的人吧。

最终效果:


免责声明!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系本站邮箱yoyou2525@163.com删除。



 
粤ICP备18138465号  © 2018-2025 CODEPRJ.COM