《花和蛇》游戏汉化

作者:冲出宇宙
主页:http://lotusroots.bokee.com

受朋友委托,帮他分析这个游戏如何汉化。后来他们取消了这个意向,所以,我就来和大家探讨一下这个游戏汉化的技术。

1 下载这个游戏。游戏特别大,选择完全安装。可以看到安装目录下有几个exe文件和几个arc文件。arc文件特别大,看来可能是数据文件。运行一下几个exe文件,可以发现,hh2d.exe和hanahebi.exe都能进入游戏。猜测前者是2d版本的,后者是3d加速版本。因为前者的图像移动有滞后的感觉。我们只是汉化游戏,所以,研究其2d版本。

2 准备工具。看了一下自己的电脑,由于上次出了问题,系统重新安装了,已经没有任何安装了的分析软件了。好在IDAG和OD是不需要安装的,嗬嗬,可以直接用。

3 用idag分析hh2d.exe文件。

3.1 得到hh2d读取的文件名

毫无疑问,hh2d运行的时候肯定会先读取数据包的。于是,对CreateFileA函数下断点。开始调试hh2d.exe文件,在运行到断点CreateFileA之后,按Ctrl+F7返回,发现EIP停在:
.text:0044D757                 call      ds:CreateFileA
继续执行,可以看到,每次读文件都是停在这里。
现在,对44D754设断点,这样设的目的是为了发现hh2d.exe是打开了具体的哪些文件。再次调试hh2d.exe,可以发现hh2d.exe打开了以下文件:mes.arc,data.arc,gcc.arc,bgm.arc,se.arc,voice.arc。在这里的时候,朋友告诉我说有一个软件能够看到许多游戏里面的数据信息,叫做mlist3.exe。我找到这个软件,然后用它依次打开了所有的arc文件,发现voice.arc里面都是声音,gcc.arc里面都是图片,只有mes.arc里面的文件看起来像文字信息,因为其扩展名是:mes,很像message。

3.2 题外话

抛开游戏本身不谈,先说说mlist3.exe这个软件。这个软件是日本人写的,功能比较强大。想起这点就斗志昂昂,日本人能搞定的,我还搞不定了么?继续继续。

3.3 寻找其他信息

先看看mes.arc文件的内容,使用UltraEdit打开它,可以看到前面一些字节为:
00000000h: F0 01 00 00 5C 4E 42 53 40 4B 46 40 48 2D 4E 46 ; ?..\NBS@KF@H-NF
00000010h: 50 03 CE CE CE CE CE CE CE CE CE CE CE CE CE CE ; P.挝挝挝挝挝挝挝
00000020h: CE CE CE CE BC 7F 46 65 0C 5E 65 79 5C 50 42 4E ; 挝挝?Fe.^ey\PBN
00000030h: 53 4F 46 2D 4E 46 50 03 CE CE CE CE CE CE CE CE ; SOF-NFP.挝挝挝挝
00000040h: CE CE CE CE CE CE CE CE CE CE CE CE 09 3B 46 65 ; 挝挝挝挝挝挝.;Fe
00000050h: D5 6A 65 79 5C 50 42 4E 53 4F 46 31 47 2D 4E 46 ; 誮ey\PBNSOF1G-NF
00000060h: 50 03 CE CE CE CE CE CE CE CE CE CE CE CE CE CE ; P.挝挝挝挝挝挝挝
00000070h: CE CE CE CE B1 33 46 65 41 FB 65 79 40 44 4E 4C ; 挝挝?FeA鹐y@DNL
00000080h: 47 46 2D 4E 46 50 03 CE CE CE CE CE CE CE CE CE ; GF-NFP.挝挝挝挝?
00000090h: CE CE CE CE CE CE CE CE CE CE CE CE 2C 57 46 65 ; 挝挝挝挝挝挝,WFe
000000a0h: 15 43 64 79 40 44 4E 4C 47 46 5C 40 4B 42 2D 4E ; .Cdy@DNLGF\@KB-N
000000b0h: 46 50 03 CE CE CE CE CE CE CE CE CE CE CE CE CE ; FP.挝挝挝挝挝挝
看起来似乎没有什么规律,除了开始的F0 01 00 00 看起来像是说这个文件有0x1F0个什么结构以外。

