GameMaker引擎游戏汉化(三)

现在已经知道怎么替换文本数据和加入汉字字体用来显示汉化文本,剩下的就是体力劳动。

找到英文文本
汉化翻译
替换文本数据
更新字体汉字范围
重新导入字体资源

如果手动从游戏里寻找要汉化的英文文本,那工作量可不小,并且会造成汉化的遗漏,有没有办法能一次性批量获取游戏里所有的文本?

字符串访问
  • 先来研究一下Gamemaker游戏里字符串访问的具体实现。
  • 第一节提到过,在YYC模式下,下面这条GML语句
test = ”Hello World”
  • 会被解释成C++代码
const char g_pString1_FB6412F6_s[] = {
0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x00,                              // Hello World.
};
const YYRValue g_pString1_FB6412F6(g_pString1_FB6412F6_s, true);
  • 第一条语句是存放文本具体内容的字符串,第二条语句将C字符串类型转换为BML类型,YYRValue是BML类型在C++中的定义。
  • 但是游戏程序是没有C++中间代码可以参考,只能反汇编得到汇编代码。
  • 先用Ida打开GameMaker测试用空白工程所生成的exe程序,看一下字符串访问的最终汇编代码实现。

Ida打开测试exe,搜索字符串”Hello World”

.text:0000000140001000                               _dynamic_initializer_for__g_pString1_9F1281B1__ proc near
.text:0000000140001000                                                                       ; DATA XREF: .rdata:g_pString1_9F1281B1$initializer$↓o
.text:0000000140001000                                                                       ; .pdata:ExceptionDir↓o
.text:0000000140001000 48 83 EC 28                   sub     rsp, 28h
.text:0000000140001004
.text:0000000140001004                               ; void __fastcall YYRValue::_ctor_(YYRValue *this, const char *, bool)
.text:0000000140001004                               YYRValue___ctor_:
.text:0000000140001004 48 8D 15 05 8C 54 00          lea     rdx, g_pString1_9F1281B1_s      ; "Hello World"
.text:000000014000100B 48 8D 0D FE 68 8A 00          lea     rcx, g_pString1_9F1281B1        ; struct RValue *
.text:0000000140001012 E8 49 B2 02 00                call    ?YYConstString@@YAXPEAURValue@@PEBD@Z ; YYConstString(RValue *,char const *)
.text:0000000140001012
.text:0000000140001017 48 8D 0D 62 31 41 00          lea     rcx, _dynamic_atexit_destructor_for__g_pString1_9F1281B1__
.text:000000014000101E 48 83 C4 28                   add     rsp, 28h
.text:0000000140001022 E9 29 64 3C 00                jmp     atexit
.text:0000000140001022
.text:0000000140001022                               _dynamic_initializer_for__g_pString1_9F1281B1__ endp
  • 在代码段找到了一个访问该字符串的代码,g_pString1_9F1281B1_s就是字符串的地址。双击可以跳转到存放对应字符串的数据段。
.rdata:0000000140549C10 48 65 6C 6C 6F 20 57 6F 72 6C+aHelloWorld db 'Hello World',0          ; DATA XREF: sub_140001000+4↑o
  • 这里就是用UltraEdit二进制打开exe程序,搜索文本所找到位置,同样是以0x00结尾的C字符串。
  • 回到代码段,Ida已经贴心的注释这段汇编就是YYRValue的构造函数,函数中调用子函数的注释YYConstString进一步指明了这个BML类型就是字符串常量。
  • 那这段代码有什么用?
    1. 代码里面包含字符串地址。
    2. 代码在exe程序中的位置相对固定。这段代码就位于exe程序代码段的最开始,实际上VS编译C++中间代码时,将所有文本对应的C字符串转BML类型代码放到了程序最开始。为了验证这个,可以在GameMaker的空白工程里不同位置添加不同的字符串,然后编译生成,看看反汇编结果。
  • 现在我们就可以通过遍历exe程序中固定位置的二进制代码来获取字符串地址,再导出所有游戏文本。
反汇编游戏
  • 用Ida打开游戏程序Circadian Dice.exe,可以看到代码段最开始就是类型转换的汇编代码。
