fredangstadt-lang/LineWarsII-DOS

GitHub: fredangstadt-lang/LineWarsII-DOS

该项目将1994年DOS空战游戏LineWars II的引擎从原始二进制文件用C语言进行行为重建,并通过模拟器执行原始机器码的方式进行严格的自动化验证。

Stars: 2 | Forks: 0

# LineWars II,重构版 LineWars II 是 Patrick Aalto 于 1994 年制作的一款 DOS 空战游戏,由 Safari Software 发行。这是它的引擎,从发布的二进制文件中用 C 语言重建。它构建为 单个 `game.exe`(107,200 字节),可以在 DOSBox-X 中玩全部十个任务: 座舱、HUD、雷达、扫描仪、声音、音乐、配置界面和吸引模式(attract-mode) 演示。 ![标题画面](https://static.pigsec.cn/wp-content/uploads/repos/cas/b9/b92b633d2c6f73f796dddc2ed22fd1b1656c17715519365340a9e542915583b6.png) ![演示任务:Cobra 战机正在攻击受保护的 StarBase](https://static.pigsec.cn/wp-content/uploads/repos/cas/9b/9bbd557fad630d223e1c93a5ad17ace70aa1d85be05a79546e5b533f02f9e821.png) ## 只想玩这款游戏 `dist/` 是一个预构建的、可直接运行的包:无需编译器,无需构建步骤。它包含 `GAME.EXE` 和 `GAME.DAT`,一个已内置该游戏的可启动 FreeDOS 软盘 (`LINEWARS2.img`),一个 `Play.bat` 启动器,以及一份 `README.txt`。安装 DOSBox-X(免费,dosbox-x.org),双击 `Play.bat`,它就会直接启动进入 游戏。你可以从解压的任何位置运行它。本文档的其余部分适用于 从源码构建。 ## 出处与授权 LineWars II 版权所有 © 1994 Patrick Aalto。在发布任何内容之前,我联系了他, 他在 2026 年 7 月对该项目给予了祝福:该重建版可以以免费软件的形式发布; 任何商业用途都需要单独与他进行授权讨论。此仓库保持在该界限的免费软件范围内。 `NOTICE.md` 包含完整的条款。 ## 这是什么,以及不是什么 这是对零售二进制文件的行为重建,而不是匹配的反编译。该 C 语言代码使用 Open Watcom 构建,因此编译出的操作码有意地与原始可执行文件不匹配。其目标是使用 可移植的 C 语言来计算和绘制 1994 年游戏所计算和绘制的内容,并通过机械化 方式验证,而不是靠肉眼。 验证套件包含 41 个自动化关卡;其中 27 个在模拟环境下执行原始 机器码,并将其与此构建版本进行 diff 比较:包括在扫描输入域上的函数返回值 (仅 `lw2_atan_base` 就在所有 78,060 个黄金向量上进行了检查)、VGA 端口写入、 全部十个任务生成表(逐字节比较),以及一个跨越所有 153,600 个帧缓冲字节的 冻结场景比较。当 C 语言代码与原始版本产生偏差时,构建就会失败(变红)。 哪些是经过关卡验证的,哪些只是实现了功能: | 领域 | 状态 | | --- | --- | | VGA 渲染、定点运算、游戏逻辑、所有 10 个任务生成表 | 经关卡验证,与原始执行字节一致 | | 音频(通过 `SPEAKER.LW2` 驱动的 PC speaker;Sound Blaster DSP/DMA 及其音乐驱动)、键盘 | 已实现并经过游戏测试;未进行 oracle 关卡验证 | | 串口/调制解调器多人模式(Duel/Wing/Group/StarWars) | 在提示屏幕后被存根(stubbed);原版需要串口对端 | | 原版的其他声音设备(Covox 8-bit DAC、Gravis UltraSound、Roland LAPC-I、Roland SCC-1)和视频驱动(S3、VESA、ATI、MCGA) | 不在范围内;此构建版本仅支持 VGA 以及上述两条音频路径 | 如果你在经过验证的范围内发现与原版不一致的行为, 那是一个 bug。请提交它。 从原版重建的每个函数都带有其来源的偏移量。 `lw2_flight.c` 中的 `/* @0x24a4..0x24a7 */` 表示原始 映像中的这四个字节是该行重建的内容。这是故意的:这是将 2030 年发现的 bug 追溯到 1994 年代码的方式。(另一个独立的 SDL3 移植版, ,去除了所有这些内容,因为 移植版需要的是行为,而不是出处。) ## 它是如何制作的 LLM 智能体编写了它:由 Claude Opus 4.8 & Fable 5 主导,较小的模型作为 工作者,由一名操作员驱动。在 2026 年 7 月进行了 12 个日历日,其中 10 天处于 活跃状态,通过 12,310 次调用花费了 2,655.98 美元的 API token。这些数字是 从运行自身的记录中解析出来的。我不会花力气去证明这个时间线;信不信由你。 这个产物才是我会维护的部分。 它不是一帆风顺的。第一周准确地产生了文献中关于 LLM 反编译所预测 的结果:看似合理、自信但却是虚构的代码。有一次, 一个子智能体在一个启动时就崩溃的构建版本上报告了 MISSION WON。这被 老派的方法捕获了,即由人类来玩游戏。回应是机械化的: 每一条之前的声明都根据重新运行的反汇编进行了重新审核,并且从 7 月 3 日起,如果没有执行原始字节的关卡对变更进行投票裁决,任何改动都不会发布。 在这个过程中发现了两个比较丑陋的问题:一个静默的 DGROUP 耗尽导致 getenv() 失效并杀死了所有声音,以及一个优化器丢弃了 far 指针的段(segment) 部分。 ## 开发在哪里进行 这个仓库是存档:反汇编注释、关卡,以及 DOS 构建版本,它们作为参考被完整保留,以备日后需要挖掘任何内容之用。 新功能不会合并到这里。未来的工作(TCP/IP 多人游戏、作为设置选项的敌方和 友方 AI 的导弹使用、可定制的第 10 个任务、进一步 的移植)在 SDL 仓库中进行: 。 ## 包含内容 ``` engine/ 23 C modules and their headers. 20 are linked into game.exe; lw2_setup.c, lw2_object.c and lw2_image.c are earlier reconstructions kept for reference and for the tests. tests/ 41 gates. unit/ = golden-vector tests; oracle/ = the Unicorn harness that executes the original bytes; stubs/ = link stubs. tools/ build scripts, the codegen gate, the naming and style checkers, the performance bench, generators/ for the extracted-data headers. reference/ the original game and the dumps every gate diffs against. docs/ the reverse-engineering log, per-module notes for 9 of the modules under docs/engine/, and screenshots in docs/captures/. data/ game.dat, the assets the engine reads at runtime. ``` ## 前置条件 这里没有内置任何这些工具;全部都是免费的。 - **Open Watcom 2.0** 用于构建 DOS 二进制文件()。 将 `WATCOM` 指向你的安装路径,或者在 `tools/watcom/ow2` 中放入一个: $env:WATCOM = "C:\WATCOM" # PowerShell export WATCOM=/opt/watcom # bash - **gcc** 和 **bash** 用于构建并驱动测试。 - **Python 3**,带有 **`unicorn`**(`pip install unicorn`)用于 oracle 关卡。 没有 unicorn,41 个关卡中有 27 个无法运行,并且套件会失败,而不是 报告剩下的 14 个关卡成功。如果完全没有 Python,测试套件将直接 失败。 ## 构建 ``` powershell -File tools\build_game.ps1 ``` 已通过 Windows PowerShell 5.1 和 Open Watcom 2.0 beta 测试。生成 `engine/game.exe`,107,200 字节。要运行它,请将 `game.exe` 和 `data/game.dat` 放在同一个目录下,并让 DOSBox-X 指向它。 构建被刻意拆分了。所有中断可触及的部分都使用 `-2` 编译, 因为 16 位 ISR 序言不会保留 32 位 寄存器的上半部分,而完整的 `-3` 构建会在菜单绘制时挂起。只有四个主循环 模块(`lw2_math3d`、`lw2_render`、`lw2_flight`、`lw2_gfx_vga`)使用 `-3`,这 带来了真正的 `imul`/`idiv`,而不是 Watcom 的 `__I4M`/`__I4D` 辅助调用。 `tools/build_game.ps1` 记录了每个标志及其存在的原因。 ## 测试 ``` bash tests/run_unit_tests.sh ``` 预期输出 `UNIT TESTS: all passed`:12 个宿主测试,27 个 oracle 关卡,2 个源码关卡。 这需要几分钟时间,其中大部分时间都是 Unicorn 在单步执行 1994 年的代码。 oracle 关卡只需要 gcc 和 unicorn;object-hash 源码关卡还需要 Open Watcom,如果没有它,测试套件会指出缺失的编译器并失败, 而不是少报结果。`LW2_ALLOW_NO_WATCOM=1` 接受 40 个关卡的运行。 这两个源码关卡值得了解一下: - `tools/prove_identical.py` 编译所有 20 个链接模块,并将每个模块 与记录的基线进行比较。重命名局部变量或重写注释不能 移动任何一条指令,因此如果哈希值发生变化,说明该编辑做了它 不应该做的事情。 为了使该基线在本机之外有意义,必须处理两件事, 而第二件是真正的 Open Watcom 怪癖。首先,`wcc` 将源码的 名称标记到 THEADR 中,并将每个依赖项的路径和 mtime 标记到 COMENT class 0xE9 记录中,因此如果你仅仅是解压到 其他地方或触碰了头文件,原始的 `.obj` 哈希值就会改变:同一个 `lw2_fmt.c` 在这里生成了一个 646 字节的目标文件,而从更长的路径 则生成 661 字节,代码完全相同,15 个字节的路径差。该关卡仅 对携带代码和符号的记录进行哈希处理,并跳过那两者。其次, 更奇怪的是:**Open Watcom 的代码生成取决于构建路径的长度。** 从一个 36 字符的目录和一个 39 字符的目录编译字节相同的 `lw2_gfx_vga.c`,你会得到两个不同的函数,具有不同的栈(stack) 槽和额外的重新加载。这是一个阈值,而不是滑动,并且该模块上的 `-oe=80` 正是暴露它的原因。所以该关卡不会在你解压的地方编译:它将 `engine/` 复制到一个较短的固定路径并在那里进行编译。因此, `tools/object_hashes.json` 中的基线可以在任何地方重现,这一点已通过 在不同的目录名下从 112 字符的路径构建它进行了测试。 - `tools/check_names.py` 会对以逆向工程中碰巧落入的寄存器命名的变量报错。 `cl` 表示值所在的位置; `shade` 表示它是什么。Delta(`dx`/`dy`/`dz`)、坐标系列 (`cx`/`cy`/`cz`、`sx`/`sy`)和名为 `ch` 的 `char` 是豁免的, 测试中的 `asm_ref`/`ref_*` 模型也是如此,它们的全部工作就是镜像 原版的寄存器。 `tools/check_knf.py` 检查 OpenBSD style(9)。它是一个独立的工具,而不是一个 关卡,它目前报告了大约 2,800 行长行:该树在注释中保留了 `@0x` 考古信息,而这些注释超过了 80 列。那是一个 选择,而不是疏忽。 ## 性能 在 DOSBox-X 中设置 `cycles=32000` 时,`game.exe` 运行吸引模式(attract demo)的速度为 26.4 fps, 运行任务 1 的速度为 46.8 fps,这是使用 `tools/perf_bench.ps1` 针对 `tools/perf_baselines.json` 测量得出的。在优化过程中,每帧的工作量从 40.3 毫秒 下降到了 25.6 毫秒,减少了 36%(`docs/PROGRESS.md`)。原版仍然更快; 缩小剩余的差距是正在进行的工作。 决定这里每一项优化决策的因素:在 DOSBox-X 的正常核心中,一个 模拟周期就是一条指令,固定的,并且一次 VGA 字节写入消耗 1 个周期, 因为 `vmemdelay` 默认为 0。唯一按硬件速率计费的是 端口 I/O,一个字(word)的 `OUT` 为 16 个周期,一个字节(byte)为 25 个周期。因此,这里的衡量标准是 指令计数,而不是内存流量,这与 1994 年时代的 优化指南告诉你的做法大相径庭。`docs/DOSBOX_COST_MODEL.md` 中有相关的 测量数据。`docs/PROGRESS.md` 记录了那些结果变*慢*并被 还原的实验,包括一个停放索引方案(28.7 到 29.9 毫秒)和一个 RAM 平面渲染目标。 在 `tools/perf_report.py` 中有一个测量注意事项:任何改变帧 计时的构建也会改变自动驾驶仪的轨迹,因为输入是按 dt 缩放的,所以 除非你强制设定,否则两个构建版本测量的不是同一个场景。工作量计(work meter) (`-dLW2_DIAG_NOBLIT`)的存在就是为了规避这个问题的。 ## 原始游戏文件 `reference/original/lw2/` 是 1994 年发布的原版。`reference/original/` 还包含解包后的可执行文件和提取的驱动程序二进制文件。关卡 需要它们:一个不执行原版代码的关卡不能证明关于原版的任何信息。 LineWars 版权所有 1994 Safari Software 和 Patrick Aalto,并且是 Patrick Aalto 的商标(`reference/original/lw2/LW2.DOC`)。 `engine/` 中的重建版是一个从头开始的 C 语言重写,不包含任何原始的 机器码,但它衍生自该作品,并且仅仅是因为该作品而存在。两个头文件确实携带了原始*数据*而不是代码,这些数据从二进制文件中逐字提取, 并且可以通过 `tools/generators/` 中的脚本重新生成: `lw2_setup_ui.h`(配置编辑器 UI 表)以及简报和 DS 文本表。 ## 状态 功能完善并已通过游戏测试:所有十个任务、声音、音乐、座舱、HUD、 雷达、配置界面、F1-F4、暂停。 `docs/PROGRESS.md` 是工作日志,而不是一份经过润色的文档,未完成的项目都在那里。 诚实的总结:这在每个代码块(block)上是语义等效的, 并不与原始二进制文件字节完全一致。Open Watcom 的寄存器调度与 1994 年的编译器 不同,并且为了可移植性,早期就放弃了字节一致性。当某个关卡报告 “0 mismatch”时,这意味着编译后的 C 语言代码与执行的原版在测试的每个输入上都达成了一致,并且测试说明了那是多少个输入。 ## 许可证 重建的 C 语言源代码版权归 2026 Fred Angstadt 所有,采用 PolyForm Noncommercial License 1.0.0(`LICENSE.md`):出于 任何非商业目的,你可以使用它、修改它并分享它。原始游戏及其数据(`reference/`、 `data/`、`dist/`)仍归 1994 Safari Software 和 Patrick Aalto 版权所有,并 经 Patrick Aalto 于 2026 年 7 月 16 日的书面许可,在此作为免费软件重新分发。 对这里的任何内容进行商业用途使用,必须事先获得他的同意。 `docs/dbx_src/` 中的 DOSBox-X 源码摘录采用 GPL-2.0-or-later 许可,并带有 各自的许可证(`docs/dbx_src/COPYING`);该代码均未被编译进 游戏。详见 `NOTICE.md`。
标签:DOS, DOSBox-X, Open Watcom, 云资产清单, 客户端加密, 游戏, 逆向工具, 逆向工程