3.4 监视hh2d.exe对mes.arc文件的读取

我们把目光转到hh2d.exe文件上来,这次,我们对ReadFile函数下断点。运行程序,等程序停到ReadFile断点处,按Ctrl+F7返回,来到:
.text:0044D815                 call    ds:ReadFile
.text:0044D81B                 retn    8
同样,我们取消ReadFile的断点,把断点设在44D814处。
这样,我们设置的断点有2个,分别是:44D754和44D814。
重新运行hh2d.exe,根据44D754处的断点得到打开mes.arc文件后得到的句柄hfile(假设为0x350)。取消44D754断点,把44D814处断点的条件设置为ecx == 0x350,继续运行。
接下来,程序会中断在44D814处,ok,我们监视到了hh2d.exe对mes.arc文件的读取。看看当前堆栈上面的内容:
Stack[00000440]:0012F8EC                 dd 416BA44h
Stack[00000440]:0012F8F0                 dd 4
Stack[00000440]:0012F8F4                 dd 12F904h
Stack[00000440]:0012F8F8                 dd 0
比较一下44D807处的代码:
.text:0044D807                 push    0               ; lpOverlapped
.text:0044D809                 lea     eax, [esp+4+NumberOfBytesRead]
.text:0044D80D                 push    eax             ; lpNumberOfBytesRead
.text:0044D80E                 mov     eax, [esp+8+lpBuffer]
.text:0044D812                 push    edx             ; nNumberOfBytesToRead
.text:0044D813                 push    eax             ; lpBuffer
.text:0044D814                 push    ecx             ; hFile
.text:0044D815                 call    ds:ReadFile
可以看到,这次是读取了前面4个字节,就是F0 01 00 00。
继续运行,再次停在44D807这里,使用同样的方法,可以看到这次读取了0x4D80个字节。0x4D80/0x1f0 = 0x28,看来每个结构是0x28(40)个字节。
使用F8单步执行,不久就发现程序在40B060和40B1A3之间来回循环。认真的监视一下代码段:
.text:0040B191                 cmp     eax, edx
.text:0040B193                 mov     [esp+5Ch], edi
.text:0040B197                 mov     [esp+58h], ebx
.text:0040B19B                 mov     [esp+48h], bl
.text:0040B19F                 mov     [esp+10h], eax
.text:0040B1A3                 jb      loc_40B060
发现在40B191处,edx等于0x1f0,而eax不断的在加1,这里是一个很明显的对读取的数据进行处理的过程。于是开始认真地分析40B060到40B1A3之间的代码。
然而,再经过几次单步执行后,发现这里处理的数据不是读取的那些数据,说明数据已经先进行了处理。因为这里处理的数据地址在esi中,于是,从后往前的寻找对esi进行修改的地方,发现只有一处:
.text:0040B039                 push    esi
.text:0040B03A                 mov     ecx, edi
.text:0040B03C                 call    sub_40A0E0
我们知道,ecx一般都是传递类指针的,这里明显就是调用类里面的函数。
动态分析显然比静态分析容易,于是,我们重新启动程序,并且按照上述的步骤,达到40A0E0处。
于是分析得到如下结果:
.text:0040A0E0 sub_40A0E0      proc near               ; CODE XREF: .text:0040B03Cp
.text:0040A0E0
.text:0040A0E0 arg_0           = dword ptr  4
.text:0040A0E0
.text:0040A0E0                 mov     eax, [ecx+14h]  //eax = 0x1F0
.text:0040A0E3                 push    esi
.text:0040A0E4                 xor     esi, esi    //esi = 0
.text:0040A0E6                 test    eax, eax
.text:0040A0E8                 jbe     short loc_40A119
.text:0040A0EA                 mov     eax, [esp+4+arg_0]  //eax = arg0
.text:0040A0EE                 push    ebx
.text:0040A0EF                 add     eax, 24h    //eax +=0x24
.text:0040A0F2                 mov     bl, 3    //bl = 3
.text:0040A0F4
.text:0040A0F4 loc_40A0F4:                             ; CODE XREF: sub_40A0E0+36j
.text:0040A0F4                 xor     edx, edx
.text:0040A0F6
.text:0040A0F6 loc_40A0F6:                             ; CODE XREF: sub_40A0E0+1Ej
.text:0040A0F6                 xor     [eax+edx-24h], bl  //[eax+edx-24h] ^= 3
.text:0040A0FA                 inc     edx
.text:0040A0FB                 cmp     edx, 20h
.text:0040A0FE                 jl      short loc_40A0F6  //这里前32个字节都和3异或
.text:0040A100                 xor     dword ptr [eax-4], 65465465h  //倒数第2个DW异或
.text:0040A107                 xor     dword ptr [eax], 79651388h  //倒数第1个DW异或
.text:0040A10D                 mov     edx, [ecx+14h]
.text:0040A110                 inc     esi
.text:0040A111                 add     eax, 28h    //下一个结构,每个40字节长
.text:0040A114                 cmp     esi, edx
.text:0040A116                 jb      short loc_40A0F4
.text:0040A118                 pop     ebx
.text:0040A119
.text:0040A119 loc_40A119:                             ; CODE XREF: sub_40A0E0+8j
.text:0040A119                 pop     esi
.text:0040A11A                 retn    4
.text:0040A11A sub_40A0E0      endp
嗬嗬,这下我们知道了他们是如何对结构进行加密的。
再次结合40B060到40B1A3之间的代码,我们最终分析到的mes.arc文件头结构为:
class CFileNode
{
public:
char  FileName[32];  //file name
int  FileLen;    //file length
int  FilePos;    //file start position in arc file
};