.text:0000000140001000                               ; =============== S U B R O U T I N E =======================================
.text:0000000140001000
.text:0000000140001000
.text:0000000140001000                               sub_140001000 proc near                 ; DATA XREF: .rdata:0000000140987E10↓o
.text:0000000140001000                                                                       ; .pdata:ExceptionDir↓o
.text:0000000140001000 48 83 EC 28                   sub     rsp, 28h
.text:0000000140001004 48 8D 15 86 D0 AA 00          lea     rdx, unk_140AAE091
.text:000000014000100B 48 8D 0D 7E E8 EA 00          lea     rcx, unk_140EAF890
.text:0000000140001012 E8 69 56 5D 00                call    sub_1405D6680
.text:0000000140001012
.text:0000000140001017 48 8D 0D 32 28 95 00          lea     rcx, sub_140953850              ; void (__cdecl *)()
.text:000000014000101E
.text:000000014000101E                               loc_14000101E:                          ; DATA XREF: .rdata:0000000140B86B60↓o
.text:000000014000101E                                                                       ; .rdata:0000000140B9A65C↓o
.text:000000014000101E 48 83 C4 28                   add     rsp, 28h
.text:0000000140001022 E9 11 42 89 00                jmp     atexit
.text:0000000140001022
.text:0000000140001022                               sub_140001000 endp
.text:0000000140001022
.text:0000000140001022                               ; ---------------------------------------------------------------------------
.text:0000000140001027                               algn_140001027:                         ; DATA XREF: .pdata:ExceptionDir↓o
.text:0000000140001027 CC CC CC CC CC CC CC CC CC    align 10h
.text:0000000140001030
.text:0000000140001030                               ; =============== S U B R O U T I N E =======================================
.text:0000000140001030
.text:0000000140001030
.text:0000000140001030                               sub_140001030 proc near                 ; DATA XREF: .rdata:0000000140987E18↓o
.text:0000000140001030                                                                       ; .rdata:0000000140B92EB4↓o
.text:0000000140001030                                                                       ; .pdata:0000000140ECB00C↓o
.text:0000000140001030 48 83 EC 28                   sub     rsp, 28h
.text:0000000140001034 48 8D 15 05 D1 AA 00          lea     rdx, aExpertise                 ; "Expertise"
.text:000000014000103B
.text:000000014000103B                               loc_14000103B:                          ; DATA XREF: .rdata:0000000140BB60FC↓o
.text:000000014000103B 48 8D 0D 3E E8 EA 00          lea     rcx, unk_140EAF880
.text:0000000140001042 E8 39 56 5D 00                call    sub_1405D6680
.text:0000000140001042
.text:0000000140001047 48 8D 0D 32 28 95 00          lea     rcx, sub_140953880              ; void (__cdecl *)()
.text:000000014000104E 48 83 C4 28                   add     rsp, 28h
.text:0000000140001052 E9 E1 41 89 00                jmp     atexit
.text:0000000140001052
.text:0000000140001052                               sub_140001030 endp
.text:0000000140001052
.text:0000000140001052                               ; ---------------------------------------------------------------------------
.text:0000000140001057                               algn_140001057:                         ; DATA XREF: .pdata:0000000140ECB00C↓o
.text:0000000140001057 CC CC CC CC CC CC CC CC CC    align 20h
  • 每段类型转换代码占用30h字节,字符串地址位于相对偏移 +7字节的位置,据此就可以直接从exe程序的二进制代码中获得所有的游戏文本。1
总结
  • 终于可以开始汉化了,再来一遍完整流程。
导出所有英文文本
汉化翻译
替换文本数据
统计使用到的汉字
更新字体汉字范围
重新导入字体资源
  • 将英文文本 “Version 3.2.01” 替换为 “版本” 是没有问题的,但如果翻译完整一点 “游戏版本 3.2.01”,你会发现新的问题。

  1. 要从汇编代码的字符串地址获得exe程序文件中字符串的物理地址,首先要先通过汇编间接寻址找到字符串的内存地址,然后再通过PE文件头对应数据段信息,将内存地址映射到文件中的物理地址。 ↩︎

  • 17
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值