在84天内反编译一款Nintendo 64游戏
摘要
文章宣布在84天内完整反编译Nintendo 64游戏Snowboard Kids,强调社区贡献和AI辅助,与以往项目相比,进度更快。
暂无内容
查看缓存全文
缓存时间: 2026/08/27 18:22
# 用84天逆向工程一款任天堂64位游戏
来源:https://blog.chrislewis.au/decompiling-a-nintendo-64-game-in-84-days/
我很高兴地宣布,原版*雪人滑雪*现已100%完成逆向工程!这意味着所有函数¹ (https://blog.chrislewis.au/decompiling-a-nintendo-64-game-in-84-days/#fn:1)都拥有匹配的C语言实现,编译后能生成与原始游戏完全相同的机器码。
这显然不是一个人能完成的工作。我特别感谢inspectredc (https://github.com/inspectredc)、Bl00D4NGEL (https://github.com/Bl00D4NGEL)和queueRAM (https://github.com/queueRAM)对项目做出的重大贡献。任何AI都无法替代他们² (https://blog.chrislewis.au/decompiling-a-nintendo-64-game-in-84-days/#fn:2)。同时也要感谢iFuzzle (https://github.com/iFuzzle)、JamesBLewis (https://github.com/JamesBLewis)和douglasjv (https://github.com/douglasjv)为此提供的宝贵支持。
我希望完整的逆向工程成果能对雪人滑雪社区有所裨益。速通玩家长期以来一直专注于初代游戏。可运行的源代码有助于揭示外部观察到的现象,例如CPU路径选择和影响玩家速度的确切因素。完全理解源代码也将对未来静态重编译和更深入的模组开发有所助益。
项目的完成速度也值得关注。*雪人滑雪*的逆向工程仅耗时84天,相比*雪人滑雪2*的596天,大约只用了七分之一的时间³ (https://blog.chrislewis.au/decompiling-a-nintendo-64-game-in-84-days/#fn:3)。
[图表显示雪人滑雪逆向工程每周进展](https://decomp.dev/cdlewis/snowboardkids-decomp)
雪人滑雪逆向工程的每周进展。(https://decomp.dev/cdlewis/snowboardkids-decomp)
这种差异的原因何在?现在是2026年,答案至少部分在于AI。但将差异完全归因于大语言模型未免过于简化了。
## 有何不同
首先必须明确,我并非从零开始。到那时,我已在类似项目上投入近两年时间,速度远超项目初期。这一优势难以量化,但部分被新挑战抵消了,比如需要使用不同的编译器。
总体而言,约4.8%的匹配提交涉及专家干预⁴ (https://blog.chrislewis.au/decompiling-a-nintendo-64-game-in-84-days/#fn:4)。我之前已感谢过这些了不起的人,但值得再次强调。没有逆向工程社区的大力帮助,特别是inspectredc (https://github.com/inspectredc)、queueRAM (https://github.com/queueRAM)和Bl00D4NGEL (https://github.com/Bl00D4NGEL) <3,这个项目是不可能完成的。
困难通常不在于理解函数功能⁵ (https://blog.chrislewis.au/decompiling-a-nintendo-64-game-in-84-days/#fn:5),而在于如何用C语言表达该逻辑以及生成的代码如何被编译。*雪人滑雪*使用的是IDO 5.3编译器,而非*雪人滑雪2*使用的GCC 2.7.2。
大多数程序员都熟悉GCC。它是一款广泛使用的开源编译器,至今仍在积极开发中。而IDO是SGI开发的专有编译器,其自身发展史与任天堂64位机紧密交织⁶ (https://blog.chrislewis.au/decompiling-a-nintendo-64-game-in-84-days/#fn:6)。其源代码不可用,原始开发环境依赖于过时的SGI硬件和软件。为了在今天使用和正确理解它,逆向工程社区不得不反向工程并部分反编译编译器工具链(https://github.com/n64decomp/ido),并将IDO 5.3和7.1套件静态重编译(https://github.com/decompals/ido-static-recomp)到现代硬件上运行。这种封闭的历史使得IDO比GCC等开源编译器更难理解。
IDO将优化和代码生成分散在多个不同阶段,期间会对代码进行相当激进的转换⁷ (https://blog.chrislewis.au/decompiling-a-nintendo-64-game-in-84-days/#fn:7)。C代码的微小改动可能在这些阶段中传播,并产生完全不同的寄存器分配。
社区在理解IDO及其特性方面取得了长足进步,但这仍然更像是一门艺术而非科学。大语言模型和我本人在重现其输出方面并不特别擅长。对于我和AI代理来说,通常的工作流程是弄清函数功能,然后编写近似该功能的C代码。接着,借助排列器(permuter)(https://github.com/simonlindholm/decomp-permuter)进行微调,以修正任何剩余差异。但在所有情况下,并非总能通过排列获得匹配,特别是当底层结构错误时。IDO的行为使这一工作流程更不可预测。
一个有动力、拥有专业知识和直觉的人类团队可以匹敌甚至超越*雪人滑雪*逆向工程的速度。*飞行俱乐部64*(https://github.com/gcsmith/Pilotwings64Decomp)的逆向工程仅用74天就完成了⁸ (https://blog.chrislewis.au/decompiling-a-nintendo-64-game-in-84-days/#fn:8)。*飞行俱乐部64*的函数数量比*雪人滑雪*少16%,但编译后的代码总量更多,因此这也不是一个完全公平的比较。
[飞行俱乐部64截图]
《飞行俱乐部64》,这款迷人的飞行模拟器,是任天堂64位机的首发游戏之一,至今仍拥有一批忠实玩家。
*雪人滑雪*的规模也小于其续作,包含2,145个函数,而*雪人滑雪2*有2,995个。函数数量只是衡量难度的一个粗略指标,但需要逆向工程的函数确实更少。
## AI代理在哪些方面提供了帮助
我此前已在其他地方(https://blog.chrislewis.au/the-long-tail-of-llm-assisted-decompilation/)撰文介绍如何使用AI代理进行函数逆向工程。这里使用了相同的基本流程,因此我将重点介绍变化之处。与上一个项目不同,这个项目一开始就能使用前沿模型和功能强大的AI代理工具包(https://github.com/cdlewis/nigel)。
### 库代码及其他低垂果实
我想看看AI代理在项目早期阶段的表现如何。它们表现出色的一个领域是匹配标准库代码。理论上,这几乎是任何任天堂64位机逆向工程中最显眼的一部分。代码并非游戏独有,其版本在线上可得。*雪人滑雪*包含了任天堂`libultra`库的百余个源代码段,以及来自`libmus`音频库的函数。N64Sym(https://github.com/shygoo/n64sym)等工具可以识别ROM中可能存在的库函数。
这一过程相当成功。主要的障碍是说服AI代理依赖现有的库源代码,而不是重新从零开始反编译这些函数。这需要更强的提示。一旦确定了可能的库函数,就会指示AI代理将相应的源代码作为起点,在尝试自行实现之前,穷尽所有可能的SDK版本、编译器选项和条件编译路径。
另一个优化措施是让一个AI代理编写脚本,针对每个未匹配的函数运行`m2c`(https://github.com/matt-kempster/m2c)并自动整合任何精确匹配的结果,而不是让AI代理单独尝试这些函数⁹ (https://blog.chrislewis.au/decompiling-a-nintendo-64-game-in-84-days/#fn:9)。该脚本仅匹配了1,830个函数中的17个,成功率极低,仅为0.93%,但以这种方式匹配的任何函数都比消耗AI代理的令牌(token)成本更低。
### IDO工具与技巧
IDO很奇怪,但它常常以重复的方式表现奇怪。成功匹配一个函数可能揭示一个适用于许多其他函数的编译器特性。Codex通过本地记忆(localmemories)(https://learn.chatgpt.com/docs/customization/memories)等功能,已变得更好地在任务之间传递经验。为了使这些经验超越单个AI代理可用,我提示AI代理将观察到的IDO行为记录在DECOMPILATION\_LEARNINGS\.md(https://github.com/cdlewis/snowboardkids-decomp/blob/main/DECOMPILATION_LEARNINGS.md)文件中。当AI代理发现一个可推广的编译器特性时,它可以将证据记录在那里供后续尝试使用。这创造了一个有用的反馈循环。AI代理帮助记录IDO,而生成的文档又使后续的AI代理更擅长匹配IDO代码。
但最有用的资源是N64逆向工程工作台(N64 Decomp Workbench)(https://github.com/akratch/n64-decomp-workbench),这是一个用于调试后期MIPS逆向工程不匹配问题的工具和文档集合。它可以对不匹配进行分类、处理重定位、重放单个编译器阶段,并帮助区分是结构问题还是寄存器分配问题。重放需要相关的编译器二进制文件和项目特定设置,但一旦配置好,它就能提供原始汇编差异所无法提供的信息。原始差异只告诉你两个函数不同。工作台则能让AI代理更清楚地了解函数为何不同,以及可能需要何种修改来修复它们。
### 工作树与同步
对于这个项目,我在四个Git工作树上运行了逆向工程工具包。每个工作树为AI代理提供仓库的独立副本,允许并行尝试多个函数。
一个虽小但很有用的改进是为每个任务设置明确的截止时间,并向AI代理公开该截止时间。在*雪人滑雪2*项目期间,AI代理常常难以有效使用排列器,因为它会持续运行直到找到100%匹配或被手动停止。明确的截止时间允许AI代理设置合理的超时,并在排列时间与其他问题解决形式之间进行权衡。从经验来看,这也有助于它们判断在放弃之前应该在一个困难的函数上工作多久。随着简单函数的消失,剩余工作变得更困难,我可以相应地增加时间配额。
另一个在*雪人滑雪2*中就存在、但随着我添加工作树而变得更明显的问题是同步。正如我之前的文章(https://blog.chrislewis.au/the-long-tail-of-llm-assisted-decompilation/)所讨论的,每个AI代理都被分配一个函数进行逆向工程,同时还有一组已经匹配的类似函数。这些函数提供了重现特定IDO指令模式的有用参考点。工作通过Nigel的`\-\-shard`选项在工作树之间进行分配,该选项使用基本哈希将候选项分配到指定数量的工作者。
这允许更多工作并行进行而不重复,但随着工作树的分歧,引入了一个新问题。例如,一个工作树可能成功逆向工程了一个与别处尝试的函数有99%相似的函数,但这个新参考点在变更合并之前对其他AI代理不可见。定期将所有内容合并到主分支并重新同步工作树解决了这个问题,但同步所有四个工作树可能需要一个多小时。持续同步会浪费时间,而等待太久又会增加偏差。
为了使同步不那么关键,我更新了相似性搜索以检查每个工作树。新匹配的函数可以立即成为其他AI代理的参考,而无需等待其到达主分支。工具包会生成如下消息:
``
1✓ Candidate ["func_80094A94", "func_80094FF4 (../sbk-c)",
2 "func_80094808", "func_8009491C (../sbk-a)",
3 "func_8009469C (../sbk-c)", "func_80093144"] was fixed!
``
这有助于在简单函数消失后保持流程高效。同步对于整合和推送更改仍然必要,但AI代理之间相互学习已不再需要同步。
### 模型选择
我尝试了GPT-5.5和5.6、Claude 4.5和Fable,以及GLM 5.2。这完全是非科学的测试。模型针对一组不断变化的困难函数进行测试,有时是在另一个模型已经取得部分进展之后。但总体而言,Codex继续优于Claude,正如在前一个项目接近尾声时一样。Sol xhigh在可用后尤其有效。
由z.ai提供的GLM 5.2非常令人失望。我之前是GLM的忠实粉丝。它很有效,虽然不算前沿模型,但其慷慨的使用限额弥补了差距。然而,随着限额变得不再慷慨,而延迟依然糟糕,这种权衡变得毫无吸引力。反馈循环太长,以至于我停止给它分配任务,并最终取消了订阅。
## 下一步计划
首要任务是更好地记录这款游戏。100%匹配意味着我们有了每个函数的C代码;但这并不意味着我们理解每个函数的功能。仍有生成的名称需要替换,未知的结构字段需要识别,令人头疼的匹配需要清理,以及大量数据需要描述。
*雪人滑雪*的重编译工作也在进行中。幸运的是,初代游戏共享了在*雪人滑雪2:重编译版*(https://blog.chrislewis.au/snowboard-kids-2-is-recompiled/)中通过补丁解决的许多特性。
[雪人滑雪:重编译版早期截图]
我正在研究将初代游戏的关卡和其他内容移植到第二代游戏的引擎中,尽管我目前还不知道这需要多少工作量。
除此之外,我对逆向工程PlayStation上的*雪人滑雪Plus*很感兴趣,这是初代游戏的一个日本独占扩展版本,包含额外的关卡和角色。
*如果你看到了这里,你可能对逆向工程和雪人滑雪感兴趣。可以查看雪人滑雪逆向工程项目(https://github.com/cdlewis/snowboardkids-decomp)。仍然有大量的清理和文档工作要做,非常欢迎贡献。*
相似文章
Snowboard Kids 2 已实现100%反编译
作者宣布,任天堂64游戏《Snowboard Kids 2》现已完全反编译,意味着所有函数都有了对应的C语言实现,并能够编译回原始汇编代码。该项目耗时近两年,功劳归于N64反编译社区以及AI编程助手(Claude、GLM、Codex)的加速作用。
从零开始的反编译项目:Claude Code与2001年GBA游戏的51%
文章详细介绍了使用Claude Code启动一个2001年GBA游戏的反编译项目,实现了51%的反编译并发现了一个隐藏的彩蛋。
如何在2026年制作一款Nintendo 64游戏
作者详细讲述了他如何将自己的JavaScript游戏引擎移植到C语言,并制作出由Modretro发行的实体N64新游戏Xibalba 64——这是N64主机商业生命周期结束后推出的第二款新游戏。
Show HN:Decomp Academy – 学习将 GameCube 游戏反编译为匹配的 C 代码
Decomp Academy 是一个交互式在线平台,教会用户如何通过编写字节匹配的 C 代码,针对 2001 年 MWCC 编译器生成的真实 PowerPC 汇编输出来反编译 GameCube 游戏。
这个周末你打算做什么?
一位开发者描述了将《完美黑暗64》关卡移植到 noclip.website 的过程,强调了读取 N64 显示列表和重新实现渲染引擎的挑战。