3.5 得到mes.arc文件的数据结构

到目前为止,我们已经得到了mes文件的文件头结构。但是,我们还需要得到mes文件的数据结构。
继续运行,这次程序仍然中断在44D814处,看看堆栈,读取了6E2个字节。记住读取的数据存放的位置(假设为0x7250000),按F8单步执行,并且注意观察,看看是否有代码引用了这个地址的数据。运行到:
.text:0046971A                 mov     edi, eax
.text:0046971C                 test    edi, edi
.text:0046971E                 jnz     short loc_46974F
.text:00469720                 jmp     loc_4697AB
发现edi = 0x6E2。继续单步执行,到如下代码处:
.text:0046977B                 push    edi
.text:0046977C                 push    ecx             ; 保存解码后的结果
.text:0046977D                 push    ebx             ; 原始数据
.text:0046977E                 lea     ecx, [esp+1Ch]
.text:00469782                 mov     dword ptr [esp+42Ch], 0
.text:0046978D                 call    sub_45B980      ; 解码完毕
我们发现,edi = 0x6E2,ebx=0x7250000,而ecx指向一个陌生地址,运行完46978D之后,ecx的数据发生了很大的变化,于是,我们几乎可以断定,45B980处就是对数据进行解码的地方。为了安全起见,我们再次启动程序,按照上面的步骤单步执行到45B980里面,发现程序不断的在45B9D0和45BA75之间循环,这里显然是解码的关键部分。代码如下:
.text:0045B980 ; Attributes: bp-based frame
.text:0045B980
.text:0045B980 sub_45B980      proc near               ; CODE XREF: .text:0046978Dp
.text:0045B980
.text:0045B980 var_1014        = dword ptr -1014h
.text:0045B980 var_1010        = dword ptr -1010h
.text:0045B980 var_100C        = dword ptr -100Ch
.text:0045B980 var_1008        = dword ptr -1008h
.text:0045B980 var_4           = dword ptr -4
.text:0045B980 arg_0           = dword ptr  8
.text:0045B980 arg_4           = dword ptr  0Ch
.text:0045B980 arg_8           = dword ptr  10h
.text:0045B980
.text:0045B980                 push    ebp
.text:0045B981                 mov     ebp, esp
.text:0045B983                 and     esp, 0FFFFFFF8h
.text:0045B986                 mov     eax, 1014h
.text:0045B98B                 call    __alloca_probe
.text:0045B990                 mov     eax, dword_5989B0
.text:0045B995                 push    ebx
.text:0045B996                 push    esi
.text:0045B997                 push    edi
.text:0045B998                 mov     [esp+1020h+var_4], eax
.text:0045B99F                 xor     eax, eax
.text:0045B9A1                 mov     ecx, 3FBh
.text:0045B9A6                 lea     edi, [esp+1020h+var_1008]
.text:0045B9AA                 rep stosd
.text:0045B9AC                 mov     ecx, [ebp+arg_8]
.text:0045B9AF                 stosw
.text:0045B9B1                 xor     esi, esi
.text:0045B9B3                 xor     edx, edx
.text:0045B9B5                 xor     eax, eax
.text:0045B9B7                 test    ecx, ecx
.text:0045B9B9                 mov     edi, 0FEEh
.text:0045B9BE                 jz      loc_45BA7B
.text:0045B9C4                 mov     ebx, [ebp+arg_4]
.text:0045B9C7                 mov     ecx, [ebp+arg_0]
.text:0045B9CA                 lea     ebx, [ebx+0]
.text:0045B9D0
.text:0045B9D0 loc_45B9D0:                             ; CODE XREF: sub_45B980+F5j
.text:0045B9D0                 shr     eax, 1
.text:0045B9D2                 test    ah, 1
.text:0045B9D5                 mov     [esp+1020h+var_1014], eax
.text:0045B9D9                 jnz     short loc_45B9EB
.text:0045B9DB                 mov     al, [esi+ecx]
.text:0045B9DE                 movzx   eax, al
.text:0045B9E1                 inc     esi
.text:0045B9E2                 or      eax, 0FF00h
.text:0045B9E7                 mov     [esp+1020h+var_1014], eax
.text:0045B9EB
.text:0045B9EB loc_45B9EB:                             ; CODE XREF: sub_45B980+59j
.text:0045B9EB                 test    al, 1
.text:0045B9ED                 jz      short loc_45BA07
.text:0045B9EF                 mov     al, [esi+ecx]
.text:0045B9F2                 movzx   eax, al
.text:0045B9F5                 inc     esi
.text:0045B9F6                 mov     [edx+ebx], al
.text:0045B9F9                 inc     edx
.text:0045B9FA                 mov     byte ptr [esp+edi+1020h+var_1008], al
.text:0045B9FE                 inc     edi
.text:0045B9FF                 and     edi, 0FFFh
.text:0045BA05                 jmp     short loc_45BA6E
.text:0045BA07 ; 哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪哪?
.text:0045BA07
.text:0045BA07 loc_45BA07:                             ; CODE XREF: sub_45B980+6Dj
.text:0045BA07                 mov     bl, [esi+ecx]
.text:0045BA0A                 mov     al, [esi+ecx+1]
.text:0045BA0E                 inc     esi
.text:0045BA0F                 movzx   ecx, al
.text:0045BA12                 mov     eax, ecx
.text:0045BA14                 and     eax, 0F0h
.text:0045BA19                 movzx   ebx, bl
.text:0045BA1C                 shl     eax, 4
.text:0045BA1F                 and     ecx, 0Fh
.text:0045BA22                 or      eax, ebx
.text:0045BA24                 mov     ebx, [ebp+arg_4]
.text:0045BA27                 inc     esi
.text:0045BA28                 add     ecx, 2
.text:0045BA2B                 mov     [esp+1020h+var_100C], ecx
.text:0045BA2F                 mov     ecx, 0
.text:0045BA34                 mov     [esp+1020h+var_1010], ecx
.text:0045BA38                 js      short loc_45BA6B
.text:0045BA3A                 lea     ebx, [ebx+0]
.text:0045BA40
.text:0045BA40 loc_45BA40:                             ; CODE XREF: sub_45B980+E9j
.text:0045BA40                 add     ecx, eax
.text:0045BA42                 and     ecx, 0FFFh
.text:0045BA48                 movzx   ecx, byte ptr [esp+ecx+1020h+var_1008]
.text:0045BA4D                 mov     [edx+ebx], cl
.text:0045BA50                 mov     byte ptr [esp+edi+1020h+var_1008], cl
.text:0045BA54                 mov     ecx, [esp+1020h+var_1010]
.text:0045BA58                 inc     edx
.text:0045BA59                 inc     edi
.text:0045BA5A                 and     edi, 0FFFh
.text:0045BA60                 inc     ecx
.text:0045BA61                 cmp     ecx, [esp+1020h+var_100C]
.text:0045BA65                 mov     [esp+1020h+var_1010], ecx
.text:0045BA69                 jle     short loc_45BA40
.text:0045BA6B
.text:0045BA6B loc_45BA6B:                             ; CODE XREF: sub_45B980+B8j
.text:0045BA6B                 mov     ecx, [ebp+arg_0]
.text:0045BA6E
.text:0045BA6E loc_45BA6E:                             ; CODE XREF: sub_45B980+85j
.text:0045BA6E                 cmp     esi, [ebp+arg_8]
.text:0045BA71                 mov     eax, [esp+1020h+var_1014]
.text:0045BA75                 jnz     loc_45B9D0
.text:0045BA7B
.text:0045BA7B loc_45BA7B:                             ; CODE XREF: sub_45B980+3Ej
.text:0045BA7B                 mov     ecx, [esp+1020h+var_4]
.text:0045BA82                 mov     eax, edx
.text:0045BA84                 call    sub_480E53
.text:0045BA89                 pop     edi
.text:0045BA8A                 pop     esi
.text:0045BA8B                 pop     ebx
.text:0045BA8C                 mov     esp, ebp
.text:0045BA8E                 pop     ebp
.text:0045BA8F                 retn    0Ch
看到这么复杂的代码,也没有必要害怕哦。认真地分析分析,很快就发现,这个解压缩算法和LZW算法有很多相同之处。其实这个就是一个略有变形的10位LZW算法。
到目前为止,我们已经清楚了mes.arc文件的结构了哦。显然,其他arc文件也是这个结构。

