反编译是怎么做的:Tenkaichi3Decomp字节级还原PS2游戏的完整教程

【免费下载链接】Tenkaichi3Decomp

PC port of Dragon Ball Z: Budokai Tenkaichi 3, built on the BT3-Decompiled project.

项目地址:https://gitcode.com/gh_mirrors/te/Tenkaichi3Decomp
点击查看免费下载

Tenkaichi3Decomp是 PS2 名作《龙珠Z:电光石火3》(Dragon Ball Z: Budokai Tenkaichi 3,SLUS-21678)的 PC 原生移植项目,它建立在BT3-Decompiled反编译工程之上:不是模拟器,而是把游戏原本的MIPS 汇编逐函数还原成 C 代码,再用当年的Sony ee-gcc 2.96编译器重新编译,直到产出的二进制与原版字节完全一致,最后再垫上全新的 PC 渲染、声音与输入层。本文带你走一遍这套"反编译 → 字节级验证 → PC 移植"的完整流程。

一、先搞懂:反编译和"重编译"不是一回事 🤔

很多人以为反编译就是"把机器码翻译回 C 语言"。实际上 Tenkaichi3Decomp 追求的目标更苛刻——匹配反编译(matching decompilation):

  • ❌ 普通反编译:写出"逻辑上等价"的 C,能跑就行;
  • ✅ 匹配反编译:写出的 C 必须编译出和原版一模一样的机器码,每一个字节、每一条指令都不能差。

为什么要这么较真?因为 PS2 的浮点运算、随机数序列、30 帧/秒的帧逻辑都高度依赖执行时序,"逻辑等价"的代码在联网对战、录像回放时会逐渐跑偏。字节级一致才能保证移植版与主机版行为完全相同。

二、项目结构:代码从哪来,到哪去?

目录作用
src/、include/游戏本体代码(来自反编译工程),PC 改动写在#ifdef PORT里
port/src/PC 侧新代码:Vulkan/OpenGL 渲染器、内存卡、电影播放、设置窗口
port/tools/构建工具:64 位指针重写、打包、光盘数据提取
port/data/游戏内置数据表的"形状"(只有标签和大小,数值启动时从你的光盘读取)
config/symbols/符号表:每个函数/全局变量的名字和地址
docs/反编译发现的游戏机制笔记(战斗、AI、HUD、存档等)

⚠️ 注意:仓库里不包含任何游戏数据,需要你自己准备的 USA 版光盘镜像(.iso),安装时程序会校验镜像是未修改的 SLUS-21678 版本。

三、MIPS 汇编反编译的完整工作流 🔧

整个流程记录在 docs/decomp_guide.md,可以概括为一个循环:

步骤 1:读汇编

用 splat 反汇编工具把可执行文件拆成一堆.s文件。每个函数长这样:

nonmatching FunctionName, 0xSIZE glabel FunctionName ...MIPS 指令... endlabel FunctionName

步骤 2:手写 C

按地址顺序,一个 C 文件覆盖一段连续的地址范围,跳过不行、乱序也不行。类型来自 include/types.h(s8 u8 s16 u16 s32 u32 f32等)。

步骤 3:记录符号名

每取一个名字就写进 config/symbols/ 下的符号文件,格式固定:

Name = 0x00123456; // type:func gGlobal = 0x002FF080; // size:0x8

猜测的名字要标// guess,且全项目不许重名。

步骤 4:fdiff 逐函数比对

运行python3 scripts/fdiff.py src/<area>/<module>.c,它会:

  1. 用ee-gcc 2.96按原版参数(-O2 -fno-strict-aliasing,游戏代码-G8、CRI 库-G0)编译你的 C 文件;
  2. 把每个函数与原版二进制并排逐条指令比对;
  3. 输出OK表示匹配(重定位字段会被掩码屏蔽,所以链接前也能检查)。

步骤 5:迭代到全部 OK,再链接进大工程

.venv/bin/python configure.py && ninja cmp build/SLUS_216.78.rom disc/SLUS_216.78.rom # 必须完全静默 cmp build/DBZP.BIN disc/BIN/DBZP.BIN python3 scripts/progress.py # 统计反编译百分比

