·汉化新世纪 ·汉化新世纪论坛 ·百家争鸣 ·论坛集萃 ·汉化问答 ·软件介绍
文章首页 >> 汉化教学 >> 特殊汉化 >> EVA ANIMATER之动态汉化    Creative Commons License,创作共用协议(中文版)  署名 非商业性使用 禁止演绎

EVA ANIMATER之动态汉化

作者: laomms 来源:汉化新世纪论坛 时间:2005-12-23 点击:10055

【软件名称】 EVA Animater v2.3
【汉化作者】 高为
【使用工具】 Ollydbg
【汉化平台】 Win XP SP2
【下载地址】 http://my.vector.co.jp/servlet/System.File...ime/eva23dl.exe
原汉化版:http://www.hanzify.org/index.php?Go=Show::List&ID=501
【软件简介】 EVA ANIMATER是日本夏普公司的一个简单易用的二维动画制作软件。用它不仅可以做一些小型的二维卡通动画,还可以做教学课件以及一些产品使用说明演示等,功能跟FLASH相似,但比FLASH容易操作。而且做成的EVA格式动画文件容量很小,便于发布和传送


EVA ANIMATER 汉化掉已经很久了,但是程序的文本输入框不支持中文字体输入和显示,当时以为简单的把字体名称改一下,语系改一下就可以了,但始终没有测试成功,经过对WIN32API和汇编知识的一点学习,发现远远没有这么简单。

第一方案,为原程序的字体列表中加入中文字体。
原程序的字体列表如下图,只有英文字体,统计一下,应用如下字符集:ANSI_CHARSET,YMBOL_CHARSET,DEFAULT_CHARSET,看截图:
附带图片

汉化后的字体列表如下图,加入了GB2312_CHARSET字符集
附带图片
在做这一方案之前,需要了解一些基本知识:

=====================================================================
函数名:EnumFontFamilies
函数原形:
int EnumFontFamilies(
HDC hdc, // handle to DC
LPCTSTR lpszFamily, // font family
FONTENUMPROC lpEnumFontFamProc, // callback function
LPARAM lParam // additional data
);

【说明】
列举指定设备可用的字体
【返回值】
Long,由回调函数返回的前一个值
【参数表】
hdc ------------ Long,设备场景的句柄
lpszFamily ----- String,欲枚举的字体家族。如指定 vbNullString,可枚举出每种可用字体家族中的一种字体
lpEnumFontFamProc - Long,欲调用的函数地址。这个地址是用 AddressOf
运算符为来自一个标准模块的函数进行操作,或者利用某个回调控件得到
lParam --------- Long,指定希望传递给回调函数的一个用户自定义值
【其它】
这个函数取代了API函数 EnumFonts,因为它能对TureType字体样式说明进行控制
只有实际存在的字体才会列举出来,那些可由GDI合成的字体不会列出

【声明】
Public Declare Function EnumFontFamiliesEx Lib "gdi32" Alias "EnumFontFamiliesExA" (ByVal hdc As Long, lpLogFont As LOGFONT, ByVal lpEnumFontProc As Long, ByVal lParam As Long, ByVal dw As Long) As Long
【说明】
根据一个LOGFONT结构提供的信息,列举指定设备可用的字体
【返回值】
Long,由回调函数返回的前一个值
【参数表】
hdc ------------ Long,设备场景的句柄
lpLogFont ------ LOGFONT,这个结构指定了欲枚举的字体。此时用到的字段包括:lfCharSet,
lfFaceName 和 lfPitchAndFamily。其他所有字段都会忽略
lpEnumFontFamProc
Long,欲调用的函数地址。这个地址是用 AddressOf
运算符为来自一个标准模块的函数进行操作,或者利用某个回调控件得到
lParam --------- Long,指定希望传递给回调函数的一个用户自定义值

dw ------------- Long,保留,设为零
【其它】
参见EnumFontFamilies函数的注解

【声明】
Public Declare Function EnumFonts Lib "gdi32" Alias "EnumFontsA" (ByVal hDC As Long, ByVal lpsz As String, ByVal lpFontEnumProc As Long, ByVal lParam As Long) As Long
【说明】
列举指定设备可用的字体
【其它】
该函数使用的参数与EnumFontFamilies函数是一样的,工作原理也大致相同。只是EnumFontFamilies会利用ENUMLOGFONT 和 NEWTEXTMETRIC结构向回调函数传递附加的信息,而不是使用LOGFONT
和 TEXTMETRIC结构。请参考EnumFontFamilies函数,那里有更详细的解释

int EnumFonts(
HDC hdc, // 你程序的设备DC
LPCTSTR lpFaceName, // 默认选择的字体
FONTENUMPROC lpFontFunc, // 枚举字体的回调函数
LPARAM lParam // 程序支持, 比如CListCtrl指针, 传递给回调函数
);