3.6 *.mes文件的结构

根据上面的分析,编写一个程序,能够对mes.arc文件进行解压缩。解压缩得到好多文件,不过,文件格式都一样的,都是*.mes文件。随便打开一个,比如使用ultraEdit打开FAS000.mes(这个文件其实就是最先出来的那幕的字幕文件)。可以看到:
00000000h: F4 00 00 00 DA 04 00 00 58 05 00 00 F8 05 00 00 ; ?..?..X...?..
00000010h: F5 07 00 00 C9 08 00 00 AD 09 00 00 7F 0A 00 00 ; ?..?..?.....
00000020h: 3B 0B 00 00 7C 0D 00 00 74 0E 00 00 16 0F 00 00 ; ;...|...t.......
00000030h: 3D 10 00 00 EB 10 00 00 A1 11 00 00 7B 12 00 00 ; =...?..?..{...
00000040h: 49 13 00 00 2F 14 00 00 0C 15 00 00 1A 17 00 00 ; I.../...........
00000050h: 0E 18 00 00 9C 18 00 00 D0 19 00 00 AA 1A 00 00 ; ....?..?..?..
00000060h: 6C 1B 00 00 3D 1C 00 00 03 1E 00 00 D8 1E 00 00 ; l...=.......?..
00000070h: 84 1F 00 00 1A 20 00 00 D8 20 00 00 6E 21 00 00 ; ?... ..?..n!..
00000080h: 4A 22 00 00 7E 24 00 00 87 25 00 00 11 26 00 00 ; J"..~$..?...&..
00000090h: A3 26 00 00 4D 27 00 00 4B 28 00 00 34 29 00 00 ; ?..M'..K(..4)..
这个对于我们来说简直太简单了,前面的F4 00 00 00 表示有0xf4个结构,后面的刚好有0xf4个整数,可能是表示每个结构的位置吧。具体怎么计算还不知道。
再看看下面的某处:
00000930h: 00 0D 13 FF 02 01 FF 00 1D 02 1B FF 00 01 81 40 ; ...........丂
00000940h: 00 07 0C FF 00 14 FF 00 07 0D FF 00 14 FF 00 0D ; ............
00000950h: 00 FF 00 1F 2B 00 00 00 01 8D F7 82 CC 89 D4 82 ; ...+....嶗偺壴?
00000960h: D1 82 E7 82 AA 81 41 95 97 82 C9 95 91 82 A2 97 ; 褌鐐獊A晽偵晳偄?
00000970h: 78 82 C1 82 C4 82 A2 82 E9 82 E6 82 A4 82 BE 81 ; x偭偰偄傞傛偆偩?
00000980h: 63 81 63 00 0D 13 FF 02 02 FF 00 0B F3 C6 0B 00 ; c乧.......笃..
虽然是日文,不过,我们还是能够看出来,这里是一句日文的话。如果要进行汉化,那么,就该汉化这里哦。情况其实比这个要复杂很多,不过,那些东西和软件调试没有什么关系。只有实际汉化的时候才需要处理。

3.7 如何显示汉字

按照上面所说的方式修改文字之后,发现文字不能显示为中文。但是,英文可以显示。那么,到底是什么原因呢?
在一次得仔细看了看hh2d.exe文件的import函数表。发现了TextOutA函数。这个函数是用来输出文字到屏幕的。如果给这个函数设断点,会发现,每次出现文字的时候,这个函数都被调用。根据这个原理,取消所有断点,重新运行程序,然后点击游戏界面中的开始,听到一段音乐之后,会看到文字信息的出现,这个时候,对TextOutA设断,继续游戏。很快就中断在TextOutA函数处,按Ctrl+F7回到程序领空,能够看到回到414442处。继续Ctrl+F7,到了414F5A处,注意,这里是一大段,看得出来,这里和输出字符应该有关系。而且,这一段函数里面多次调用了TextOutA函数。对这段函数的开头414F00设断,取消TextOutA的断点,继续程序,很快就停在414F00处。使用F8单步执行,注意看堆栈里面的第3个输入参数,发现它为一个比较大的数(比如:8DF7)。同时注意到我们上面看到的*.mes文件的内容,能清楚地看到日文字符一般都是8***,或者9***什么的。于是想到8DF7可能是一个日文字符。继续单步执行,运行到:
.text:00414F43                 mov     ecx, esi
.text:00414F45                 call    sub_4143E0
.text:00414F4A                 lea     eax, [esp+14h+var_4]
.text:00414F4E                 push    eax
.text:00414F4F                 push    0
.text:00414F51                 push    0
.text:00414F53                 mov     ecx, esi
.text:00414F55                 call    sub_414410      ; out put 8D F7
看得到,这里就是调用了414410(TextOutA)函数进行输出的。而输出的字符串保存在eax中,就是var_4这个变量中,看看前面:
.text:00414F06                 mov     cx, [esp+10h+arg_8]
.text:00414F0B                 mov     ax, cx
.text:00414F0E                 shr     ax, 8
.text:00414F12                 cmp     al, 81h
。。。。。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。。。。。
.text:00414F3B                 mov     byte ptr [esp+14h+var_4], al
.text:00414F3F                 mov     byte ptr [esp+14h+var_4+1], cl
可以知道var_4就是第3个参数的值。
输出文字的位置找到了,那么,我们再重新看看前面的代码:
text:00414F06                 mov     cx, [esp+10h+arg_8]
.text:00414F0B                 mov     ax, cx
.text:00414F0E                 shr     ax, 8
.text:00414F12                 cmp     al, 81h
.text:00414F14                 push    edi
.text:00414F15                 mov     byte ptr [esp+14h+var_4+1], 0
.text:00414F1A                 mov     byte ptr [esp+14h+var_4+2], 0
.text:00414F1F                 jb      short loc_414F25
.text:00414F21                 cmp     al, 9Fh
.text:00414F23                 jbe     short loc_414F3B
.text:00414F25
.text:00414F25 loc_414F25:                             ; CODE XREF: sub_414F00+1Fj
.text:00414F25                 cmp     al, 0E0h
.text:00414F27                 jb      short loc_414F2D
.text:00414F29                 cmp     al, 0EFh
.text:00414F2B                 jbe     short loc_414F3B
.text:00414F2D
.text:00414F2D loc_414F2D:                             ; CODE XREF: sub_414F00+27j
.text:00414F2D                 cmp     al, 0FAh
.text:00414F2F                 jb      short loc_414F35
.text:00414F31                 cmp     al, 0FCh
.text:00414F33                 jbe     short loc_414F3B
.text:00414F35
.text:00414F35 loc_414F35:                             ; CODE XREF: sub_414F00+2Fj
.text:00414F35                 mov     byte ptr [esp+14h+var_4], cl
.text:00414F39                 jmp     short loc_414F43

这一段的意思很明显吧?它只比较第一个字节,如果第一个字节在0x81-0x9f,0xe0-0xef,0xfa-0xfc之间的话,就跳到414F3B执行,否则就跳到414F35处执行。跟踪一下,就会发现414F3B那里只输出一个字符,而414F35那里输出2个字符。也就是说,如果让汉字能够显示,就必须让它跳转到414F3B处。考虑到任何大于255的字符都不可能显示2个字符,于是把上述代码修改为:
.text:00414F12                 cmp     al, 81h
.text:00414F14                 push    edi
.text:00414F15                 mov     byte ptr [esp+14h+var_4+1], 0
.text:00414F1A                 mov     byte ptr [esp+14h+var_4+2], 0
.text:00414F1F                 jb      short loc_414F25
.text:00414F21                 cmp     al, FFh
.text:00414F23                 jbe     short loc_414F3B
.text:00414F25
.text:00414F25 loc_414F25:                             ; CODE XREF: sub_414F00+1Fj
.text:00414F25                 cmp     al, 0E0h
.text:00414F27                 jb      short loc_414F2D
.text:00414F29                 cmp     al, 0FFh
.text:00414F2B                 jbe     short loc_414F3B
.text:00414F2D
.text:00414F2D loc_414F2D:                             ; CODE XREF: sub_414F00+27j
.text:00414F2D                 cmp     al, 0FAh
.text:00414F2F                 jb      short loc_414F35
.text:00414F31                 cmp     al, 0FFh
.text:00414F33                 jbe     short loc_414F3B

这样需要修改的地方还有2处。具体的地方我就不说了。根据Ctrl+F7大法,很容易发现它们的所在。

3.8 编码的处理

日文软件显然使用日文编码。游戏要想在安装了日文字库的机器上面正确显示中文的话,还需要修改其显示时候的编码。回忆Api函数,如果还不行的话,回去看看《win2000 api大全》里面关于文字的api函数。你会发现有一个函数:CreateFontIndirectA具有一些和字符编码有关的参数。
难道我说得还不明显么?赫赫。

完成于2006年1月,累死了。休息休息。