LASRecomp零游戏文件发行策略:如何在玩家本机完成游戏代码重编译
LASRecomp零游戏文件发行策略:如何在玩家本机完成游戏代码重编译
LASRecomp 是《塞尔达传说:旷野之息外传——织梦岛》(Link's Awakening,Switch 2019)的一个静态重编译项目,它把 Switch 上的 AArch64 游戏代码重编译为 Windows、macOS、Linux 的原生应用。它的核心策略是零游戏文件发行:发布包(.app / .exe / AppImage)里不含任何任天堂的游戏代码、资源或字体,全部游戏代码在玩家首次启动时,由应用自带的生成器和 Zig 编译器在玩家自己的电脑上完成重编译。
为什么"零游戏文件"发行是可行解
直接把 Switch 游戏代码放进发布包,法律上不可行;用模拟器运行 ROM,则与"重编译"的初衷相悖。LASRecomp 的取舍是:
- 仓库和发行包只包含:重编译工具链(tools/gen.py、tools/lifter.py)、运行时(runtime/)、随机化器(third_party/LAS-Randomizer/);
- 玩家必须自备:合法获得的 v1.0.0 原版(未打更新补丁)提取文件,即包含
exefs/与romfs/文件夹的目录; - 首次启动时:应用在本机把 AArch64 指令逐条翻译成 C 语言,再编译链接成一个原生动态库(
lasgame.dll/lasgame.dylib/lasgame.so),游戏运行时直接加载它——这是真·原生代码,而非解释或即时模拟。
Makefile 中的 dist 目标甚至会在打包前用 nm 校验可执行文件内不含任何游戏符号(f_70 开头的函数名),确保发行包"干净"。
发行包里到底有什么:3 件"造代码"的部件
以 Windows 发行包为例(见 Makefile 的 dist 目标),包内只有三样与"重编译"相关的东西:
| 部件 | 作用 | 打包方式 |
|---|---|---|
lasgen/ |
游戏代码生成器(tools/gen.py 及其依赖模块) | 用 PyInstaller 打包成独立程序,玩家无需安装 Python,见 tools/lasgen.spec |
zig/ |
Zig 编译器(作为 zig cc 的 C 编译器使用),固定 0.17.0 版本 |
tools/fetch_zig.sh 校验 sha256 后拉取,tools/zig_slim.sh 预先编译好 C 运行时,避免玩家在链接时现编约 4000 个源文件 |
gamelib-src/ |
生成代码要包含的头文件(runtime/cpu.h、runtime/simd.h)与入口 tools/gamelib_entry.c | 原样随包发布 |
lasgen 内部还带着完成工作所需的库:Capstone(反汇编)、Unicorn(在微型模拟环境中解析跳转表)、python-lz4(解压 NSO 模块),详见 tools/lasgen.spec。
玩家机上的重编译流程:从选文件夹到可玩
整个设置过程由 runtime/ui/launcher.mm 的启动界面驱动,后台线程依次执行:
- 定位与校验游戏文件:在所选目录(最多 4 层深度)里寻找
exefs/main和romfs/region_common,读取 NSO 头部的 Build ID,确认是 v1.0.0 且未更新——版本不对会明确报错; - 复制文件:把游戏文件复制进数据目录(文件系统支持时用硬链接,避免复制约 6 GB 的第二份数据),并构建
romfs.bin数据镜像(runtime/romfs_build.c); - 生成 C 代码:
lasgen把exefs/main与exefs/sdk两个 NSO 模块逐函数翻译为 C。整数、访存与控制流直接转成 C;浮点/SIMD 在 ARM 主机上以 AArch64 内联汇编重放(语义天然精确),在 x86 上降低为可移植 C(tools/lifter.py 的simd='asm|c'两模式);代码按每文件约 1500 个函数分块写出,边写边供编译; - 并行编译:runtime/gamebuild.c 根据 CPU 核心数与内存同时拉起多个
zig cc进程(每文件一个日志),已写出的 C 块不等生成器跑完就开始编译; - 链接与版本标记:所有目标文件链接为
lasgame.dll/.dylib/.so,旁边写入一个.id文件——它是"生成器版本 + 头文件 + 编译标志"的 SHA-256 指纹(GAMELIB_ID,见 runtime/gamelib.h 与 Makefile)。启动时 runtime/gamelib.c 先比对.id,不匹配就拒绝加载并提示重建,保证"库与运行时版本永远配套"。
进度条如实反映这两个阶段(前 10% 是复制与镜像构建,其余 90% 是生成 + 重编译),状态行会在 "Reading the game code..." 与 "Recompiling..." 之间切换;失败时给出短诊断,例如 "The game code didn't build",并提示至少预留约 2 GB 空间。整个过程通常只需几分钟,且中断后可从启动界面重新触发(重编译支持断点续建)。
重编译产物如何被运行时加载
生成库里唯一的导出函数是 tools/gamelib_entry.c 中的 lasgame_tables(),它向运行时交出一张函数地址表(GameLibTables):
- 运行时(runtime/gamelib.c)用
LoadLibrary/dlopen加载库,找到该入口,校验 ABI 版本号与GAMELIB_ID后缓存; - 游戏调用任天堂 SDK 的函数时,经 HLE 层(runtime/hle.h 的
HLE_FUNC注册机制、runtime/hle/hle_symbols.txt)分发到宿主实现——窗口、输入、音频、Vulkan/Metal 图形都在 runtime/nvn/ 中完成; - 库查找顺序:环境变量
LARECOMP_GAME_LIB→ 可执行文件旁 → 玩家数据目录下的extracted/,逻辑见 runtime/gamelib.c 的candidates()。
相比模拟器:这套策略带来什么
- 真原生运行:代码经过静态翻译后被原生编译器(Zig/Clang,
-O1,x86 上带 AVX2-march=x86-64-v3)优化,不存在模拟器每指令的动态翻译开销; - 零授权风险的分发:任天堂 IP 从未进入任何发布渠道,README 的 Legal 一节明确"仓库与发行包均不含游戏文件";
- 天然适配各平台:同一套生成器在 ARM Mac 与 x86 机器上产出不同的 SIMD 代码(内联汇编 vs 可移植 C),产物与玩家 CPU 完全匹配;
- 代价:首次启动需要几分钟重编译和约 2 GB 临时空间,且只支持 v1.0.0 原版(更新版本会使翻译失效,因为补丁会改变被翻译的代码)。
游戏文件与重编译产物存放在哪
各平台的数据根目录(runtime/paths.c):
- macOS:
~/Library/Application Support/com.peiikai.lasrecomp/ - Windows:
lasrecomp.exe所在文件夹(绿色便携,不放 Program Files) - Linux:
~/.local/share/lasrecomp/ - 可移植/开发模式:启动目录中已有
extracted/时直接使用,例如源码树里./build/lasrecomp extracted
其中 extracted/ 放游戏文件与 lasgame.* 库,save/、cache/、fonts/ 各自独立,删除游戏文件不会动到存档。也可用 LARECOMP_DATA_ROOT=<dir> 整体重定向。
小结:一个可复制的"BYO-Files"重编译范式
LASRecomp 的策略可归纳为三步:随包发工具,不发代码;首启校验玩家文件并在本机翻译+编译;用内容指纹保证库/运行时版本一致。对任何"游戏本体不能分发"的重编译或破解周边项目,这套 runtime/gamebuild.c(生成-编译流水线)、tools/fetch_zig.sh(可校验的单文件编译器分发)与 GAMELIB_ID(版本配套机制)的实现,都是直接可借鉴的模板。🛠️

