【免费下载链接】MarathonRecomp
An unofficial PC port of the Xbox 360 version of Sonic the Hedgehog (2006) created through the process of static recompilation
MarathonRecomp(Marathon Recompiled)是一个通过静态重编译(Static Recompilation)技术,将 Xbox 360 版《Sonic the Hedgehog (2006)》移植到 PC 的非官方开源项目。它不做运行时模拟,而是把游戏原本的 PowerPC 代码直接转写成可读的 C++ 源码、把 GPU 着色器转成 HLSL,让游戏在 Windows、Linux 和 macOS 上以接近原生的方式运行。本文将用通俗的语言带你完整看懂它的工作原理。
一分钟看懂:静态重编译 vs 传统模拟器
| 对比项 | 传统模拟器(Emulator) | 静态重编译(Recompilation) |
|---|---|---|
| 运行方式 | 运行时逐条解释/翻译指令 | 提前把机器码翻译成宿主代码,直接编译 |
| 产物 | 无(每次运行都要翻译) | 可阅读的 C++ 源码 + 着色器代码 |
| 性能 | 受翻译开销影响 | 接近原生 |
| 可修改性 | 只能打内存补丁 | 可直接在源码级别修改游戏逻辑 |
| 可维护性 | 难以长期维护 | 像普通 C++ 工程一样开发 |
打个比方:模拟器像是"同声传译",边说边翻;静态重编译则是先把整本书翻译出版,之后所有人读的都是译文。这也是为什么项目 README.md 强调它"NOT meant for public use"仍在积极开发中——因为整个游戏的代码都要被重写成一份可编译的工程。
项目是怎么组织这场"翻译"的?
整个仓库分成三大块,理解它们就理解了移植的骨架:
- 重编译工具:tools/XenonRecomp 负责把 PowerPC 指令转成 C++;tools/XenosRecomp 负责把 Xenos GPU 着色器转成 HLSL。
- 重编译产物库:MarathonRecompLib 存放工具链的配置与生成出的 C++ 代码。
- 宿主运行时:MarathonRecomp 是你真正运行的程序,包含窗口、输入、音频、文件系统模拟和"补丁层"。
第一步:把游戏代码翻译成 146 个 C++ 文件
构建系统 MarathonRecompLib/CMakeLists.txt 中有一个关键循环:它会调用 XenonRecomp,为ppc_recomp.0.cpp到ppc_recomp.145.cpp共146 个源文件生成内容——这就是整个游戏default.xex可执行文件里 PowerPC 代码的翻译结果。
工具的配置写在 MarathonRecompLib/config/Marathon.toml.in,其中包含:
functions列表:告诉工具"这是一个函数入口,请从这里开始翻译";invalid_instructions:标记哪些字节序列不是指令(比如 C++ 异常帧表),避免误翻译;midasm_hook:汇编级钩子,在翻译出的代码中插入宿主 C++ 逻辑的"接线点",后文会重点讲。
而 MarathonRecompLib/config/switch_table.toml 则记录了 PowerPC 中switch语句的跳转表位置。PowerPC 的跳转表是数据不是代码,工具需要借助这份"地图"才能正确还原出switch-case结构,否则翻译结果会是一堆无法维护的跳转。
第二步:把着色器翻译成 HLSL
游戏的图形代码藏在shader.arc归档文件里。构建时先由 tools/u8extract 解包归档,再由 XenosRecomp 把 Xenos 着色器逐条转成 MarathonRecomp/gpu/shader/hlsl 风格的 HLSL 代码,最终打包成一个shader_cache.cpp(见 MarathonRecompLib/shader/shader_cache.h)。
运行时则通过第三方抽象层 plume 在 D3D12、Vulkan、Metal 等后端上执行这些着色器——也就是说,"Xbox 360 显卡指令"已经被彻底换成了现代 GPU 能直接编译的语言。
核心机制:让游戏代码"住进"一个模拟内存空间
翻译出来的代码不能直接跑,因为游戏代码满脑子都是"内存地址 0x82xxxxxx"。MarathonRecomp 的解法非常巧妙:
1. 一整块连续的模拟内存
MarathonRecomp/kernel/memory.h 中的Memory结构体在启动时申请一整块大内存(base),把游戏的default.xex映像原样拷贝进去。之后Translate(offset)负责"游戏地址 → 宿主地址"换算,MapVirtual负责反向换算。游戏代码看到的 32 位地址,在宿主上变成了真实指针,性能几乎零损耗。
2. 寄存器即结构体
PowerPC 的寄存器状态被建模成一个PPCContext结构体(r3~r31整数寄存器、f1~f13浮点寄存器……),每个翻译出来的函数都接收(ctx, base)两个参数。你可以在 MarathonRecomp/cpu/ppc_context.h 看到它被做成线程局部变量——因为游戏代码可以跑在任意线程上。
3. 游戏主线程就是一个"客人线程"
MarathonRecomp/cpu/guest_thread.h 定义了GuestThread:程序入口 MarathonRecomp/main.cpp 在加载完游戏映像后,执行GuestThread::Start({ entry, 0, 0 }),把游戏的入口点丢进一个真实线程里运行。游戏从此像"住进"宿主程序的租户,通过函数分发表(PPCFunc查找表)在成千上万个翻译函数之间跳转。
4. 跨层调用:C++ 与游戏代码互相调用
MarathonRecomp/kernel/function.h 提供了两套桥接工具:
HostToGuestFunction:宿主 C++ 函数包装成游戏函数(参数自动从寄存器/栈中按 PowerPC 调用约定取回,返回值写回r3/f1);GuestToHostFunction:反过来,让宿主代码以游戏的方式调用翻译后的函数。
指针参数会经过g_memory.MapVirtual/Translate自动在两套地址空间之间转换——这正是"静态重编译"能保持高性能的关键:没有指令翻译层,只有普通的函数调用。
补丁层:在源码级别"修"游戏
这是静态重编译相对模拟器最迷人的能力:因为代码是 C++,所以可以直接写逻辑。
MarathonRecomp/patches/ 目录里躺着一整套质量改进补丁:宽屏与动态分辨率(aspect_ratio_patches)、帧率解锁(fps_patches)、相机控制、文字渲染、加载速度等。
而更底层的修改通过前文提到的midasm_hook实现。以 MarathonRecompLib/config/Marathon.toml.in 为例:
[[midasm_hook]] name = "SetMSAALevel" address = 0x82607CD4 registers = ["r4"]含义是:在 PowerPC 地址0x82607CD4处"拦截",把r4寄存器的值交给宿主 C++ 函数SetMSAALevel处理。配置文件里还能看到InfiniteLives、RestoreChaosSpearFlips、DisableRadialBlur等钩子——它们分别对应"无限生命""还原混沌尖刺翻转动画""禁用径向模糊"等玩家选项。配置一个开关,翻译出的代码就会在正确的位置跳或不跳某段指令。
此外,宿主侧也能在游戏函数执行前后插入逻辑,例如 MarathonRecomp/app.cpp 中对DocMarathonState::Update的包装:先修正帧间隔误差、再执行原始翻译代码、最后读取暂停状态——一个函数调用同时完成了"原逻辑 + 三个增强"。
那些 api/ 头文件是什么?
打开 MarathonRecomp/api/ 会看到按游戏类名组织的头文件树:Sonicteam/Player/State/SonicContext.h、SoX/Graphics/Texture.h、Chao/CSD/...等。这是团队对游戏内部数据结构(类布局、成员偏移)的逆向映射结果,相当于给整个游戏写了一份"接口文档"。
MarathonRecomp/api/README.md 规定了严格的命名规范与映射规则:成员必须带offsetof/sizeof断言保证布局正确、不确定的字段命名为m_Field194(用十六进制偏移标注)……这套纪律确保了头文件与游戏真实内存布局始终一致。
如何参与构建:编译步骤速览
如果你想亲手体验一次完整的静态重编译流程,官方构建文档在 docs/BUILDING.md,核心步骤:
- 克隆仓库(需要子模块,即两个重编译器): git clone --recurse-submodules https://gitcode.com/gh_mirrors/mar/MarathonRecomp
- 放入游戏文件:将合法获取的
default.xex、shader.arc、shader_lt.arc放入MarathonRecompLib/private/(仓库不包含任何游戏资产); - 安装依赖:Windows 用 Visual Studio 2022(含 C++ Clang 组件);Linux 用 clang/cmake/ninja;macOS 用 Xcode;
- 构建:
cmake . --preset linux-release然后cmake --build。
第一次编译会比较久——因为 XenonRecomp 要把整个游戏的代码翻译成 146 个 C++ 文件,再把它们全部编译进最终二进制。之后每次构建,就是再跑一次"翻译 + 编译"。
总结:静态重编译的完整链路
把全文串起来,MarathonRecomp 的移植原理可以概括为一条流水线:
| 阶段 | 做什么 | 关键文件 |
|---|---|---|
| 1. 代码翻译 | PowerPC → C++(146 个源文件) | tools/XenonRecomp、switch_table.toml |
| 2. 着色器翻译 | Xenos Shader → HLSL/Metal | tools/XenosRecomp |
| 3. 运行时 | 模拟内存 + 客人线程 + 寄存器上下文 | kernel/memory.h、cpu/guest_thread.h |
| 4. 补丁层 | 汇编钩子 + 宿主代码包装,实现画质/玩法增强 | patches/、Marathon.toml.in |
| 5. 现代化外壳 | SDL3 窗口/手柄/音频、安装向导、成就菜单 | hid/、apu/、ui/ |
一句话总结:静态重编译 = 逆向 + 代码生成 + 普通 C++ 工程开发。游戏从"只能在一台停产主机的芯片上跑的机器码",变成了"一份可以在任何现代电脑上编译、阅读和修改的源代码"——这正是 MarathonRecomp 最核心的移植原理。
【免费下载链接】MarathonRecomp
An unofficial PC port of the Xbox 360 version of Sonic the Hedgehog (2006) created through the process of static recompilation
相关推荐
为什么KartPad不是Wii模拟器?深度揭秘静态重编译核心原理
为什么KartPad不是Wii模拟器?深度揭秘静态重编译核心原理 KartPad 是一款让 Mario Kart Wii 与 Retro Rewind 在 iO
ReXGlue 是什么:为什么 Xbox 360 重编译器要用 AOT 静态重编译?一篇看懂核心思路
ReXGlue 是什么:为什么 Xbox 360 重编译器要用 AOT 静态重编译?一篇看懂核心思路 ReXGlue 是一款 Xbox 360 重编译器(Rec
Kiwi库架构解密:从Simplex算法到内存优化的实现细节
Kiwi库架构解密:从Simplex算法到内存优化的实现细节 Kiwi是一个高效的C++约束求解库,它实现了经典的Cassowary算法,专为快速求解线性约束系
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考