只有ninja成功 + 两个镜像cmp静默,才算真正通过(这就是项目里常说的"gate")。

四、ee-gcc 2.96:为什么必须用"当年那台编译器" ⚙️

这是字节级反编译最玄学也最关键的一环。项目做过一轮完整的工具链排查(见 docs/decomp_guide.md 的 "Toolchain check" 一节),结论非常干脆:

  • 游戏用的编译器是gcc version 2.96-ee-001003-1(PS2 EE 处理器专用分支),其他所有公开 ee-gcc 版本(2.9、2.95、3.2)最多只能保住 16% 的函数匹配;
  • 换 118 组编译参数、361 种组合,没有任何一组能救活"卡住"的函数,反而都会弄坏已匹配的函数;
  • 去掉-fno-strict-aliasing会弄坏 1060 个函数;加-g会弄坏 84 个——所以原版就是无调试信息编译的。

这意味着你手写的 C 必须"迎合" 25 年前编译器的脾气。docs/decomp_guide.md 里沉淀了几十条"血泪经验",举几个:

汇编现象对应的 C 写法
lui/addiu而不是gp_rel小全局变量(≤8 字节)必须声明成完整类型,extern s32 tbl[]不行
beq 1 / slti <2 / beq 2阶梯那是一个switch,且显式写了case 0: break;
sltvssltu、divvsdivu有符号/无符号写反了
多出一条move v1,s1原版"重复计算"了一个表达式,别帮编译器优化
分支延迟槽里凭空多一条指令看include/gcc_prelude.inc的预序列规则:Sony 汇编器会把分支前一条指令"塞"进延迟槽

甚至连long是 64 位、浮点字面量3.14159265f一位都不能少(fdiff 看不见.lit4常量池,必须人工对字节)这些细节都有记录。

五、fdiff 逐函数比对:反编译的"单元测试" ✅

scripts/fdiff.py 是整个工程的质量门禁,思路很巧妙:

  • 掩码屏蔽重定位位:跳转目标、%hi/%lo/%gp_rel立即数这些"链接时才知道"的字段被按R_MIPS_26 / HI16 / LO16 / GPREL16掩掉,因此函数没链接也能提前验证;
  • 函数级粒度:一个文件 100 个函数,99 个OK、1 个卡住,能精确知道差在哪几条指令(原版在左、你的在右并排打印);
  • 盲区靠真镜像补:fdiff 看不见数据初值(比如一张颜色表写错一个字节),所以最终裁判是链接后build/与disc/两个 ROM 的cmp全量比对——历史上 11 色调色板里 5 个颜色写错的 bug 就是这样被揪出来的。

对"怎么写都凑不齐寄存器"的疑难函数,项目还配了两件重武器:

  1. 差分行为测试(build/scratch_difftest/):把原版字节和你的 C 在同一个随机内存上各跑 3000~4000 组种子,比对返回值、每一条外发调用的参数、写出的每个字节——59 个未匹配函数全部测试,只抓到 1 个真实行为差异(而且恰好是原版的复制粘贴 bug,已记录在 docs/known_bugs.md);
  2. 语句排列器(scripts/permute.py):对"只差寄存器分配"的函数自动搜索语句顺序,三轮共解出 30+ 个函数。

实在匹配不上的函数,会写成INCLUDE_ASM(...)保留原版汇编,并把最佳 C 尝试留在#if 0里加注释,绝不偷偷"改对"。

六、链接:最后一公里的对齐地狱 🔗

函数全 OK 之后,链接阶段还有独立的一整套坑,因为数据段的布局也是原版的字节:

  • 每个 C 文件在 config/SLUS_216.78.yaml 里要登记自己的c/.rodata/.lit4/.sdata子段起点地址;
  • 原版把.rodata补齐到 16 字节、现代编译器只补 8 字节——差 8 字节时就要手工"提前 8 字节"放下一段汇编数据块;
  • 跳转表按对象起点做 16 字节对齐,有时得把 C 文件向前扩展、用INCLUDE_ASM把前面的函数"拉进来"才能对齐(src/battle/btl_seq.c 就是例子);
  • 两个可执行文件要一起管:主程序SLUS_216.78(约 1.8 MB 代码)+ 菜单覆盖层DBZP.BIN(737 个函数,加载在 0x334C00,开发者内部叫它PROGRESS)。

