战国兰斯吧 关注:125,165贴子:2,489,086

【见解】AlicesoftGame汉化破解新手向

只看楼主收藏回复

近几天写的心得,下午发上来


回复
1楼2014-08-10 11:57
    序言
    一个游戏要汉化主要有两个难关,一是程序破解,二是剧本翻译。两个要素无论哪一方都很重要,不可或缺。程序破解更讲难度,而剧本翻译则更要求持久力。但是,对于现前的Alicesoft的游戏来说,其实程序破解的压力要比剧本翻译的压力来的小,因为“前人种树后人乘凉”,程序方面可以享受到这么一个优惠的条件。翻译则不一样,每一作都要重新开始,能吃到前人的好处的也就一些neta或是人名地名。所以翻译怕懒,懒掉了的话就坑掉了。没有好耐心,当不了好翻译。
    当然,程序也需要一颗好耐心。无耐心,不成事。下面我会以Rance9汉化为例来谈A社游戏的一种汉化方案。为什么说是一种,因为汉化的实现方式并不只有一种,但最常规的还是把程序显示文字时调用的系统编码改为GB2312字符集,使程序在中文环境下的电脑上运行时能够正确显示GB字符集的汉字。其他方案还有调用自制字库等,见解并未接触,在此不讨论。
    在序言部分声明一下,见解在文中所说的GB2312指的是代码页936的名字,代码页936早期被映射到GB2312-80,也就是常说的GB2312,这个字符集字库很小,基本上只包括了简体字。后来GB2312拓展为GBK,GBK能向下兼容GB2312-80,另外又包含了很多繁体即其他字符。如今的代码页936也不再映射到GB2312-80而是映射到GBK。所以文中所说的GB2312严格意义上应理解为代码页936的名字,实指的是GBK字符集。
    话题回过来,其实在接触程序之前,见解也觉得程序破解很困难,自己没那技术,根本做不到的。但是凭着C语言和一些汇编基础,再加上参考了一些论坛的贴子和网上下载到的书籍,在没有任何破解经验的基础上,探索了半个月,跌跌撞撞地完成了一个汉化的方案。当然若是没有前人种的树,凭我的渣技术和这一点点紧凑的时间,做出一套较为完整的方案肯定是天方夜谭。不过这个时代就是这样:N3能力的翻译员借助互联网的词典和搜索引擎,也能达到N2的水平;有C语言基础的程序员,凭借着互联网的Microsoft Developer Network和搜索引擎,也能编出能正确运行并实现预期功能的C#程序。能够利用外力和使用工具,向来是人类的特点,这也是智能的一种。
    其他的也不多扯了。下面的正文部分是写给有一定基础(C语言和汇编语言)的,对程序破解感到好奇但不知如何入门的同学看的。见解也是纯新,最多算半入门而已。自己摸索了半个月的Alicesoft的程序,今天想跟大家分享一下这些天的心得。


    收起回复
    2楼2014-08-10 11:58
      Alicesoft游戏框架及破解环境
      从哪里开始讲起好呢?先准备好一套要破解的游戏吧。见解就以Rance9为例,拷贝出一份日文原版的Rance9游戏程序出来,作为做工场地。
      做程序,要养成勤备份的好习惯。当然不做程序也要备份,比如SL啊什么的。图上备份了1.0x和2.0的两份补丁。A社的补丁,主要都是对ain文件的修改,较大的补丁则会追加CG文件。为什么呢,因为ain文件里是游戏程序的主要代码,包括剧情文本和游戏参数,比如战斗计算公式、物品获得概率。而在近些年引入ex文件以后,一部分的游戏参数被转移到了ex文件之中。相比ain和ex文件直接与游戏的上层程序相关,exe则更加底层一点,像是一个程序的入口,来挂载ain和ex,加载程序,然后分配系统资源,在合适的时机读写文件,调用CG资源等等。总而言之,以上所说的exe、ain和ex是程序的最重要的三个部分,也是破解的最大难关。你可以发现三个文件加起来大小不超过10M,而整个游戏有2G多。但正是这10M,也是游戏的精华。任何游戏,要优化的好,或是要系统做的别出心裁,就看这10M。时常注意这三个文件的修改日期,因为要不断的修改替换和备份,只有修改日期最能体现出它的特征。

      程序部分以外,还有很多资源文件,像是CG.afa,里面就是CG资源。注意对CG的理解不要偏差,广义的CG是指一切在游戏出现的图片资源,包括立绘、背景这些,都在这个文件里封装着。这文件是改图的工作场所。
      Font.fnl为A社自带的字库文件,里面是日文字体的字体库。什么是字库,为什么有的游戏没有自带字库。因为一般操作系统都自带字库,当系统要显示文字时,根据当前字符集,读出字符编码,依照这个编码,去字库里面找到对应的字出来显示。A社近年的游戏都带了自制字库,也就是说不调用系统字库,而显示出自己设计的漂亮的字体。可惜汉化要用的简体字在A社的字库里是没有的,所以我们要做汉化还是要调用系统默认字库。当然,有闲情逸致可以自己设计字体,开发一套字库来调用,但我想我们只是搞汉化又不是在开发游戏,这么费苦劳也不太合适。
      图上红框里的是2.0补丁追加的CG和Flat资源文件。为什么有4个,其实只有两个,另外两个是相同内容不同文件名的文件。为什么要这么做呢,因为exe在调用资源时,会按文件名查找文件。中文环境下,exe查找文件名函数,使用的参数是日文文件名的shift-jis码,由GB2312码读取出来就是乱码。系统会根据这个乱码去找文件,找到对应文件则调用。而如果是日文环境,exe查找文件名函数的参数则被系统正确识别为日文字符,能够正确找到日文文件名的文件。转区和Microsoft Applocale都可以做到中日文环境的转换。但为什么很多日文游戏,不用转区和app打开,就能正确打开并显示日文呢。正确打开,说明各种文件都调用成功,很可能文件名中不包含日文字符,而无论GB还是shift-jis字符集,英文字母都是相同的编码,所以不影响;正确显示日文,是因为指定了字符串的编码,系统按照指定的编码去转换字符了。所谓中文环境和日文环境,也就是“非 Unicode 程序的语言”是中文还是日文的区别,这指的是在程序没有指定字符集时,系统会选择那种语言作为默认字符集。关于字符集的论述,在稍微后面点的内容还会讲到。


      下面要讲到的是破解环境,也就是前人工具集成。当然图中并不是全是用来破解A社游戏的,很多是其他游戏的拆包工具。破程序最常用的ollydbg,这是必备的,用来调试exe,功能强大。ollydbg有官网,更新到2.x了。ProcessMonitor是一款进程监视工具,用途广泛,在我们破程序过程中也能起到一定作用。crass不用说,泛用的acg拆包工具,不过我们用不到,而且好像已经停更了,前浪貌似被扑倒沙滩了。A社游戏解压文件夹里还有一些重要工具。


      回复
      通过百度相册上传3楼2014-08-10 12:32
        aindecompiler是很重要的工具,因为很重要所以我存了三个版本?嘛,其实只要用最新版本,对于需求方面的差异可以通过修改代码实现。因为aindecompiler的作者公开了source code,即源代码,这点对我们非常有帮助,这样就可以根据自己的需求对ain编译/反编译工具进行调整,以达到自己预期的效果。当然,要看懂整个程序大体的工作流程还是挺费时间的,当初有段时间见解看着看着感到头大,差点放弃,不过最终还是熬过来了,熬过来就是胜利啊。
        sys3Decompiler也是ain编辑工具,但是它针对的是sys39的引擎,即exe名字为system39的A社游戏。近年的作品早都采用sys4x了,所以sys3de不适用,而要采用aindecompiler工具。当然sys3de有其用武之地,比如Daiakuji这种好多年前的优秀作品。另外还有更老的游戏用的sys35或其他更老的引擎,这种可能会有相应工具,不过可能比较难找了。
        sys42SDK是官方公布的SDK,其实相当于公开了一个架构和思路,给了一套模板。里面有一些工具可以参考。
        exafa和exald是拆解afa和ald资源文件的工具。exafa有两个版本了,A社封装CG文件的方案大致没变,不过不时会有些小变动。exafa拆解出来的图片资源为QNT格式,要再通过al2010cv工具转换为bmp或png等图片格式。
        这样整体架构就基本讲完了,具体细节还要动手深入才会了解到。


        回复
        通过百度相册上传4楼2014-08-10 12:32
          显示文字时修改调用GB2312编码
          为了让程序显示字体时能显示出正确的简体字,见解的方案是让系统调用CreateFontIndirectA的API,使用GB2312字符集。什么是API,API就是Application Programming Interface,应用程序编程接口。API是操作系统提供给用户的比较底层的函数,通过这些函数可以读写文件、显示关闭窗口、弹出消息框等等基础的操作系统功能。CreateFontIndirect就是创建一种逻辑字体的函数,它带有参数,比如字体大小、字符集等等。若是能在exe中断到这个函数,在设定字符集参数的地方改成设定GB2312编码的话,我们的目的就达到了。打开ollydbg,打开rance9.exe。
          有没有觉得很洋气?回家几天就靠这个在爸妈面前挣面子了,以前抱着个电脑总是打游戏打游戏的,老是被说,现在就不会被抱怨了。话说见解现在自己也觉得打游戏打游戏的很没意思啊,游戏有什么好打的,还不如玩玩这么洋气的软件233。废话不说了,ollydbg主要窗口分4个部分,左上角汇编代码,即程序块,右上角为寄存器,左下角内存,右下角堆栈。我也没系统学过,自己摸索的,有说错的话见谅。
          刚打开exe时程序块停住的语句就是程序入口了。我们当然不会一行一行去看,用万行计数的汇编代码,哪里能看?我们要做的是定位到我们想要修改的地方进行修改。我们要断CreateFontIndirectA。在程序窗口右键go to——》expression。或者快捷键ctrlG。

          输入createfontindirect,可以找到这些函数入口。我们选中A并进入。
          进去以后,我们按F2键下断点,试试能不能断下来。按F9进行调试。


          回复
          通过百度相册上传5楼2014-08-10 12:33
            结果很遗憾,无论是标题界面还是到对话框出来,ollydbg都没断下来,也就是说程序运行过程中没有调用到这个API。这时我们想到了fnl的自带字库,多半是因为它的缘故而不调用系统默认创建逻辑字体的API。那么怎么做呢?删掉fnl,我真是太机智了,可惜不仅文字不显示了,ollydbg也照样断不下来。因为程序还是没有去调用CFI。这时可以参考PRO的修改建议,搜索<CMP DWORD PTR DS:[EBX+4],100>。代码区右键,searchfor all commands。
            搜索命令必须输入完整,不能少参数。


            收起回复
            通过百度相册上传6楼2014-08-10 12:34
              如果带上“DS:”段提示符却找不到的,可以尝试像图上那样,去掉这个段提示符再搜索一下,因为默认段是DS。可以找到两个地方,按pro的修改建议把100改成1FE试试看。
              像这样改完,然后运行调试。注意保持CFIA的断点为active。


              收起回复
              通过百度相册上传7楼2014-08-10 12:34
                这样,在op过了之后就在CFIA函数处断下来了。说明修改了那处的100判定之后,程序调用了CFI的API函数。这时看右下角堆栈窗口第一行,找到函数返回地址,ctrlG跳转过去。
                在调用CFI的入口处下个断点,以后可以用到。里面的断点用不到了,可以delete掉。可以看到在调用CFI时,上面logfont参数已经赋值完毕了,赋值过程应该在不远之前。Logfont结构看不完整,我们下次断到这边的时候可以看得更仔细。
                虽然就这样一步步走下去也可以,但见解这次要采取的方案不是改pro建议的100参数的修改。Ollydbg点关闭调试,那么之前的修改都不会保存,但是断点和注释会保留。不保存100的修改关闭。
                100是hex值,换成十进制为256。256是什么呢?我们打开aindecompiler,解开rance9.ain。菜单tool-》find-》int。


                搜索256,可以找到很多使用了256这个常量的函数。我们希望找到跟字体相关的。


                收起回复
                通过百度相册上传8楼2014-08-10 12:36
                  找到这个函数:字体种类_确认。看下函数,也就是说0,1还有256到262都是合理字体。再找一些其他的函数看一下,大致可以猜测出:0是Gothic字体,1是明朝字体,这两种字体是日文系统自带的,中文环境windows可能不是自带,但可以去网上下载到这两个字体库安装。而256到262的字体则是来自外部文件,即来自fnl字库文件里面。我们搜索设置字体的函数。找个函数试试看。在ainde里面不能直接修改代码,我们要先导出asm代码,修改之后再导入。

                  虽然也有导出c代码的选项,但功能不是很稳定,我们导出asm汇编进行修改。找到相应的文件打开。


                  回复
                  通过百度相册上传9楼2014-08-10 12:37
                    通过ctrlF找到对应处。注意,导出的jam都是shiftjis编码,所以打开时要用指定编码打开。另外文本编辑工具尽量用比txt高级点的,比如见解用的EmEditor,各方面都还不错。
                    如上改好并保存,在ainde里导入asm,查看修改结果。


                    回复
                    通过百度相册上传10楼2014-08-10 12:38
                      注意勾选第二个多选框,表示导入后直接打开新的ain,否则看不到改动。修改成功。这样我们把改过的ain复制到游戏文件夹覆盖。打开ollydbg调试,保持CFI的断点为允许状态。
                      和修改100时不同,标题画面并没有断下来,而且标题的开始和读取按钮的字体仍是和原来没变。但是点开始游戏(はじめから)按钮后,ollydbg就抓到断点了。


                      回复
                      通过百度相册上传11楼2014-08-10 12:39
                        我们可以看到logfont的内容。如果看不全,在堆栈区右键,有显示水平滚动条的选项,可以左右滚动查看。
                        仔细来看这个堆栈区,0012EFF0存的是指向logfont数据的指针,logfont的位置在0012eff8。Logfont的结构可以去搜索一下,可以知道前五个long型参数,也就是4字节参数。从堆栈区看,第一个是height,字高,是FFFFFF58,其他都是00000000。接下来0012f00c就是四个byte参数,第四个byte参数是CharSet字符集,上网搜一下,知道80代表的是shiftjis,86代表GB2312。那么我们的目标就是把80改成86,当然直接改堆栈是不对的,我们要找到赋值80的地方进行修改。另外,0012F014的地方,是font face name,即字体名。它是TCHAR数组参数。堆栈区显示不出shiftjis字符集,我们在内存区右键,跳转到这个位置0012f014。
                        左下角内存窗口就可以清楚地看到这个字符串对应的shiftjis编码的文字是什么了。注意,默认设置下hex dump右边显示的是ascii码,我们要到菜单Option处进行如下修改。


                        回复
                        通过百度相册上传12楼2014-08-10 12:40
                          然后在内存区域点标签转换到multibyte code显示,就能看到shiftjis文字了。字体名为MS Gothic,和我们修改的一致。因为我们之前在ain里把message字体赋值时统一赋成0,也就是Gothic字体。代码区,顺着断点往上找找,虽然有个CALL,我们先跳过,不远处就看到了给堆栈赋值80的地方。这个就是设置字符集的地方。当然,如果不确定,可以在这里下断点,然后重新调试,断到这里以后,用F8单步运行,确认是不是这个参数。当然见解已经知道就是这里,我们把80修改为86。保存修改:右键edit-》copyall modification to execute。
                          然后右键全选,save file。


                          回复
                          通过百度相册上传13楼2014-08-10 12:40
                            重新运行,可以看到对话框显示GB乱码了。说明修改成功,程序成功调用CFI创建了使用GB2312字符集的字体,另外字体名其实基本上也在我们的掌控之中,到ain里找到对应的地方,把0,1所对应的字体名改成想要的字体名,很有可能就能实现使用理想的系统自带字体。当然上图中那个字体显然不是Gothic。系统应该是调用了默认的适用GB2312编码的字体,应该是宋体吧。
                            有关字体的参数,很可能在这两个函数内:


                            对case0的字符串进行修改可能达到想要的结果。虽然这只是猜测,见解还尚未验证。
                            总结一下,在ain里setfont处修改字体类别为0或1,可以避开fnl文件的调用,而且具有针对性,对特定部分的字体进行字体和编码修改。这样可以实现不影响其他部分的区域汉化,比如只汉化对话框内的文字,而界面如标题等可以保留日文原文和原字体效果。等到要汉化标题模块字体时再进行相应区域的setfont函数修改。这样可以做到精准定位和局部修改。
                            而pro的修改建议,则是一劳永逸的把所有调用fnl字库的地方全部改为调用CFI创建系统字体,简单快捷。
                            因为考虑到今后必须对每一部分的文字的大小、粗细、间距进行精确微调,所以见解在本次示例中采用了局部修改的方法。改字体参数这一块便成了很累的活,要把全程序的字体函数关系理出个大概来,很麻烦。但无论哪种方法,也逃脱不了这个环节,除非仍然调用fnl文件,对fnl文件内增添GB2312字符,或是修改。见解估计这么做的工作量也不会低,而且字形方面也是问题。这也是A社引入fnl自带字体库文件后带来的麻烦,当然对于日文玩家来说,能看到漂亮的字体当然是件毫无弊端的好事。
                            字体方案究竟何去何从,我们在下面一节更加深入地探讨一下。


                            收起回复
                            通过百度相册上传14楼2014-08-10 12:42
                              字体显示深入探究
                              为了探究字体显示,我们还是先断点断到CFIA,并把内存窗口转到logfont的字体名字符串位置。
                              在内存区选中那串字符串,0x00是字符串结束标志,一起选中。右键,edit-》binary edit。当然你可以在MBCS里面改,不过为了确保准确性,我们直接改HEX值。我们尝试调用楷体。那我们就要知道“楷体”这个字符串的编码。这时候我们就碰到问题了,应该使用什么编码来表示“楷体”这个字符串?用shift-jis呢还是GB2312呢?
                              前面我们讲到,根据程序运行的环境,程序会有不同的非unicode程序语言,在未指定编码的情况下采用默认的某种编码。如果用app选择日文加载,那么默认编码就是shift-jis,现在因为我们exe是裸开,以后也是,所以默认编码是GB2312,所以我们需要改的“楷体”的HEX值应该是GB2312编码的表示值。这样就可以解释之前的问题了,虽然shift-jis编码表示的字体名为“MSゴシック”,但系统会采用GB编码值来读取这个字符串,读到的字体名就是乱码,然后系统因为找不到这个字体,所以调用了默认的宋体。这就是我们一开始看到的字体。
                              怎么查看“楷体”两个字的GB2312编码的HEX值呢?我们打开文本编辑器,输入“楷体”,用GB2312编码保存,然后用十六进制视图查看这个文件就可以了。记事本基本上做不到,所以见解推荐过大家用高级一点的文本编辑工具。


                              转换一下可以知道,“楷体”二字的GB2312编码值为BF AC CC E5。


                              回复
                              通过百度相册上传15楼2014-08-10 12:43
                                如上改好,后面空位补0就行了,不要去改变字节长度,可能会引发未知的错误。F9继续调试,直到离开断点,我们看到游戏程序显示出了楷体字。
                                比起之前生硬的大概是宋体模样的字要好看点了,带上了楷体字特色的笔锋。但是显然文字大小间距还需要调整,这很费功夫。既然游戏程序里有gothic和明朝这两种字体供调用,那么这两种字体对系统设置的字体大小间距应该有很好的适应性。那么我们尝试让CFIA调用这两种字体,看能不能省去对字体大小和间距的调整。
                                如上图修改为“MSゴシック”的GB2312编码值。


                                回复
                                通过百度相册上传16楼2014-08-10 12:44
                                  事实证明我想多了,Gothic字体库并没有那么万能。摒却奇葩的一粗一细一高一低间隔的情况,光是看字形大小,也明显感觉不合适。大概整个程序的ain里对字体大小和间距的设置,都只是针对自带字库fnl的,对于其他系统自带字体都不能很好地适应。也就是说对ain里的函数部分进行字体微调是在所难免。另外,自己系统中已安装的字体可以在控制面板-》外观-》字体里面找到,双击打开可以查看字体名和字体预览。另外为何会有字字形状不同间隔排列,可能和字体库中包含的字数有关,比如可能有“我”字的字形,但没有收入“瓁”这种奇怪的字。当然这只是见解的猜测,大家不要相信。
                                  字体先定下来用楷体,试了一下兼容性还是比较好的。但是改内存中的logfont并不是长久之计,每次CFIA的时候都会把logfont重新赋值,而logfont的fontname参数又会变回shift-jis编码的“MSゴシック”。我们要找到赋值的地方,从本源上调换字体。在logfont赋值那块地方下个断点,重启调试。


                                  断到这边,上面好几行mov语句,可以看到都是对logfont前几个参数的赋值,而对最后面的这个fontname的赋值,在这个call里面,调用了一个函数。我们可以跟进去看,找到直接赋值的地方,进行修改。但我们可以看一眼这边这个参数,在内存窗口转到01774d9c的地方,看到它指向的正是将要赋值的字符串“MSゴシック”。那么我们可以考虑另一个修改方案,直接循着这个参数,找到最本源的“MSゴシック”字符串的来历。


                                  回复
                                  通过百度相册上传17楼2014-08-10 12:45
                                    往上找eax赋值的地方,发现是跟esi有关,而esi在当前函数块并没有被赋值的迹象,也就是说esi来自函数外。我们跳出这个函数,跳到函数返回地址,在上一行的函数入口或更前的位置下断点,进一步找这个esi的来历。可以看到esi又由edi赋值,而edi在这个函数块又不见来源,再次往外跳。像这样不断往前找,找到了这个位置。
                                    这个语句就是关键了,因为这是直接寻址。我们找到[767d3c]=01774c60,加上1b4为01774e14,在内存窗口转到这个位置。


                                    回复
                                    通过百度相册上传18楼2014-08-10 12:47
                                      在上下可以找到“MSゴシック”字符串。
                                      要找来源,我们要在静态的位置下断点。重启调试,我们在直接寻址的位置,即767d3c下内存断点。

                                      F9运行调试,断到对该处进行赋值的语句。(读值就继续)
                                      断到这里后,可以试着往后调试,发现之后都一直没有对这块内存进行重新赋值。那么这里的eax就十分重要了,我们断到前面点的地方,把内存断点禁用掉。重启调试,断到我们想要的断点。


                                      回复
                                      通过百度相册上传19楼2014-08-10 12:48
                                        显然是这个函数里面对eax进行了赋值,并对eax指向的块数据进行了修改。F7跟进这个函数。
                                        跟进去以后F8单步运行,内存窗口调整到整个函数体在不断赋值的位置。运行到这个函数,F8跳过这个函数入口的话会发现下面内存窗口的值一下子都赋值结束了,包括我们要找的“MSゴシック”字符串。说明关键步骤还在里面,我们把内存块调整到eax指向的位置,也就是参数1(arg1)指向的位置,跟进这个函数。


                                        回复
                                        通过百度相册上传20楼2014-08-10 12:49
                                          再不断跟进,观察内存区域的赋值,直到出现这个“H”什么的字样,从前几次经验,后面跟着的字符就应该是“MSゴシック”,那么这个函数过程很可能就至关重要了,我们跟进去。其实在来到这里之前,会走过一个判断,那个判断语句,ollydbg贴了标签“string too long”,可以猜到是判断字符串长度是否溢出的语句,那么这附近肯定是字符串赋值之类操作的地方。过到这个函数入口,其实根本不用跟进去看了,arg3是长度,arg2是源地址,arg1是目标地址,这个函数是一个数据段复制的地方。我们在内存窗口跳转到arg2指向的地方。
                                          没什么好说,肯定就是这里了。exe中保留的静态数据存储的地方。这块地址并不是程序运行后新分配出来的内存,所以对这里的改动可以保存到exe文件中。


                                          回复
                                          通过百度相册上传21楼2014-08-10 12:50
                                            改好后,右键复制到exe,然后全选保存。当我们兴冲冲地以为大功告成时,重启调试后就发现打脸了。调用CFIA时fontname参数又变回shift-jis编码的“MSゴシック”了。在可以肯定之前的修改没出错的基础上,我们可以这么推测,是在中间环节,程序对这个fontname参数进行了检测,并重新改回了“MSゴシック”,或者说就是重新赋值了一遍。
                                            无奈我们还得再花一番功夫才能成功。那么修改的地方要怎么找才比较快捷呢?首先要确定是在什么时候程序进行了重新赋值。断到以前所说的直接寻址的语句,地址是0040F7F9。(平时自己最好多做comment注释,可以方便自己找到以前去过的关键点。)内存跳到新赋值的区域,对其监视。发现区域中某块数据变回“MSゴシック”是在点击开始游戏按钮之后。那么我们在点开始游戏按钮前,对那块“楷体”的GB2312码值(BF AC CC E5)所在的内存地址下断点。然后可以断到上面所说的数据段赋值的函数,然后顺着函数返回地址找,快的话几分钟,就可以找到这个位置。

                                            于是我们再次抓住了元凶。哦不,元凶只有一个的话,我们这次应该算是抓住了帮凶。

                                            把这个地方也改好后,我们终于看到了预期效果。话说楷体比宋体漂亮多少,其实差距也没那么大。字的大小和粗细对文字的饱满度影响很大,但这些都属于字体微调的活儿了,在ain内可以修改完成,见解在此文中可能不会多做介绍。下面将要讲的是把GB编码的剧本导入到ain中,使得对话窗口能够显示简体字。


                                            回复
                                            通过百度相册上传22楼2014-08-10 12:52
                                              AinDecompiler工具修改及汉化文本导入
                                              ain文件是程序调用的主要脚本文件,里面有重要的函数和剧本,还有很多字符串。我们要从里面导出日文文本,汉化成中文再导入进去。但是,当前的ain工具,不能完全地满足我们需要的功能,因为工具采用的编码主要是shift-jis,而对GB编码的汉字会存在识别问题。我们新建一块开工场所,可以从ainde的作者发布ainde的地方下到最新版本的ainde,见解用的这个是8月3日新下的版本,以之为示例进行必要的modidfy。
                                              据说新增了添加对话页,增加了剧本外观上的一些功能。嘛,对我们来说不重要,我们首先要对ainde的大体工作流程进行一下粗略的了解。打开source code文件夹,打开工程。见解用的是visual studio2013,就以这个平台为例给大家介绍了。打开解决方案资源管理器,可以看到里面有很多cs脚本。我们从程序入口program.cs看起。当然program.cs也主要是些准备过程,我们想看直接相关的函数部分的话,打开explorerform.cs。
                                              窗体设计视图看起来很直观。我们双击open ain file这个按钮,可以跟进按下这个按钮触发的函数。


                                              回复
                                              通过百度相册上传23楼2014-08-10 12:53
                                                像这样找到位置以后,我们只要一行行看下去就行了,重要函数F12跟进去看。当然,也可以开调试模式F10步进看,能很清楚地看到程序运行过程。见解不带着大家细看了,自己顺着走一遍体会一下。大体上open ain file的过程是如下所述的。
                                                读取ain文件头4个字节,前4字节为签名,如果为“AINI”,为system39的ain文件,本ainde不支持,会弹出提示框提示转用sys39de工具打开。如果为“AI2”,则为ainde支持的ain文件,进入拆解过程。如果上述两者都不是,则尝试用附带的工具arc_conv和crage拆解,拆解不成功则返回。基本上sys4x的ain都是“AI2”签名的ain文件,所以我们着重要看“AI2”的拆解过程。

                                                结合这张图,我们来讲“AI2”签名的ain文件的结构。前4个字节说过了,是字符串“AI2”,第二个dword(双字,4字节,int32)为0,第三个dword为一个int32型值,大小为0x8f0d01,windows自带计算器可以换算其值,为9309441,第四个dword大小为0x2b9e93,十进制值为2858643。那么第三第四个int32型值是什么意思呢,结合ainde程序,第三个dword表示的是未压缩数据的长度,第四个为压缩后数据的长度。压缩后数据长度为2858643 Byte,加上文件头16 Byte,也就是说这个ain文件为2,858,659字节,检查一下,发现和这个ain文件的大小严格一致。
                                                从第17字节开始,就是压缩后的数据。可以看到数据段的头两字节为“78 DA”,这对新手来说很陌生,但其实这包含着十分重要的信息。说道压缩软件,大家可能只对winrar,7zip,gzip这种比较了解,其实对数据进行压缩,很多是采用了zlib工具,而zlib工具压缩出来的数据段,头两字节大多是“78 DA”(当然也跟压缩等级有关,78 da为最高压缩等级,默认等级为78 9c,最低等级为78 01,另外还有很多例外等)。
                                                接下来,程序读取了压缩后的数据段,用zlib进行解压缩,解压出来的数据写到一个新文件,就是后缀为ain_的文件了。这个文件在用ainde打开ain文件后会自动生成,它可以用文本编辑器直接打开,用shift-jis编码,你可以看到很多有意义的日文字符,甚至可以翻到message段。当然,有这个ain_文件,你只能看到,而无法进行有效合理地更改,强行更改会让ain结构破坏而使游戏程序调用ain时出错。当然好在我们手头的ainde工具已经做得非常完善了,可以对其进行有效处理。
                                                生成ain_文件后,ainde程序对其进行读取和语法分析。大体上就是读标签,根据标签判断接下来是什么数据,然后读数据。一边读,一边把数据分类,message放一块,函数放一块,string放一块,等等。整个读完,建立树状图结构,在窗体中显示。大体上open ain file的流程就是这样的。
                                                其实到这儿ain文件也算拆解完毕了,封装也只是个逆过程,提取message和string之类的也只是将读到并归到一起的数据写出到文件而已。总体架构上我们算是了解了ainde。
                                                但是我们的工作并没结束,或者说还没开始。我们直接用这个工具是无法导入中文剧本的。导出的文本为shift-jis编码,我们翻译了这个文本后,很多字符是shift-jis编码无法识别的,所以继续以shift-jis编码保存文件,会丢失这些字符信息。那么我们要做的第一件事就是,让程序以utf8输入输出脚本。
                                                这样还不够,utf8的脚本被读取,简体字被存储到内存的string字符串。等到封装ain时,若用shift-jis识别这些字符,碰到很多简体字会无法识别而丢失字符信息,而就算是能识别的,这些字的shift-jis编码值被写入ain文件,也是背离我们目标的。我们要让程序显示出简体字,由于exe已经改为用GB2312编码读取字符,那么读到shift-jis编码的简体字,显示出来的必然是乱码。所以我们要达成的第二个要点,是让程序正确读取字符,按GB2312编码值写进ain文件。
                                                要做到这两点,对ainde程序进行精细的调整,会需要不少精力。尽量做到少修改,而达到最精准的效果,需要的是对症下药,要准确定位到要害之处。
                                                再次理清一下工作目标:从ain_里读取出字节,按shift-jis识别这些字节,存储进string全局变量(可能会有其他形式,反正是内存中的存储介质)。在导出字符串时,将string变量按utf8输出到文本。导入字符串,同样用utf8识别字节读入到string字符串存储,然后存储ain时,要按GB2312编码写入ain。
                                                这部分见解不做图示了,大致地描述下mod思路和过程。首先对readain有了初步了解,我们去看一下readtag函数。里面有两个标签值得注意,“MSG0”和“STR0”,都调用到了函数util.readstring,这个函数又调用了ReadStringNullTerminated函数,里面用到了encode类,跟编码有关。默认为shift-jis编码。当然这里我们不用改,我们要改的是写入ain文件时,以及导入导出字符串时。
                                                Ainde工具提供了多种文本导出方式,个人感觉export(new)-》to multiplefiles导出的文本格式比较好看,就决定用这个。那么我们要改的就是对这个函数进行修改,而尽量不影响到其他函数。打开ExportImportTextNewForm设计器,双击export messagesto multiple files按钮,转到这个函数的代码。从外层函数exportImport.SaveTextToMultipleFiles看,就带有了encode类参数,可以跟进去看的仔细些,不过其实只要改这个encode参数类就行了。
                                                为了不影响其他函数的功能,我们并不直接改动这个TextEncoding变量,而是在旁边新建一个变量。


                                                回复
                                                通过百度相册上传24楼2014-08-10 12:54
                                                  然后我们在exportImport.SaveTextToMultipleFiles调用参数时调用我们新建的这个变量。这样还只是导出,我们再去找到导入的地方。ImportMessagesAndStrings按钮,双击转到相应函数,转到关键函数ReplaceText,里面streamreader类用到了encode类做参数,不过是调用到了自己写的检测文本编码的函数,这边其实不用改应该也行。

                                                  注意到由于新版新增的功能,最后的勾打上,以保持和旧版的文本格式一致。


                                                  回复
                                                  通过百度相册上传25楼2014-08-10 12:56

                                                    可以看到导出的文本编码是utf8不带签名了。关于utf8格式带不带签名,差别在于文件头四个字节有没有多出个签名。Utf8带签名就是比不带签名多了4个字节在开头,生命自己是utf8文本。一般不带签名方便点,当然有时也会出问题,看情况办。
                                                    导出后我们先不做改动,再导入回去,点开各个标签,检查有没有问题。没有问题,我们再进行下一步改动。
                                                    位置还是在导入的地方,因为导入时不仅读取文件中的字符串到内存,还写入到了ain文件进行了存储。
                                                    要找写ain文件的地方不太容易,ainde其实是用了backgroundWorker,这个类,来后台异步处理并显示进度。Ainde做的工作并不像我们想的那么简单,但是我们是要mod这个工具,不是要做出这么工程浩大的东西来,我们只需要找到我们想找的位置,这样可以节约下很多时间。当然,最省事的是直接查找writetag关键词,因为对应于readtag,可以猜测作者会用writetag这个函数名。正常的思路的话,看到这个位置。

                                                    对于用到outputFileName这个参数的地方我们应当格外留心,因为这个参数存着的值是要保存的文件名,那么有关写这个文件的操作的函数,应当会调用这个参数。也就是说,调用这个参数的函数,很可能和写ain文件直接相关,而前面都只是些准备工作。我们跟进这个函数。
                                                    用了backgroundworker这个类,这个类的执行函数是DoWork,那一行中后面的函数跟进去。


                                                    回复
                                                    通过百度相册上传26楼2014-08-10 12:57

                                                      很蛋疼,不断跟进以后里面是个虚函数。我们只能查找所有引用,找overwrite的地方。

                                                      找到隶属于buildProjectInBackground类的函数,下面就直观多了,写ain文件的过程一目了然。很快我们就能找到writetag函数,在里面,我们同样找到这两个标签“MSG0”和“STR0”,他们都调用到了函数Util.WriteStrings。跟进去,函数内是调用了WriteStringNullTerminated函数。
                                                      我们看到有encode类,但是为了精准修改,我们不直接改这个类,而是在旁边新建个类,前面也说过了,这样可以尽可能不影响其他函数的正常运行。另外看到,这个函数有11个引用,我们也不应该直接改动这个函数,而是新建一个类似的函数供单独调用。


                                                      回复
                                                      通过百度相册上传27楼2014-08-10 12:58
                                                        首先binaryencoding旁边新建个我们要的对象。然后在函数下面复制粘贴修改,做个高仿马甲。
                                                        再跳出到外层函数,发现有4个引用,比我们想改的两个地方多了2,同理新建马甲。


                                                        回复
                                                        通过百度相册上传28楼2014-08-10 12:59
                                                          到writetag的地方,在对应的两处改为调用我们新建的马甲函数。这样就大功告成。我们生成ainde,然后启动,打开ain,导出字符串,导入字符串。然后拿这个ain覆盖游戏文件夹内的ain,开游戏,结果——错的不能再离谱了。为什么,因为我们做的是不对称工作,按shift-jis读,却按GB2312写。很多资源比如CG图片等,ain内会调用到它们,而调用时所用的参数当然是这些资源的名字。而名字很可能保存在STR0字符串里面,经过我们的不对称修改,使得系统找不到这些资源而产生了错误。
                                                          怎么改,很简单,把方案调整一下。把不对称读写改对称就行了,找到之前说的readtag地方,和writetag相对称,改用GB读取。这样读出来的虽然是乱码,但封装回ain可以保证元数据的前后一致。

                                                          对话文本并没有乱码,说明没有改对地方。在readmsg1和writemsg1的地方做对称修改。改好后就ok了,说明对话文本其实只跟这两个函数有关。那么str0和msg0的地方还要不要改,其实改了没坏处,不改的话看起来更直观,但是不改的话也就不能对那一块做汉化。Str0一块需要汉化吗?str0里面虽然很多,或者说大部分都是系统函数名、调用资源名,这些都是不用汉化也不能汉化的,但是也有部分诸如人物的姓名啊,存档的信息啊之类的字符信息在里面,需要汉化。所以见解建议都用GB读写,虽然会造成中介过程乱码不直观的小弊端。而msg0,其实是版本问题,最新版的ainde在对msg的读写方面新增了功能,所以采用了新的函数,即read/writemsg1,而msg0的读写函数都完全没被调用到了,所以msg0那里改不改都行。当然,旧版的貌似这里必须改。
                                                          像这样,基本就没问题了,导出到多文件。注意勾选旧格式。


                                                          回复
                                                          通过百度相册上传29楼2014-08-10 12:59
                                                            我们去翻译楼里找个试验文本,和我们导出的文本对比一下格式,拟合度没问题的话,把内容复制粘贴一下,保存文本导入进ainde。我们先在ainde中检验一下成果。当然别忘了要open的是新保存的ain,不然看不到变化。结果TM又大跌眼镜……嘛,做程序嘛,就是在不断的骂娘声中进行的。为什么全是问号,明明我们做的对称修改是不可能有问题的。问题出在哪里,肯定是中介环节,ainde又使用shift-jis编码进行了某种操作。当然我们可以这么猜测,但是单纯的string to string是不会牵扯到编码问题的,涉及编码必定是文件读写或是stream读写操作。说到文件读写,如果对import string这个函数过程有稍微深入点的了解,我们会想到导入过程中,并不是简单的内存替换,ainde进行了更复杂的过程,生产了临时文件jam等代码文件,然后再合并和编纂。这个操作用到的encode类是什么,直觉告诉我们是textencoding这个对象,我们之前新建了个它的马甲让导出导入对话文本使用,现在输入输出jam代码也应该用utf8来保证对乱码字符的识别才行。

                                                            而事实上,ainde本身提供了一个转换按钮,可以使输入输出文本(包括asm代码)使用utf8编码。但我们嫌麻烦,不动这地方,也不要去点那个按钮,不触发这块的代码。我们把textencoding直接全改成utf8完事。为什么全改,不放心的话,看一下它的所有引用,发现都是和filestream操作有关,即和输入输出文件有关,主要是两大块,输入输出文本,输入输出asm代码。那么改成utf8编码毫无坏处,再说ainde自带的那个功能也是直接一下子改动这个textencoding,说明这样改不会有问题。


                                                            回复
                                                            通过百度相册上传30楼2014-08-10 13:01