将 Zig 与宝可梦反编译链接起来

2024 年 5 月 8 日:

  • 测试用 Zig 0.13.0-dev.35+e8f28cda9,源代码在GitHub
  • pret小组有多个宝可梦游戏的反编译版本:

    • GBA 平台的宝可梦火红/叶绿,为可读 C 代码。
    • GBA 平台的宝可梦红宝石/蓝宝石/绿宝石,为可读 C 代码。
    • DS 平台的宝可梦钻石/珍珠,大多为汇编代码,有许多未知符号。
    • DS 平台的宝可梦白金,大多为 C 代码,有许多未知符号。
    • DS 平台的宝可梦心金/魂银,大多为汇编代码,有许多未知符号等。
  • 这些仓库包含针对 GBA 和 DS 处理器(分别为ARM7TDMIARM946E-S)的 C 和汇编代码,数十名贡献者投入大量工作将原始机器码转换为可读 C 代码。
  • Zig 是一种新编程语言,提供出色的工具链和强大的元编程能力,同时与 C 代码库兼容性良好,今日项目将重点为火红反编译添加 Zig 支持并展示comptime功能。
  • 添加 Zig 到构建系统

    • 需在两个独立的 C 编译器中选择,agbcc是 GCC 的分支,编译的 C 代码与原始机器码 1 对 1 匹配,sha1构建的原始 ROM 和重新编译的 ROM 相同;devkitARM是包含arm-none-eabi-gcc编译器的工具包,编译出的机器码与原始 ROM 不匹配,称为“现代”编译器/构建,这里使用devkitARM,Zig 支持开箱即用的交叉编译,可将 Zig 源编译为arm7tdmi目标。
    • 为将 Zig 插入pokefired的构建系统,对源代码目录进行修改,src/含游戏引擎(C 代码),asm/含汇编宏,data/含游戏各种数据及脚本(汇编和包含文件),通过Makefile编译和组合,可复制为新的zig/目录,C 代码经过标准 C 预处理器、自定义预处理器后编译为汇编,汇编添加一些行后组装为对象文件,而 Zig 代码无需这些步骤。
    • 为让 C 代码调用 Zig,需创建声明 Zig 符号的头文件,Zig 目前无法自动生成,需手动为每个 Zig 文件编写相应头文件,如添加自定义字符串pub const testZigString = "Zig";,为在 C 中可见需声明extern const unsigned char testZigString[];,但由于未使用的顶级声明不在最终对象中,会导致链接错误,需使用export关键字,export@export的语法糖,用于在comptime导出符号,暗示.C调用约定以匹配目标的 C ABI,Zig 有 17 种其他调用约定可指定。
  • 宝可梦字符映射

    • 目前一切编译通过但出现垃圾数据,因为宝可梦字符串渲染器不使用 ASCII,有自己的字符映射定义在charmap.txt中,C 源中字符串都用独特语法包裹,_()include/global.h中但未做任何事,实际字符串转换由tools/preproc/preproc程序完成,将 ASCII 字符串转换为包含映射字符的字节数组字面量,游戏会自动在每个字符串后添加结束标记0xFF
    • 在 Zig 中无需预处理技巧,可构建comptime函数来操作字符串,使最终对象文件中的字节数组使用正确映射,如pub export const testZigString = mapStr("Zig");函数,mapStr函数接受编译时已知的字符串切片并返回字符串,返回值使用c_char匹配 C 代码期望,长度根据字符串长度加 1 确定,通过comptime var声明编译时可变的缓冲区并填充0xFFmapChar函数实现实际映射,不考虑原始映射器的多字节序列,但已取得一定效果,字符串在游戏中能正确渲染。
    • 总体而言,Zig 集成非常简单,接下来将开始利用游戏的 C 头文件在 Zig 中构建功能,目前将专注于其他项目,但 Zig 看起来是一个很有前途的新工具。
阅读 23
0 条评论