2024 年 5 月 8 日:
- 测试用 Zig
0.13.0-dev.35+e8f28cda9
,源代码在GitHub。 pret小组有多个宝可梦游戏的反编译版本:
- 这些仓库包含针对 GBA 和 DS 处理器(分别为
ARM7TDMI
和ARM946E-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 种其他调用约定可指定。
- 需在两个独立的 C 编译器中选择,
宝可梦字符映射:
- 目前一切编译通过但出现垃圾数据,因为宝可梦字符串渲染器不使用 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
声明编译时可变的缓冲区并填充0xFF
,mapChar
函数实现实际映射,不考虑原始映射器的多字节序列,但已取得一定效果,字符串在游戏中能正确渲染。 - 总体而言,Zig 集成非常简单,接下来将开始利用游戏的 C 头文件在 Zig 中构建功能,目前将专注于其他项目,但 Zig 看起来是一个很有前途的新工具。
- 目前一切编译通过但出现垃圾数据,因为宝可梦字符串渲染器不使用 ASCII,有自己的字符映射定义在
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。