最近沉迷于一款GameMaker引擎制作的游戏Circadian Dice,发行于Steam平台。游戏本身并没有中文,因此决定上手汉化一下。
GameMaker引擎
- GameMaker是Yoyogames公司开发的一款2D游戏引擎。它拥有直观的图形界面和灵活的编程功能,使得游戏制作变得相对简单和直观。许多游戏制作爱好者和中小型独立游戏制作者,都利用GameMaker来创建自己的2D游戏作品。但由于其资源打包方式,制作的游戏对于汉化非常不友好。
- GameMaker引擎按时间顺序分为GM8(GameMaker8.0)、GMS(GameMaker:Studio)和GMS2(GameMaker:Studio 2)三个版本,最新的GMS2在2022版本后重新更名为GameMaker。Circadian Dice是使用GameMaker 2023.1.0开发制作的,因此本篇文档所有内容均基于GameMaker 2023.1.0版本。
相关工具
- UndertaleModTool(UTMT)
一个专为GameMaker游戏爱好者设计的强大模组制作工具。它允许用户读取游戏的数据文件,还提供了对游戏内部数据的全面编辑功能。支持GMS1.4和GMS2,支持引擎数据版本 13-17。
用于游戏资源解包和重新打包。 - GameMaker
引擎本体。 - Ida64
静态反汇编。 - Cheat Engine
动态反汇编。 - UltraEdit
二进制文件编辑。
解包资源
- GameMaker游戏主要包含两个文件,exe程序和data.win,data.win即是打包的资源文件。
使用UTMT打开游戏目录下data.win,打开时提示游戏使用YYC(YoYo Compiler),脚本被编译到可执行文件中,这也是游戏汉化困难的主要原因之一。
- UTMT打开data.win后可以看见里面包括了图片、字体等资源,也包括脚本和文本,不过YYC模式下,脚本和文本只有名称,没有具体内容。
YYC模式
- YYC是GameMaker提供的一种编译模式,可以提高程序运行效率。YYC模式下,生成的data.win中只包含资源(assets),而脚本和字符串被嵌入可执行文件中,所以汉化没法通过只修改data.win来完成,必须要编辑exe程序。
- YYC模式本质上是GameMaker将GML(GameMaker Language)解释成C++代码后,再利用VS编译工具进行编译生成程序。
用GameMaker写个测试程序来了解一下GML中的字符串通过YYC编译生成的C++中间代码。
GameMaker中创建一个空白工程,然后Room1的初始化代码中加入一条语句test = “Hello World”,使用YYC模式编译生成。
生成完毕后,GameMaker会在临时目录生成一个C++工程,用VS打开生成的C++工程,在gml_Room_Room1_Create.gml.cpp文件中可以看见YYC编译生成C++中间代码。
#include "pch.hpp"
extern YYVAR g_Script_gml_Room_Room1_Create;
#ifndef __YYNODEFS
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);
#else
extern const char g_pString1_FB6412F6_s[];
extern const YYRValue g_pString1_FB6412F6;
#endif // __YYNODEFS
void gml_Room_Room1_Create( CInstance* pSelf, CInstance* pOther );
#ifndef __YYNODEFS
void gml_Room_Room1_Create( CInstance* pSelf, CInstance* pOther )
{
YY_STACKTRACE_FUNC_ENTRY( "gml_Room_Room1_Create", 0 );
YYGML_array_set_owner( (int64)(intptr_t)pSelf );
YY_STACKTRACE_LINE(1);
YYRValue* sWself_test = &((CInstanceBase*)pSelf)->GetYYVarRefL(kVARID_self_test); /* set ContextID to 1 */
(*sWself_test)=g_pString1_FB6412F6;
;
}
#endif
- 很明显,字符串Hello World被存放到一个以0x00结尾的字节数组中(即C语言字符串存储格式),那意味着只要替换程序文件中对应数组内容就可以更改该字符串。
替换文本
- 尝试替换游戏标题界面左下角版本号的文本 “Version 3.2.01”
用UltraEdit打开Circadian Dice.exe,搜索字符串“Version 3.2.01”。
00acc7e0h: 56 65 72 73 69 6F 6E 20 33 2E 30 2E 30 31 00 00 ; Version 3.0.01..
- 可以看到,这就是一个存储对应字符串的字节数组,以0x00结尾,与前面C++中间代码相一致。
尝试改成"Hello World",不足位用0x00填充。
00acc7e0h: 48 65 6C 6C 6F 20 57 6F 72 6C 64 00 00 00 00 00 ; Hello World.....
保存后重新打开游戏程序,标题界面左下角版本号的文本更改成功。
总结
- 是不是很简单?exe程序中搜索文本,然后替换,完成!
- 别急,才刚上路,换成中文试试。