laqieer/fireemblem8j
GitHub: laqieer/fireemblem8j
该项目是《火焰之纹章:圣魔之光石》日版 ROM 的完整反编译工程,致力于从源代码逐字节重建原版 GBA ROM。
Stars: 1 | Forks: 0
# Fire Emblem: Seima no Koseki (聖魔の光石)
[](https://github.com/laqieer/fireemblem8j/actions/workflows/compare.yml)
[](https://github.com/laqieer/fireemblem8j/actions/workflows/selfcontained.yml)
[](https://decomp.dev/laqieer/fireemblem8j/jp)
[](https://decomp.dev/laqieer/fireemblem8j/jp)
[](https://laqieer.github.io/fe-decomp-portal/)
[](https://github.com/laqieer/fireemblem8j/actions/workflows/progress.yml)
[](https://laqieer.github.io/fe-decomp-portal/)
**Fire Emblem: The Sacred Stones — 日版** 的反编译 / 反汇编
(`Fire Emblem - Seima no Koseki`,游戏代码 `BE8J`)。
它构建以下 ROM:
* `fireemblem8.gba` `sha1: 7da0456035366aa18414faa79d8fe7649f03c1ed`
这是(基本已完成的)美版反编译项目 [laqieer/fireemblem8u](https://github.com/laqieer/fireemblem8u) 的日版对应项目。这两个 ROM 是由相同的编译器从同一套 Intelligent Systems 源代码编译而来的,因此大部分工作在于**将美版的 C 源代码针对日版 ROM 的数据布局进行重新链接**,以及对相对少量的特定区域代码和数据进行反编译(文本、字体、菜单、存档版本控制)。
## 目标
**在移除 `baserom.gba` 的情况下,从提交的源代码逐字节构建 ROM。**
真正的反编译是从匹配的 C (`src/*.c`)、描述性/数据汇编 (`asm/*.s`) 以及**提取的、具有描述性命名的源资源**(PNG/`.pal` 图像、charmap 编码的文本、C 结构体表、音乐)中重现 ROM —— 这些均由一套资源工具链编译。原始 ROM `baserom.gba` **仅**作为 `make compare` 的验证目标;它**绝对不是**构建输入。删除 `baserom.gba` 后,`make` 依然必须输出逐字节一致的 ROM(这正是 `fireemblem8u` 和 `pokeemerald` 已经做到的)。请参阅 [`docs/decomp-completion-standard.md`](docs/decomp-completion-standard.md) 了解下列数字背后的完整标准和客观审计。
这**不是**指“`asm/baserom.s` 中没有任何 incbins”(那只是一个表面的数字,可以通过将 incbins 转移到其他文件来弄虚作假)。唯一重要的数字是**构建自包含性**:移除 `baserom.gba` 并查看 `make` 是否仍能构建。运行 `python3 scripts/check_selfcontained.py` 进行检查,运行 `python3 scripts/calcprogress.py` 查看所有四个维度。
## 状态 — 客观评分表
FE8J 是一个**进行中**的反编译项目,但它已达到基础里程碑:**✅ 在移除 `baserom.gba` 的情况下,ROM 能够从提交的源代码逐字节完美构建。** `mv baserom.gba away && make` 会输出逐字节一致的日版 ROM(sha1 `7da0456…`)—— 自包含构建**通过**(阻挡性 CI 门禁)。四个真实维度(目标:各达到 **100%**):
| 维度 | 目前进度 | 含义 |
|---|---|---|
| **构建自包含性** | **100%** ✅ | 在缺少 `baserom.gba` 的情况下,ROM 仅通过提交的源代码构建 —— **包含 0 条 `.incbin "baserom.gba"`** 指令。这是唯一无法作假的数字,并且已经达成。 |
| **匹配的 C 函数** | **~80.0%** (6,820 / 8,528) | 字节来源于编译 `src/*.c` 的函数。其余部分为 **gbadisasm 描述性汇编**(反汇编,*而非*反编译)—— 这是持续进行的 asm 转 C 的苦力活。(上方 decomp.dev 的 **Code** 徽章按代码*大小*而不是函数数量追踪相同的进度 —— 目前约为 **~67%**,低于按数量计算的数字,因为仍处于汇编状态的函数平均体积更大;在那里,描述性汇编被视为*不匹配*,与 fe8u/decomp.dev 中一样)。注意:客观的上限是 **8,209**,而不是 8,528 —— 大约有 170 个函数(ARM 模式下的 `arm.o`/`m4a_1`、libgcc/libc)即使在美版反编译中也是手工汇编的;以该上限计算,匹配的 C 函数为 **~83.1%**。 **D102**:随着机械化手段被扫清(D100),目前的活跃前沿是针对每个函数的手工反编译,现在有两个互补的工具 —— (a) `scripts/graduate_jp_batch.py`,一个高效的批量收集器(wire-all → build-once → byte-check → revert-mismatches),用于将非叶子节点的日版别名直接移植项毕业(bl/字面量重定位在 JP 链接时解析);(b) **手工反编译**,用于处理批量工具无法识别的内容 —— 存在区域差异的*逻辑*(例如 `GetStringLineEnd` 中 JP 的变宽文本编码)、美版内联函数(`extract_func_only` 找不到干净的定义 —— 从汇编 + struct 头文件重新推导,提供像 `GetUnitMaxHp`/`GetUnitPower` 这样的内联访问器作为 `extern inline`)以及代码生成形态匹配(if/else 对比三元运算符,结果临时变量对比提前返回)。 **D100**:确定性的 **stub-graduation(占位符毕业)** 层级现已扫清 —— `scripts/carve_trivial_funcs.py` 将反汇编过程从未*尝试*处理的简单区域差异 gbadisasm 占位符重新编译为 C(`bx lr`→`void f(void){}`, `movs r0,#N`→`return N`, `ld*/st* [r0,#N]` 访问器,const/zero 字段存储,信号量递增/递减),以实现逐字节一致的 agbcc 输出,受 make-compare 门控限制(+114 个函数)。机械化的匹配 C 手段和可靠的 funcmap 命名手段现在都已耗尽(已验证:0 个仍处于汇编状态的占位符带有 funcmap 名称 —— 它们是占位符*因为*它们存在区域差异/不匹配);剩下的是真正的逐函数手工反编译/permuter 前沿。关键点 (D60/D63/D64/D67/D68/D81/D89):剩余的大部分原本都可以通过机械化方式处理 —— 所谓的“~2,224 个区域差异”估计在很大程度上是分类器的假阴性。扫清大部分阻碍的手段包括:快速的 **reloc-resolve carve(重定位解析切割)**(`scripts/perm2_graduate.py` —— 除了重定位之外与原区域相同),**CF:agbcc 数据绑定**(绑定一个函数引用的 TU 私有数据表,然后它就能编译出字节精确的结果),**per-frag region-same harvest(按片段区域相同收获)**(`scripts/perfrag_carve.py`),**constant-diff carve(常量差异切割)**(`scripts/const_diff_carve.py` —— D81:“FAR”尾部的大部分与美版字节的区别*仅在于*日版数据常量,如 msg-IDs `0x8A3`→`0x843`;替换为日版字面量后即可编译出字节精确的结果),以及 **codegen-shape type-widening sweep(代码生成形态类型扩展扫描)**(D89:真正的代码生成 FAR/LEN 尾部主要由可机械化的临时变量宽度增量主导 —— 日版在美版使用 `s8`/`s16`/`bool` 的地方声明了局部 `int`,因此 agbcc 会保留一个 int 临时变量而不是重新将其窄化;按声明扫描 `signed/bool → int` 并进行验证)。剩余的手工反编译/permuter 前沿(寄存器分配/临时变量排序 + `lsr↔asr` 编译器扩展残留 + 被数据表阻挡 + 纯日版特有内容)在规模上已大幅缩小。 |
| **提取的数据** | **~99.9%** (~13.93 MB) | 真正提取的资源字节数(有类型的 C struct 表 / PNG) ÷ 数据字节数。具名的 `.incbin "baserom.gba"`(来自提交的 `.bin`)**不**计入提取范围。巨大的跳跃(D71)将约 404 KB 的日版消息块作为**编译好的 C 代码**输出(`src/msg_data.c`:Huffman 位流 + 树 + `gMsgTable`,美版结构)而不是 `.byte` 汇编块 —— 链接后字节一致;外加有类型的游戏数据表(章节/属性/物品/角色/职业/AI,D70–D76)。 **D97**:推迟的 **graphics/gbagfx 流水线已被证明可移植** —— 日版 ROM 字节通过 PNG→`gbagfx`→`INCBIN_U8` 有类型的 C 代码进行往返转换,结果逐字节一致(`DATA_INCBIN_CFILES` 预处理路径)。 **D101**:早先“~50-70% 上限”的估计是**错误的** —— `scripts/asmgfx2c.py` 得到了强化,以处理多 `.incbin` 符号(预处理 `INCBIN_U8("a","b",..)` 连接)、真正的切片(`INCBIN_U8("X",off,len)`)、`extern u16 CONST_DATA SYM[]` 类型形式,以及 agbcc 接受的别名形式(`extern T X[1] __attribute__((alias(Y)))`),并带有 `baserom`-incbin 保护,这样就不会出现通过 WARM 但在 COLD 失败的情况 —— 推动数据进度从 **3.51% → 95% → 99.91%**,全部经过 COLD 的 `make compare` 验证。剩余的约 0.09%(12 KB)确实非常困难:struct 类型的符号(需要真正的有类型数据表,而不是 INCBIN)+ GCC 带点的名称内局部标签(`bldyLut.10`,无效的 C 标识符)。 |
| **命名符号** | **~78.0%** (13,340 / 17,103) | 具有有意义名称的标签 ÷ 总标签数。其余部分是 `sub_/data_/nullsub_/sheet` 占位符。可靠的 funcmap 命名手段已耗尽(剩余的函数占位符属于区域差异/不匹配,因此它们没有经过字节验证的美版名称);现在为它们命名依赖于对它们进行手工反编译。 |
进度在[项目看板](https://github.com/users/laqieer/projects/3)上追踪,并在[**FE Decomp Portal**](https://laqieer.github.io/fe-decomp-portal/)上实时绘制图表(与 fe8u / fe6 / fe7j 并列)。有关移植方法论,请参阅 [`docs/strategy.md`](docs/strategy.md)。
## 构建
你需要 ARM 工具链(`binutils-arm-none-eabi`),若要进行 C 反编译,还需将 `agbcc` 安装到 `tools/agbcc`(与美版反编译相同)。
```
# 在此处放置您自己的原始 ROM 副本:
# ./baserom.gba (sha1 7da0456035366aa18414faa79d8fe7649f03c1ed)
make compare # builds fireemblem8.gba and verifies the sha1
```
成功结束时将显示:
```
fireemblem8.gba: OK
```
**`baserom.gba` 不是构建输入** —— 每个 ROM 字节均来自提交的源代码(匹配的 C、描述性汇编、提取的资源、提交的 `data/*.bin`)。它*仅*用于验证结果。**自包含构建**(已达成的最终状态):
```
mv baserom.gba /tmp/ && make # ✅ builds the byte-identical ROM from source ALONE
mv /tmp/baserom.gba . && make compare # restore ONLY to verify: sha1 -> OK
```
使用 `python3 scripts/check_selfcontained.py` 检查当前的自包含性。
## 目录布局
| 路径 | 用途 |
|------------------|----------------------------------------------------------------|
| `baserom.gba` | 原版日版 ROM(由你提供;已被 gitignored)。 |
| `asm/*.s` | 切割/描述性汇编 + 数据。 **~83% 仍为 `.incbin "baserom.gba"`**(生成的 `asm/baserom.s` + 按区域划分的 `.s` 文件) —— 随着反编译的进行,将被提取的源文件取代。 |
| `src/` | 反编译的 C 代码(从美版反编译移植/改编)。 |
| `include/` | 头文件(从美版反编译移植 |
| `ldscript.txt` | ROM 布局;反编译的对象被放置在 incbin 之前。 |
| `tools/agbcc` | GCC 2.95 ARM 编译器(本地安装;已被 gitignored)。 |
| `docs/` | 策略与方法论笔记。 |
## 逆向工程
对于剩余的必须针对日版 ROM 进行手工反编译的区域差异函数(文本/字体/菜单/存档),有两个反编译器**通过 MCP 无头模式连接**,以便 Claude Code(以及自主循环)可以请求获取某个日版地址的伪 C 代码:**IDA Pro Hex-Rays**(服务器 `ida`,主力工具 —— `make ida-db`)和 **Ghidra**(服务器 `ghidra`,开源交叉验证 —— `make ghidra-db`)。
两者均使用项目自身的 `fireemblem8.elf` 作为输入。为了弥补当移植的函数编译结果*接近*但不完全精确到字节时的最后一点差距,通过 `scripts/permuter/` 接入了 **decomp-permuter**(上游工具,现已支持 ARM32/Thumb + agbcc)。设置、基本原理和用法详见 [`docs/reverse-engineering.md`](docs/reverse-engineering.md) 和 [`docs/decisions.md`](docs/decisions.md)(D6)。IDA/Ghidra/permuter 的安装和数据库均在本地/被 gitignored;仅追踪脚本/配置。
## 许可证
反编译基础设施与美版反编译项目保持一致。游戏资产/数据归 Nintendo / Intelligent Systems 所有;不提交任何受版权保护的 ROM 数据。
标签:GBA, URL提取, 云资产清单, 反编译, 客户端加密, 汇编, 游戏, 逆向工具, 逆向工程