//回调函数
int CALLBACK EnumFontsProc(
CONST LOGFONT *lplf, // 枚举的逻辑字体
CONST TEXTMETRIC *lptm, // 物理字体信息
DWORD dwType, // 字体类型
LPARAM lpData // 传递给回调函数的数据指针
);

上述lpData一般就是类似编辑器字体列表的下拉框对象指针, 比如:
添加字体名称到列表,所以CallbackFunc 很重要。
======================================================================
第二个知识点:
字集编辑字串表.
ANSI_CHARSET=00(H)
DEFAULT_CHARSET=01(H)
SYMBOL_CHARSET=02(H)
MAC_CHARSET=4D(H)
SHIFTJIS_CHARSET=80(H)
GB2312_CHARSET=86(H)
CHINESEBIG5_CHARSET=88(H)
GREEK_CHARSET=A1(H)
TURKISH_CHARSET=A2(H)
HEBREW_CHARSET=B1(H)
ARABIC_CHARSET=B2(H)
BALTIC_CHARSET=BA(H)
RUSSIAN_CHARSET=CC(H)
THAI_CHARSET=DE(H)
EASTEUROPE_CHARSET=EE(H)
OEM_CHARSET=FF(H)
======================================================================

现在介绍一下过程(以2.23版本的代码为范文,既原汉化版,日文版为2.3版):
用OLLYDBG载入程序,在代码区右击,搜索-当前模块中名称,查看软件的所有输入函数,发现跟字体有关的有CreateFontA、EnumFontFamiliesA、GetTextFaceA、GetTextMetricsA。本方案涉及字体列表,先在输入函数表中找到EnumFontFamiliesA函数,右击,查看导入参考,发现程序有四处调用,我们先拿第三处进行分析(因为第三处就是对应那个字体框的,用NOP大法很容易判断),双击来到代码区,代码如下:
0050BEBA |. 8B55 E4 mov edx,dword ptr ss:[ebp-1C]
0050BEBD |. 52 push edx ; /lParam
0050BEBE |. 68 4A965100 push 0051964A ; |CallbackFunc,关键就是这里,这里压入51964A地址的信息,我们跟进去。
0050BEC3 |. 6A 00 push 0 ; |FamilyName = NULL
0050BEC5 |. 8B45 C8 mov eax,dword ptr ss:[ebp-38] ; |
0050BEC8 |. 50 push eax ; |hDC
0050BEC9 |. FF15 C8115500 call dword ptr ds:[<&GDI32.Enum>; \EnumFontFamiliesA
0050BECF |. 8B4D C8 mov ecx,dword ptr ss:[ebp-38]

可以看出0050BEBE处就是调用CallbackFunc的地方,那就跟进去分析,这里是压入0051964A,在代码区按CTRL+G,输入482B60,按确定后停在482B60处,F2下断,F9运行程序,点右边的字体选择选项A,马上被断下,F8逐步分析:
00506C1A /. 55 push ebp
00506C1B |. 8BEC mov ebp,esp
00506C1D |. 8B45 08 mov eax,dword ptr ss:[ebp+8]
00506C20 |. 0FBE48 1C movsx ecx,byte ptr ds:[eax+1C] ; 取字体名称的HEX码
00506C24 |. 83F9 40 cmp ecx,40 ; 与0X40H(@)对比,比如@宋体
00506C27 |. 75 0A jnz short xeva22dl.00506C33 ; 不等就跳到506C33处
00506C29 |. B8 01000000 mov eax,1 ; EAX赋值1
00506C2E |. E9 CE000000 jmp xeva22dl.00506D01 ; 跳到0056D01处
00506C33 |> 8B55 10 mov edx,dword ptr ss:[ebp+10] ; EBP+10地址的值放入EDX
00506C36 |. 83E2 04 and edx,4 ; 与4做AND运算
00506C39 |. 85D2 test edx,edx ; 是否为0
00506C3B |. 75 0A jnz short xeva22dl.00506C47 ; 不是就跳到506C47处
00506C3D |. B8 01000000 mov eax,1
00506C42 |. E9 BA000000 jmp xeva22dl.00506D01
00506C47 |> 8B45 08 mov eax,dword ptr ss:[ebp+8]
00506C4A |. 33C9 xor ecx,ecx ; ECX清零
00506C4C |. 8A48 17 mov cl,byte ptr ds:[eax+17] ; 地址EAX+17的值放入CL,EAX+17处放的是字集编码(这个要记住哦)
00506C4F 81F9 80000000 cmp ecx,80 ; 与80对比,80应该是日文语系SHIFTJIS_CHARSET
00506C55 |. 75 2E jnz short xeva22dl.00506C85 ; 不等就跳到506C85处
00506C57 |. 8B55 08 mov edx,dword ptr ss:[ebp+8]
00506C5A |. 83C2 1C add edx,1C
00506C5D |. 52 push edx
00506C5E |. A1 40225E00 mov eax,dword ptr ds:[5E2240]
00506C63 |. C1E0 08 shl eax,8
00506C66 |. 05 40215B00 add eax,xeva22dl.005B2140
00506C6B |. 50 push eax
00506C6C |. E8 EF940200 call xeva22dl.00530160
00506C71 |. 83C4 08 add esp,8
00506C74 |. 8B0D 40225E00 mov ecx,dword ptr ds:[5E2240]
00506C7A |. 83C1 01 add ecx,1
00506C7D |. 890D 40225E00 mov dword ptr ds:[5E2240],ecx
00506C83 |. EB 7C jmp short xeva22dl.00506D01
00506C85 |> 8B55 08 mov edx,dword ptr ss:[ebp+8] ; 跳到这里
00506C88 |. 33C0 xor eax,eax ; EAX清空
00506C8A |. 8A42 17 mov al,byte ptr ds:[edx+17] ; 字集编码放入AL。(应该不会忘记EAX+17放的是什么吧)
00506C8D |. 85C0 test eax,eax ; 判断是否为0,00应该是ANSI_CHARSET
00506C8F |. 75 2E jnz short xeva22dl.00506CBF ; 不是就跳到506CBF处
00506C91 |. 8B4D 08 mov ecx,dword ptr ss:[ebp+8] ; 字体名压入ECX
00506C94 |. 83C1 1C add ecx,1C ; ECX=ECX+1C
00506C97 |. 51 push ecx
00506C98 |. 8B15 44225E00 mov edx,dword ptr ds:[5E2244] ; 5E2244地址的值放入EDX
00506C9E |. C1E2 08 shl edx,8 ; 右移8位
00506CA1 |. 81C2 40215C00 add edx,xeva22dl.005C2140 ; ASCII "Arial"
00506CA7 |. 52 push edx
00506CA8 |. E8 B3940200 call xeva22dl.00530160
00506CAD |. 83C4 08 add esp,8
00506CB0 |. A1 44225E00 mov eax,dword ptr ds:[5E2244]
00506CB5 |. 83C0 01 add eax,1
00506CB8 |. A3 44225E00 mov dword ptr ds:[5E2244],eax
00506CBD |. EB 42 jmp short xeva22dl.00506D01
00506CBF |> 8B4D 08 mov ecx,dword ptr ss:[ebp+8] ; ESP+8地址的值移入ECX
00506CC2 |. 33D2 xor edx,edx ; EDX清零
00506CC4 |. 8A51 17 mov dl,byte ptr ds:[ecx+17] ; 字集编码移入EDX,ECX+17始终装着字符集。
00506CC7 |. 83FA 02 cmp edx,2 ; 与2对比,2是SYMBOL_CHARSET
00506CCA |. 75 30 jnz short xeva22dl.00506CFC
00506CCC |. 8B45 08 mov eax,dword ptr ss:[ebp+8]
00506CCF |. 83C0 1C add eax,1C
00506CD2 |. 50 push eax
00506CD3 |. 8B0D 48225E00 mov ecx,dword ptr ds:[5E2248]
00506CD9 |. C1E1 08 shl ecx,8
00506CDC |. 81C1 40215D00 add ecx,xeva22dl.005D2140 ; ASCII "Marlett"
00506CE2 |. 51 push ecx
00506CE3 |. E8 78940200 call xeva22dl.00530160
00506CE8 |. 83C4 08 add esp,8
00506CEB |. 8B15 48225E00 mov edx,dword ptr ds:[5E2248]
00506CF1 |. 83C2 01 add edx,1
00506CF4 |. 8915 48225E00 mov dword ptr ds:[5E2248],edx
00506CFA |. EB 05 jmp short xeva22dl.00506D01
00506CFC |> B8 01000000 mov eax,1 ; EAX放赋值1,1是DEFAULT_CHARSET
00506D01 |> 5D pop ebp ; 对应上面的PUSH EBP,保护现场指针
00506D02 \. C2 1000 retn 10 ; 返回GDI32.DLL
00506D05 /$ 55 push ebp

跟完了这段循环取字体的代码段,我们有了个基本的了解,字体列表中的字体先跟日文字集相比,不是的就在列表中显示,再跟ANSI_CHARSET字集比,属于该字集的字符就显示,再跟SYMBOL_CHARSET字集比,属于该字集的字符就显示,假如都不是,就显示DEFAULT_CHARSET字集,由此可见,至始至终没有提到过GB2312_CHARSET,如果我们要加入中文字体,要么找个空的地址段写入跟DEFAULT_CHARSET字集对比的语句,然后仍旧返回到这里;要么把其中的一处字集比较改成跟GB2312字符集的对比,我们采用第二个方案(为什么?因为简单啊,虽然有点损,嘿嘿)。我们拿506C8D地址这里动刀,原来是跟0对比,就是跟ANSI_CHARSET比较,我们把它改为跟86(GB2312_CHARSET)对比,假如单单把test eax,eax改成CMP EAX,86,字节会不够,干脆把上面的一句也改了腾出点空间(更损)改成如下代码:
005196BA B0 86 mov al,86
005196BC 90 nop
005196BD 8BC0 mov eax,eax

改好后,由击,复制到可执行文件-全部保存,打开保存后的文件,字体列表里已经有中文字体了。
顺便也说一下不损的办法:
CTRL+B打开二进制搜索,输入大量的0,按确定,来到550C0B的空地址段,写下如下代码:

00550C5B 33DB xor ebx,ebx //EBX清空,准备赋值
00550C5D 8A5A 17 mov bl,byte ptr ds:[edx+17] //字集编码放入BL
00550C60 80FB 86 cmp bl,86 //是否为中文字集GB2312_CHARSET
00550C63 ^ 0F85 9360FBFF jnz 00506CFC //不是就跳走掉
00550C69 ^ E9 5E60FBFF jmp 00506CCC //是的话就跳回506C91继续执行
这样写完后,还得让程序跳到这个地方执行代码命令,改506C8F处的代码:
00506CCA |. 75 30 jnz 0050C5B //不是SYMBOL_CHARSET就跳到下面跟GB2312_CHARSET对比

事实上还有一种更简单的改法,只要改一个字节:
00506C4F 81F9 80000000 cmp ecx,80
改为
00506C4F 81F9 86000000 cmp ecx,86
改了后字体列表里就有中文字体了.
其他的EnumFontFamilies修改方法类似,这里就不讲了。
 
二、分析文本输入框和字体选项框
经过上一节内容,我们已经为软件的字体列表中加入了中文字体,可是当我们选择中文字体,输入字体时,软件弹出个对话框,大致意思是说字体不符什么的,而且输入的还是乱码,如图:
附带图片
是不是软件对这个文本框做了限制,只能输入日文和英文呢,不管,我们先把那恼人的对话框去掉,我们跟到代码处看看,取输入的文本可以下 BP GetDlgItemTextA或拦截那个错误的对话框BP MessageBoxA,断下后,我们来到这里:
004FC12B |. FF15 94B45300 call dword ptr ds:[<&USER32.GetDlgItemT>; \呼叫GetDlgItemTextA函数,取输入的内容
004FC131 |. 8D8D FCFEFFFF lea ecx,dword ptr ss:[ebp-104] ; 返回的内容放在EBP-104里,移入ECX
004FC137 |. 51 push ecx
004FC138 |. E8 A33F0300 call xeva22dl.005300E0
004FC13D |. 83C4 04 add esp,4
004FC140 |. 8D95 FCFEFFFF lea edx,dword ptr ss:[ebp-104]
004FC146 |. 52 push edx
004FC147 |. E8 943F0300 call xeva22dl.005300E0 ; 这个CALL是取字符串的长度,返回EAX
004FC14C |. 83C4 04 add esp,4
004FC14F |. 3D F0000000 cmp eax,0F0 ; 与240对比
004FC154 |. 76 1B jbe short xeva22dl.004FC171 ; 小于204个字符就跳过下面的对话框
004FC156 |. 6A 30 push 30 ; /Style = MB_OK|MB_ICONEXCLAMATION|MB_APPLMODAL
004FC158 |. 68 30ED5800 push xeva22dl.0058ED30 ; |Title = "EVA ANINMATOR"
004FC15D |. 68 40ED5800 push xeva22dl.0058ED40 ; |多余240个字符的MESSAGEBOX
004FC162 |. 8B45 08 mov eax,dword ptr ss:[ebp+8] ; |
004FC165 |. 50 push eax ; |hOwner
004FC166 |. FF15 A8B45300 call dword ptr ds:[<&USER32.MessageBoxA>; \MessageBoxA
004FC16C |. E9 C6000000 jmp xeva22dl.004FC237
004FC171 |> C785 D4FEFFFF 00>mov dword ptr ss:[ebp-12C],0 ; 跳到这里,赋值0给EBP-12C
004FC17B |. EB 0F jmp short xeva22dl.004FC18C ; 跳到4FC18C
004FC17D |> 8B8D D4FEFFFF /mov ecx,dword ptr ss:[ebp-12C]
004FC183 |. 83C1 01 |add ecx,1
004FC186 |. 898D D4FEFFFF |mov dword ptr ss:[ebp-12C],ecx
004FC18C |> 8B95 D4FEFFFF mov edx,dword ptr ss:[ebp-12C] ; O放入EDX
004FC192 |. 0FBE8415 FCFEFFF>|movsx eax,byte ptr ss:[ebp+edx-104] ; 输入字符串的HEX码放入EAX
004FC19A |. 85C0 |test eax,eax ; 判断是否为0,即有没有输入字符
004FC19C |. 74 25 |je short xeva22dl.004FC1C3 ; 没有输入字符就跳到4FC1C3处
004FC19E |. 8B8D D4FEFFFF |mov ecx,dword ptr ss:[ebp-12C] ; O放入ECX
004FC1A4 |. 33D2 |xor edx,edx ; EDX清零
004FC1A6 |. 8A940D FCFEFFFF |mov dl,byte ptr ss:[ebp+ecx-104] ; 字符串的HEX码放入EAX
004FC1AD |. 81FA 81000000 |cmp edx,81 ; 与81对比,注意a-z、A-Z,以及特殊字符的ASCII都小于81
004FC1B3 |. 7C 0C |jl short xeva22dl.004FC1C1 ; 小于就跳到4FC1C1
004FC1B5 |. C785 D0FEFFFF 01>|mov dword ptr ss:[ebp-130],1 ; 1赋值给EBP-130
004FC1BF |. EB 02 |jmp short xeva22dl.004FC1C3 ; 跳到4FC1C3
004FC1C1 |>^ EB BA \jmp short xeva22dl.004FC17D
004FC1C3 |> 33C0 xor eax,eax ; EAX清空
004FC1C5 |. A0 18455E00 mov al,byte ptr ds:[5E4518] ; 输入字符串的语系放入EAX
004FC1CA |. 3D 80000000 cmp eax,80 ; 与80比较,SHIFTJIS_CHARSET
004FC1CF |. 74 26 je short xeva22dl.004FC1F7 ; 是SHIFTJIS_CHARSET字体就跳到004FC1F7
004FC1D1 |. 83BD D0FEFFFF 00 cmp dword ptr ss:[ebp-130],0 ; ebp-130的值与0比较
004FC1D8 |. 74 1D je short xeva22dl.004FC1F7 ; 相等就跳到004FC1F7,否则出现下面的对话框
004FC1DA |. 6A 34 push 34 ; /Style = MB_YESNO|MB_ICONEXCLAMATION|MB_APPLMODAL
004FC1DC |. 68 98ED5800 push xeva22dl.0058ED98 ; |Title = "EVA ANIMATOR"
004FC1E1 |. 68 A8ED5800 push xeva22dl.0058EDA8 ; |字体不符的MESAAGEBOX
004FC1E6 |. 8B4D 08 mov ecx,dword ptr ss:[ebp+8] ; |
004FC1E9 |. 51 push ecx ; |hOwner
004FC1EA |. FF15 A8B45300 call dword ptr ds:[<&USER32.MessageBoxA>; \MessageBoxA
004FC1F0 |. 83F8 07 cmp eax,7
004FC1F3 |. 75 02 jnz short xeva22dl.004FC1F7
004FC1F5 |. EB 40 jmp short xeva22dl.004FC237
004FC1F7 |> 8D95 FCFEFFFF lea edx,dword ptr ss:[ebp-104] //字符串放入EDX
004FC1FD |. 52 push edx


这段代码的意思是输入的字符不能超过240个字节,假如不是日文和英文就跳出那个对话框,在这里,我们可以把204个字节的限制去掉,再把对话框屏蔽掉。顺便把那个跟日文语系对比的语句改成跟中文对比:
004FC154 |. 76 1B jbe 004FC171
改为:004FC154 /EB 1B jmp 004FC171

004FC1CA |. 3D 80000000 cmp eax,80
改为:004FC1CA |. 3D 86000000 cmp eax,86

再把下面这句NOP掉:
004FC1EA |. FF15 A8B45300 call dword ptr ds:[<&USER32.MessageBoxA>]
004FC1EA 90 nop
004FC1EB 90 nop
004FC1EC 90 nop
004FC1ED 90 nop
004FC1EE 90 nop
004FC1EF 90 nop

改好保存后,运行程序,输入字体还是乱码,而且更改字体类型时,那个对话框还是会出现,看来我们在选第一种日文字体时候,选择的同时程序对语系也做了规定,判断单选按钮或检查框是否被选择调用的是IsDlgButtonChecked,可是程序会有很多地方调用这个函数,我们可以用查找检查框的ID的方法找到代码处,不过这里既然会弹出个错误对话框,那应该就在那个对话框的附近,我们就找那个对话框:
004FA514 |. /74 0D je short 文章.004FA523
004FA516 |. |6A 02 push 2 ; /Arg4 = 00000002
004FA518 |. |6A 00 push 0 ; |Arg3 = 00000000
004FA51A |. |6A 00 push 0 ; |Arg2 = 00000000
004FA51C |. |6A 00 push 0 ; |Arg1 = 00000000
004FA51E |. |E8 E2C70000 call 文章.00506D05 ; \文章.00506D05
004FA523 |> \6A 03 push 3 ; /Arg2 = 00000003
004FA525 |. 8B0D 20215B00 mov ecx,dword ptr ds:[5B2120] ; |
004FA52B |. 51 push ecx ; |Arg1 => 00000000
004FA52C |. E8 DC9C0200 call 文章.0052420D ; \文章.0052420D
004FA531 |. 83F8 01 cmp eax,1
004FA534 |. 75 30 jnz short 文章.004FA566
004FA536 |. 33D2 xor edx,edx
004FA538 |. 8A15 EC3A5E00 mov dl,byte ptr ds:[5E3AEC]
004FA53E 81FA 80000000 cmp edx,80 ; 与日文语系对比
004FA544 |. 74 20 je short 文章.004FA566 ; 不是就跳出提示的对话框
004FA546 |. 6A 34 push 34 ; /Style = MB_YESNO|MB_ICONEXCLAMATION|MB_APPLMODAL
004FA548 |. 68 88EA5800 push 文章.0058EA88 ; |Title = "EVA ANIMATOR"
004FA54D |. 68 98EA5800 push 文章.0058EA98 ; |Text = "尰嵼偺僼僅儞僩偱偼丄敿妏僇僫傗慡妏暥帤偑暥帤壔偗偟傑偡丅
偙偺傑傑懕峴偟傑偡偐丠"
004FA552 |. 8B45 08 mov eax,dword ptr ss:[ebp+8] ; |
004FA555 |. 50 push eax ; |hOwner
004FA556 FF15 A8B45300 call dword ptr ds:[<&USER32.MessageBox>; USER32.MessageBoxA
004FA55C |. 83F8 07 cmp eax,7
004FA55F |. 75 05 jnz short 文章.004FA566
004FA561 |. E9 9D020000 jmp 文章.004FA803

我们只要把004FA53E处的cmp edx,80该为cmp edx,86就可以了,还有几处IsDlgButtonChecked返回后跟日文语系对比的地方,有一处是跟英文对比,也就是下面的那个字体列表选择后返回的字体的语系必须为英文,我们加入了中文字体所以会出现乱码。我们都把它改了,顺便把提示字体错误的对话框也屏蔽了。
 
三、更改软件逻辑字体
===============================================================================
小知识:createFont 用指定的属性创建一种逻辑字体
HFONT CreateFont(
int nHeight, //字体的高度
int nWidth, //字体的宽度
int nEscapement, //字体显示的角度
int nOrientation, //字体的角度
int nWeight, //字体的磅数
BYTE bItalic, //斜体字体
BYTE bUnderline, //带下划线的字体
BYTE cStrikeOut, //带删除线的字体
BYTE nCharSet, //所需的字符集
BYTE nOutPrecision, //输出的精度
BYTE nClipPrecision, //裁减的精度
BYTE nQuality, //逻辑字体与输出设备的实际
//字体之间的精度
BYTE nPitchAndFamily, //字体间距和字体集
LPCTSTR lpszFacename //字体名称
);

经过上两次的修改后,对话框是解决了,但是输入的字体还是乱码,因为我们把最基础的忘记了,createFont也涉及语系。看了第一节内容,我们应该很容易找到程序调用createFont的地方,总共有四处,我们先分析第三处(因为那里修改比较直观,以日文版为范例):
00529FCA |> \68 D0C>push xeva23dl.005AC0D0 ; /FaceName = "俵俽 僑僔僢僋"
00529FCF |. 6A 00 push 0 ; |PitchAndFamily = DEFAULT_PITCH|FF_DONTCARE
00529FD1 |. 6A 00 push 0 ; |Quality = DEFAULT_QUALITY
00529FD3 |. 6A 00 push 0 ; |ClipPrecision = CLIP_DEFAULT_PRECIS
00529FD5 |. 6A 00 push 0 ; |OutputPrecision = OUT_DEFAULT_PRECIS
00529FD7 |. 68 800>push 80 ; |CharSet = 128.
00529FDC |. 6A 00 push 0 ; |StrikeOut = FALSE
00529FDE |. 6A 00 push 0 ; |Underline = FALSE
00529FE0 |. 6A 00 push 0 ; |Italic = FALSE
00529FE2 |. 6A 00 push 0 ; |Weight = FW_DONTCARE
00529FE4 |. 6A 00 push 0 ; |Orientation = 0
00529FE6 |. 6A 00 push 0 ; |Escapement = 0
00529FE8 |. 6A 00 push 0 ; |Width = 0
00529FEA |. 6A 0D push 0D ; |Height = D (13.)
00529FEC |. FF15 B>call dword ptr ds:[<&GDI32.CreateFontA>>; \CreateFontA

CreateFont的14个参数一目了然,我们只要修改字体名称和字集(语系)就可以了,也就是说只要修改第一项和第六项,第六项修改简单,只要直接把80改为86就可以了:
00529FD7 68 86000000 push 86
第一项原来是压入5AC0D0地址的字符串:俵俽 僑僔僢僋(MSゴシック),我们要压入宋体的话的最好的办法就是把5AC0D0地址处的字符串替换成宋体,用UltraEdit最方便不过了,UltraEdit打开程序,查找内容“俵俽 僑僔僢僋”,替换成“宋体”,注意字节的变化,多余的部分用0来填充。
同样的方法将第四处的CreateFont中的 “俵俽 俹僑僔僢僋”替换为“黑体”,语系改为86。
现在来看第一和第二处,
00528FEE |. >mov eax,dword ptr ss:[ebp+16] ; 将EBP+16地址的字体名放入EAX
00528FF1 |. >push eax ; /FaceName
00528FF2 |. >mov ecx,dword ptr ss:[ebp+14] ; |
00528FF5 |. >and ecx,0FF ; |
00528FFB |. >shl ecx,4 ; |
00528FFE |. >push ecx ; |PitchAndFamily
00528FFF |. >push 1 ; |Quality = DRAFT_QUALITY
00529001 |. >push 0 ; |ClipPrecision = CLIP_DEFAULT_PRECIS
00529003 |. >push 4 ; |OutputPrecision = OUT_TT_PRECIS
00529005 |. >mov edx,dword ptr ss:[ebp+15] ; |EBP+15地址对应的是指定字体的地址+语系
00529008 |. >and edx,0FF ; |EAX&=0xff,就是取十六进制码的后两位,这里就是语系
0052900E |. >push edx ; |CharSet = 128.
0052900F |. >push 0 ; |StrikeOut = FALSE
00529011 |. >push 0 ; |Underline = FALSE
00529013 |. >mov eax,dword ptr ss:[ebp+10] ; |
00529016 |. >and eax,0FF ; |
0052901B |. >and eax,20 ; |
0052901E |. >neg eax ; |
00529020 |. >sbb eax,eax ; |
00529022 |. >neg eax ; |
00529024 |. >push eax ; |Italic
00529025 |. >mov ecx,dword ptr ss:[ebp+10] ; |
00529028 |. >and ecx,0FF ; |
0052902E |. >and ecx,10 ; |
00529031 |. >neg ecx ; |
00529033 |. >sbb ecx,ecx ; |
00529035 |. >and ecx,2BC ; |
0052903B |. >push ecx ; |Weight
0052903C |. >push 0 ; |Orientation = 0
0052903E |. >push 0 ; |Escapement = 0
00529040 |. >push 0 ; |Width = 0
00529042 |. >mov edx,dword ptr ss:[ebp+C] ; |
00529045 |. >push edx ; |Height
00529046 |. >call dword ptr ds:[<&GDI32.CreateFontA>] ; \CreateFontA

字体名称我们已经用UltraEdit替代了,关键就是语系的问题,我们用直接赋值的方法,将语系部分改成:
00529005 68 86000000 push 86 ; 直接压入中文语系
0052900A 90 nop
0052900B 90 nop
0052900C 90 nop
0052900D 90 nop
0052900E 90 nop
0052900F |. 6A 00 push 0 ; |StrikeOut = FALSE
第二处修方法也一样:
00529150 |. 8B45 16 mov eax,dword ptr ss:[ebp+16]
00529153 |. 50 push eax ; /FaceName
00529154 |. 8B4D 14 mov ecx,dword ptr ss:[ebp+14] ; |
00529157 |. 81E1 FF000000 and ecx,0FF ; |
0052915D |. C1E1 04 shl ecx,4 ; |
00529160 |. 51 push ecx ; |PitchAndFamily
00529161 |. 6A 01 push 1 ; |Quality = DRAFT_QUALITY
00529163 |. 6A 00 push 0 ; |ClipPrecision = CLIP_DEFAULT_PRECIS
00529165 |. 6A 04 push 4 ; |OutputPrecision = OUT_TT_PRECIS
00529167 |. 68 86000000 push 86 ; |CharSet = 134.
0052916C |. 90 nop ; |
0052916D |. 90 nop ; |
0052916E |. 90 nop ; |
0052916F |. 90 nop ; |
00529170 |. 90 nop ; |
00529171 |. 6A 00 push 0 ; |StrikeOut = FALSE
00529173 |. 6A 00 push 0 ; |Underline = FALSE
00529175 |. 8B45 10 mov eax,dword ptr ss:[ebp+10] ; |
00529178 |. 25 FF000000 and eax,0FF ; |
0052917D |. 83E0 20 and eax,20 ; |
00529180 |. F7D8 neg eax ; |
00529182 |. 1BC0 sbb eax,eax ; |
00529184 |. F7D8 neg eax ; |
00529186 |. 50 push eax ; |Italic
00529187 |. 8B4D 10 mov ecx,dword ptr ss:[ebp+10] ; |
0052918A |. 81E1 FF000000 and ecx,0FF ; |
00529190 |. 83E1 10 and ecx,10 ; |
00529193 |. F7D9 neg ecx ; |
00529195 |. 1BC9 sbb ecx,ecx ; |
00529197 |. 81E1 BC020000 and ecx,2BC ; |
0052919D |. 51 push ecx ; |Weight
0052919E |. 6A 00 push 0 ; |Orientation = 0
005291A0 |. 6A 00 push 0 ; |Escapement = 0
005291A2 |. 6A 00 push 0 ; |Width = 0
005291A4 |. 8B55 0C mov edx,dword ptr ss:[ebp+C] ; |
005291A7 |. 52 push edx ; |Height
005291A8 |. FF15 B4105500 call dword ptr ds:[<&GDI32.CreateFontA>] ; \CreateFontA

改好后保存,软件已经可以输入中文字体了。也没有乱码了。
附带图片
 
四、总结
纵观软件对几处语系的调用处,都很类似,就是将输入字体的语系压入某一寄存器,然后跟日文语系对比,它的代码基本上是这样的:
cmp(test) 寄存器,80
比如:cmp eax,80
cmp ecx,80
cmp edx,80
知道了这点,其实我们很容易用UltraEdit的替换功能来达到目的,用UltraEdit载入程序,选择“搜索”-“替换”,查找内容输入:3D 80000000(cmp eax,80),替换内容输入:3D 86000000,全部替换就可以了,还有81F9 86000000和81FA 86000000,因为程序只用了EAX、ECX、EDX寄存器来装语系,如果你输入cmp edx,80、cmp esi,80之类的会查不到,全部修改后,将几个调用CreateFont处的PUSH 80改为PUSH 86就可以了(千万不要搜索所有的PUSH 80用PUSH 86代替,因为并不是所有的86都是语系,有的是对话框ID之类的,改了就完蛋了)。
这么说前面几篇的分析都是多余的了,其实不然,起码我们知道程序是直接用80跟输入字体的语系进行对比的,假如它换种方式,将80赋值给某一寄存器,比如MOV ECX,80 CMP ECX,EAX(EAX中放着输入字体的语系),或者通过算法得到80,然后进行对比,那我们用搜索大法一点都没有用。全文到这里为止,放上一个修改后的EVA ANIMATER主文件,2.3版的(只修改了语系)。

汉化新世纪 责任编辑: 乾 .:|:. 标签(Tag): 动态汉化

·上一篇: 关于汉化资源借鉴的说明! ·下一篇: 投诉指南(试行版)

· 版权申明: 本文引自《汉化新世纪论坛》,如有版权疑问请及时联系本站,以便本站处理。

· 转载申明: 本文引自《汉化新世纪论坛》[ 作者: laomms],如需转载请直接联系原始作者,并请注明原始出处。

相关文章                                                                                发表评论 打印此文 关闭窗口

| 设为首页 | 加入收藏 | 联系我们 | 友情链接
Creative Commons License,创作共用协议(中文版)  署名 非商业性使用 禁止演绎
本站内容,除转载或版权特别申明的内容外,皆遵守 创造共用协议中文版之“署名-非商业性使用-禁止演绎 2.5 中国大陆”条款
This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 2.5 China License.
本网站内容源自汉化新世纪论坛的摘录和汉化新世纪成员的原创文章。
凡汉化新世纪论坛的文字皆默认为汉化新世纪与原作者共同拥有并授权发布。
如对本站发布文章有所异议请来信告知,我们将及时删除。
凡商业摘录本站文字请先与我们联系,本站将保留非授权商业发布的追究权利。
凡非商业摘录本站文字请明显注明出处和原作者,并不得改动,凡改动必先征求原作者同意。
苏ICP备05002283号