原生程序 / 交互案例 / 阅读约 22 分钟
TH04:16 颗子弹怎么知道你在哪?
用 REA 分析 TH04 环形弹幕,追踪字节角度、瞄准和旋转,并在交互图示中验证一个小型重建实现。
本页内容
01. 拆解之前,先试一试
把菱形拖到中心附近,再发射一圈子弹。浅色射线标记第一颗子弹。把数量从 16 改为 12:整数角度步长无法再均分整圈。也可以用键盘操作下方滑块。
16 个方向,每个相隔 16 个角度单位。
这是角度图示,并非游戏实录。鼠标瞄准使用近似 atan2 换算;原程序使用 iatan2。
一个交互角度图示,以及明确验证舍入和回绕的小型环形计算。
分析之前图示可直接使用。若要重复二进制分析,请准备匹配的 TH04 MAIN.EXE 并配置 Ghidra 分析后端。还需要下方链接中的固定版本重建源码。 配置 REA →
02. 先弄清可执行文件的地址体系
TH04 是 PC-98 DOS 程序。追踪函数地址前,先确认加载器如何把文件映射到内存。记录可执行文件哈希、加载段、代码模式和地址到文件的映射关系。缺少加载配置的地址,可能让你读到错误字节。
官方报告提供已知辅助函数和固定版本的重建代码,帮助确定分析方向。借助这个锚点属于引导练习,并不代表智能体独立发现了整个程序。
使用 REA 和 Ghidra 打开我的 TH04 MAIN.EXE。对照链接的 TH04 证据报告,验证 DOS MZ 加载配置和文件身份。若匹配,分析报告中的 bullet_velocity_and_angle_set 辅助函数,并将地址映射回文件字节。若文件或加载配置不同,停止使用历史地址,并解释如何找到等价函数。03. 画圆之前,先还原一个方向
关键是运算顺序。分别梳理索引、数量、瞄准和旋转。这个环形分支可理解为 floor(index × 256 / count),再加上瞄准角和旋转角,最后回绕到一个字节。屏幕上的角度沿顺时针增加。
试试十二颗子弹。先乘再除得到偏移 0、21、42、64…;如果用索引乘以 floor(256 / count),则得到 0、21、42、63…。两者起初相同,随后偏离。看起来合理的圆形也可能藏着错误。
- 16 颗子弹,不加瞄准角:0、16、32、…、240。
- 12 颗子弹:索引 3 应为 64,而非 63。
- 瞄准角 250 加偏移 16,回绕后为 10。
- 右侧为 0,下方为 64,左侧为 128,上方为 192。
04. 将环形规则与瞄准函数分开
追踪玩家和发射原点坐标从哪里进入方向函数,再找到模板旋转角的相加位置,以及最终字节的保存位置。将它们分别记录,才能逐阶段测试。
图示使用 JavaScript atan2,并四舍五入为最接近的字节角度,用于鼠标瞄准。原程序使用 iatan2。若要在每个玩家坐标上完全一致,还需还原并测试该辅助函数;本图示只演示环形算术,不声称两者等价。
只还原瞄准环形弹幕分支。展示先乘后除的指令序列、计算瞄准方向的调用、模板旋转相加以及最终字节存储。独立实现环形算术。在单独确认原版 iatan2 行为之前,图示使用明确标注的近似瞄准函数。测试 12 和 16 颗子弹、负旋转角以及回绕。05. 先测试角度,再判断画面
刻意选择几个棘手输入,导出数值角度。截图可能让一个单位的误差看起来正常。测试不能均分的数量、接近 255 的瞄准角,以及跨过零点的旋转。检查最终字节范围,不要只检查间距。
如果第一条射线略微偏离指针,先检查角度量化,再修改环形计算。如果图示上下镜像,检查坐标约定。如果原生调用图不一致,检查实模式段映射和指令证据。
06. 对比已知结果
将这些结果作为明确的检查点。与它们一致,只能支持这一小段规则,不能证明与整个应用兼容。
| 输入 | 预期角度 | 能发现的问题 |
|---|---|---|
| 数量 16;瞄准角 0;旋转角 0 | 0, 16, 32, …, 240 | 均匀间距 |
| 数量 12;瞄准角 0;旋转角 0 | 0, 21, 42, 64, …, 234 | 先乘后除 |
| 数量 16;瞄准角 250;旋转角 0 | 250, 10, 26, …, 234 | 字节回绕 |
| 数量 4;瞄准角 0;旋转角 −1 | 255, 63, 127, 191 | 负旋转角归一化 |
将两个文件保存在同一文件夹,用 Node.js 运行验证脚本,无需安装软件包。
node check-case-labs.mjs07. 到底验证了什么?
本站测试提取出的环形算术,并绘制独立图示。我们没有执行原 DOS 程序、复现 iatan2 辅助函数,或重复历史编译器验证。十六颗只是选定的教学数量,并不代表所有游戏难度或发射状态。
来源: REA 的 TH04 证据报告 · 固定版本的重建环形宏。教程、图示和浏览器实验均独立编写。