常有人问:这是基于 N64 模拟器做的吗?还是相当于把游戏重做了一遍?两者都不是。Marchwind 64 用的是静态重编译(static recompilation,简称 recomp):把原版卡带里的程序逐个函数翻译成 C 代码,再用现代编译器编成 Windows、macOS、Linux 和安卓上的原生程序。
模拟器、重做与重编译
- 模拟器在你的电脑上假装是一台 N64:一边运行,一边逐条解释原版的 MIPS 指令,同时模拟 N64 的处理器、显示芯片和音频芯片。游戏本身原封不动,但每一条指令都要经过这层翻译。
- 重做是照着原版重新写一个游戏:画面、规则和剧情都要重新实现,很难做到和原版一模一样。
- 重编译介于两者之间:游戏逻辑还是原版的那一份,只是提前翻译成了本机代码。战斗计算、敌人 AI、事件脚本、存档格式都和原版逐条一致;运行时不需要模拟处理器,所以也能在原版程序的边界上插入新功能。
打个比方:模拟器像同声传译,每句话现场翻;重编译像把整本书先译好再出版;重做则是照着原书的故事另写一本。
从卡带到原生程序
- 找出代码。 原版程序分成 18 段:1 段常驻内存,17 段按需从卡带装入(标题、地图、战斗、场间画面等各有一段,有些段会装到同一块内存)。我们逐段确定了每段在卡带里的位置和运行地址,共 3,407 个函数。
- 翻译成 C。 用开源的 N64Recomp 把每个函数的 MIPS 指令翻译成等价的 C 代码,保留原版的内存布局和计算方式。
- 替换系统层。 原版程序依赖 N64 的系统库(线程、消息队列、计时器、手柄、存档)。N64ModernRuntime 用电脑上的线程和文件实现了这些接口,翻译出来的代码不需要知道自己不在 N64 上。
- 画面与声音。 N64 的画面由显示芯片按「显示列表」绘制。游戏照旧生成这些显示列表,由 RT64 用 Direct3D 12、Vulkan 或 Metal 在现代显卡上画出来,宽屏和高清贴图替换也在这一层。音频芯片上跑的音频程序同样被重编译成本机代码。
- 编译。 生成的 C 代码和我们自己的代码一起,用各平台的编译器编成最终程序。
游戏的图片、声音和文本仍然从你自己的 ROM 里读取,所以需要自备日版 ROM。
在原版之上加东西
因为原版函数都变成了普通的 C 函数,我们可以在指定的函数前后接管或包装它们,现在共有 134 处。主要用在:
- 原生界面。 对白、场间菜单、改造、强化部件、存档、设置等画面,由我们用 RmlUi 重新绘制,支持中英日三语、高分辨率文字和触屏;游戏状态仍由原版代码管理,原版画面也可以随时切回来。
- 翻译。 全部台词以纯文本文件随程序附带,界面和数据文本(人名、机体、武器、精神等)用词条表管理,运行时按语言替换,玩家也可以自己改。
- HD 美术。 头像、背景、地图、立绘、战斗演出等在绘制时替换成高清图,不改游戏逻辑。
- 体验改进。 多存档栏与自动存档、快进与跳过、战斗演出中止、改键、滤镜与框体、可开关的原版 bug 修正等。
- 开发工具。 内置的调试接口可以让 AI 工具读取游戏状态、按键和截图,方便自动测试和复现问题。
我们自己写的部分大约是 3.6 万行 C++(运行时、界面、翻译、HD、平台适配),另有约 4 万行 Python 工具,负责剧情脚本分析、翻译流水线、美术打包、测试和发布。
几个具体例子
对白。 剧情仍由原版的事件脚本推进:谁说话、说到哪一句、什么时候等玩家按键,都由原版代码决定。我们在脚本显示文字的那一刻读出这句台词的编号和说话人,用这个编号去对应的台词文件里取当前语言的译文,再用自带的 HarmonyOS Sans 字体按高分辨率排版绘制。原版的日文字库只有几千个固定字形,这样做就不受它的限制,也能整句重新断行、少翻页。
场间画面。 原版的整备菜单由一张调度表按编号切换各个画面。我们在调度入口判断:如果某个画面已有原生版本,就由新画面接管输入和绘制;玩家确定操作时,再把结果交回原版代码处理。比如改造机体时,扣资金、改数值仍走原版的那一段逻辑,所以价格和上限都和原版一样。
存档。 原版的存档读写全部经过同一个函数,一次搬运一段卡带存档(SRAM)。我们只把这次搬运转到别的文件:32 KiB 的卡带存档文件原样保留,所以能和 ares、Project64、mupen64plus、RetroArch 等模拟器互相导入;多出来的存档栏和自动存档另存为单独的文件,每一份都是原版写出的同一段字节,校验和恢复也照旧由原版代码完成。
跳过战斗演出。 原版本来就能在设置里关掉战斗动画,关掉时走另一条结算路径。我们中止演出时,就切回原版这条「不播动画」的路径继续结算,伤害、命中和经验都与关掉动画时完全相同,不会因为少跑了几帧而影响结果。
宽屏。 RT64 可以把 3D 画面画得更宽,但游戏自己的 2D 元素(状态栏、光标、文字框)是按 4:3 摆放的。我们为这些元素逐个设定在宽屏下的位置,让画面铺满 16:9 时界面依然对齐。
更多 HD 美术的接入方式见HD 美术是怎么做的。
怎样确认和原版一致
- 用模拟器对照。 需要确认原版行为时,我们在 ares 模拟器里运行同一个 ROM、读同一份存档,逐个画面对比。
- 先读代码再动手。 战斗公式、按键绑定、隐藏要素等都先从原版代码里查清楚并写成文档,再决定怎么接入或修正;原版 bug 的修正都做成可以关掉的开关。
- 自动测试。 约 450 个测试检查数据导出、翻译文件、界面词条等;另有离线排版检查,不开游戏就能把每个界面在各种语言和屏幕尺寸下排一遍,找出超出框的文字。
- 自动驾驶游戏。 通过内置的调试接口,脚本或 AI 工具可以启动游戏、按键、截图、读取内存状态,用来复现玩家报告的问题。
还做不到的
重编译保留了原版的全部逻辑,也就保留了它的节奏:游戏逻辑固定每秒 30 帧,等待、动画和滚动都按帧计数,所以没法简单改成 60 帧,只能以后在显示层插帧。同理,凡是原版逻辑决定的事情,比如敌人的行动和战斗结果,我们都尽量不碰,只在默认开启、可以关掉的修正里调整。
一份代码,五个平台
macOS、Windows、Linux、Steam Deck 和安卓用的是同一份代码,图形分别走 Metal、Direct3D 12、Vulkan。每次提交都会在 GitHub 上自动构建 Windows、Linux 和安卓版本。
致谢
没有这些开源项目,就没有 Marchwind 64:N64Recomp 与 N64ModernRuntime(重编译与运行时)、RT64(图形)、RmlUi(界面)、librashader(RetroArch 滤镜),以及 Zelda 64: Recompiled 等先行项目积累的经验。项目源码以 GPL-3.0 发布在 GitHub。