《花和蛇》游戏汉化
作者:冲出宇宙
主页: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月,累死了。休息休息。
[招生]科锐逆向工程师培训(2024年11月15日实地,远程教学同时开班, 第51期)