进度统计由 scripts/progress.py 给出。截至最新提交:主程序 94.96%、覆盖层 99.24% 的代码由 C 编译产出,两个镜像字节一致,约 7270 个函数全部匹配,仅剩 45 个INCLUDE_ASM。

七、从反编译到 PC 移植 🖥️

字节级一致之后,"移植"就变成了一层干净的工程:

  • 渲染:port/src/gs/里的 Vulkan 渲染器重画 PS2 的画面(描边、辉光、深度着色、远景模糊都可开关),没有 Vulkan 的机器自动落到 OpenGL 3.3;
  • 数学层:游戏 30 帧的战斗模拟用 PS2 VU0 数学,PC 上由softfloat_ps2.c逐位复现,保证帧确定性;
  • 联网:port/matchmaking/是一个完整的 Cloudflare Worker 匹配服务(房间码 + TURN 中继),回滚帧数由房主设定;
  • 数据:仓库只带数据表的"空壳"(port/data/),启动时从你自己的光盘镜像里取真实数值。

游戏机制的发现全部沉淀在 docs/systems/:战斗帧结构、格斗家对象、AI 对手、HUD、存档块 等,每条结论都标注Verified(编译出原版字节)或Inferred(从反汇编推断)。

八、新手入门路线:如何动手参与 🚀

想上手贡献?建议按这个顺序读:

  1. README.md —— 项目总览、安装与源码构建(python3 install.py --iso <你的镜像>);
  2. docs/decomp_guide.md —— 反编译一个模块的完整方法论(核心文档);
  3. docs/status.md —— 当前进度交接笔记,看最新状态怎么验证;
  4. docs/agent_rules.md —— 并行协作的规矩;
  5. 参考已完成的范例:src/sys/heap.c + include/sys/heap.h。

环境上你需要:tools/ee-gcc2.96/(Sony 编译器)、tools/binutils/mips-ps2-decompals-*(MIPS 汇编器/反汇编器)、Python + splat + ninja,以及一份合法获得的 SLUS-21678 光盘镜像。日常验证三板斧:

.venv/bin/python configure.py && ninja cmp build/SLUS_216.78.rom disc/SLUS_216.78.rom python3 scripts/fdiff.py src/<area>/<module>.c

九、为什么"字节级反编译"值得做?💎

  • 确定性:浮点、随机数、帧时序逐位一致,联网对战和回放才不会跑偏;
  • 可修改性:C 代码可以随便加功能(本项目就加了在线对战、新渲染器、自定义关卡/音乐),改完还能一眼看出影响;
  • 可移植性:字节一致是移植的地基——同一份游戏逻辑跑在 Linux、Windows 甚至 Apple Silicon(实验性)上;
  • 研究价值:反编译过程顺带发现了原版随机数只刷新 396/624 状态字的怪实现、碰撞检测里的死代码、以及多个"修不得"的原版 bug(docs/known_bugs.md)。

小结:Tenkaichi3Decomp 展示的是一条可复制的路——原版编译器 + 符号表 + 逐函数字节比对 + 全量镜像验证,把一个 PS2 游戏 7000 多个函数逐一"请回" C 语言世界,再在这个完全透明的地基上构建 PC 移植。如果你对 MIPS 汇编、编译器行为或游戏逆向感兴趣,docs/decomp_guide.md 里那几十条"踩坑笔记"本身就是最好的教材。

许可说明:port/下移植代码为 AGPL-3.0(早期为 MIT),src/游戏代码版权归原作权利方所有;项目不包含游戏资源,需自备光盘镜像。

【免费下载链接】Tenkaichi3Decomp

PC port of Dragon Ball Z: Budokai Tenkaichi 3, built on the BT3-Decompiled project.

项目地址:https://gitcode.com/gh_mirrors/te/Tenkaichi3Decomp
点击查看免费下载

相关推荐

上一篇:din_mmoe基准评测指南:推荐模型性能评估方法与结果解读
下一篇:刚刚:AI 交易 Agent 对苹果一战赚了 26.6%,开源圈彻底坐不住

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考