个案研究·东方4:莲花地故事

重建TH04
子弹图案。

一圈弹幕从一段简短的角度计算开始。用 REA 检查原始 DOS 函数,再把指令对应到可读的 C++ 代码。

PC-98·16位DOS · C++和汇编

查看TH04重建

从原始指令到经过验证的源码

  1. 01 · 原创游戏主要。EXE文件该 PC-98 可执行文件及其子弹角助手。
  2. 02 · 用 REA 检查指令 + 引用验证加载的 DOS 代码,再从可执行文件中读取角度计算。
  3. 03 · 阅读 C++等分 → 瞄准 → 旋转把每个操作对应到维护中的重建源码。
  4. 04 · 验证源码2,139 字节匹配已记录的编译器检查覆盖了包含这个辅助函数的弹幕生成代码。
REA 返回原始代码和分析证据。TH04 项目提供维护中的源码,以及编译器检查结果。

环形弹幕如何瞄准玩家

Elly 的攻击请求一个初始数量为 16 的自机狙环形弹幕。辅助函数计算每颗子弹的方向。同一圈弹幕既可以朝向固定方向,也可以转向玩家。

你的编程助手

使用REA查找TH04如何创建一个子弹环并将其瞄准玩家。 解释计算并显示代码。

一个示例提示,原始可执行文件和重建签出可在本地使用。

TH04 用 256 个角度单位表示顺时针转一整圈。16 颗子弹之间各隔 16 个单位:0, 16, 32, …, 240。再加上玩家方向,就能让所有方向一起旋转。

两幅图展示 16 颗子弹的环。固定环从角度 0 开始。加上玩家方向 40 后,每个方向都旋转相同角度,高亮的第一颗子弹指向玩家。两者都保持 16 个角度单位的间距,即 22.5 度。
图中演示还原出的角度计算:子弹数量为 16,玩家方向为 40,模板旋转角为 0。查看大图

还原源码中的发射模板

Elly 的环形攻击 · 源码节选
bullet_template.angle = 0;
bullet_template.group = BG_RING_AIMED;
bullet_template.count = 16;
bullet_template_tune();

这些线在难度和性能调整之前选择模式。 阅读攻击的来源.

REA 返回什么

我们检查设置每颗子弹速度和角度的函数 bullet_velocity_and_angle_set()。REA 返回指令、伪代码和引用,其中三部分解释了这种弹幕如何生成。

  1. 在阅读其代码之前检查DOS映像

    编程助手→ REA
    open_binary
    {"path": "MAIN.EXE"}
    
    inspect_native_load_image
    {}
    REA →编程助手 · 选定字段
    status: "verified"
    format: "dos-mz"
    load_segment: 4096
    header_bytes: 6144
    module_bytes: 150114

    这项检查确认加载的代码和重定位记录对应提供的可执行文件。上方路径为便于显示而缩短。

  2. 查看环形弹幕的乘除计算

    编程助手→ REA · analyze_function
    {"procedure": "0x2cfc8"}
    REA →编程助手·指令摘录
    0x2d0eb: MOV AX, SI
    0x2d0ed: SHL AX, 0x8
    0x2d0f0: MOV DL, byte ptr [0x53af]
    0x2d0f9: IDIV BX

    SI 保存子弹索引。移位将它乘以 256,再除以从 DS:53AF 读取的数量。重建源码把这个字段命名为 bullet_template.count。完整指令序列见下文。

  3. 找到模式转向玩家的位置

    相同的函数结果 · 目标摘录
    0x2d190: SUB AX, word ptr [0x53a6]
    0x2d198: SUB AX, word ptr [0x53a4]
    0x2d19d: CALLF 0x1000:1da8
    0x2d1a2: ADD word ptr [BP + -0x2], AX
    对应维护的C++
    group_angle_offset += iatan2(
      player_pos.cur.y - bullet_template.origin.y,
      player_pos.cur.x - bullet_template.origin.x
    );

    指令操作数显示了两个坐标差,以及一个计算方向的辅助函数。它的返回值加到相对环形角度上。源码名称让这些地址的含义更易读;REA 则帮助助手核对地址背后的原始操作。

从汇编到可读C++

点击每一步,对照原始指令和角度计算。这份摘要展示自机狙环形弹幕的路径;完整源码还处理散射、叠层和随机模式。

REA · 原始 16 位指令
0x2cfcf: MOV SI, word ptr [BP + 0x4]
0x2d0eb: MOV AX, SI
0x2d0ed: SHL AX, 0x8
0x2d0f0: MOV DL, byte ptr [0x53af]
0x2d0f4: MOV DH, 0x0
0x2d0f6: PUSH DX
0x2d0f7: CWD
0x2d0f8: POP BX
0x2d0f9: IDIV BX
0x2d0fb: MOV word ptr [BP + -0x2], AX
0x2d18d: MOV AX, [0x4650]
0x2d190: SUB AX, word ptr [0x53a6]
0x2d194: PUSH AX
0x2d195: MOV AX, [0x464e]
0x2d198: SUB AX, word ptr [0x53a4]
0x2d19c: PUSH AX
0x2d19d: CALLF 0x1000:1da8
0x2d1a2: ADD word ptr [BP + -0x2], AX
0x2d1b9: MOV AL, byte ptr [BP + -0x2]
0x2d1bc: ADD AL, byte ptr [0x53ad]
0x2d1c0: MOV [0xbcc8], AL
可读 C++ · 自机狙环形弹幕摘要
unsigned char aimed_ring_angle(
    int index, int count,
    int player_direction,
    unsigned char rotation)
{
    int angle = index;
    angle = (angle * 256) / count;
    angle += player_direction;
    angle += rotation;
    return (unsigned char)angle;
}

01 · 读取子弹索引。 栈参数加载到 SI。在 16 颗子弹的环中,索引从 0 到 15。

汇编节选展示自机狙环形分支、瞄准计算和最终存储。上面的 C++ 是便于阅读的摘要。下方的编译器检查针对链接项目中维护的源码。

阅读维护的环和瞄准源
环宏 · TH04分支,摘录
angle_offset = (i * BULLET_ANGLE_FULL_TURN) / count;
if (i >= (count - 1)) {
    group_complete = true;
}
goto aim_or_no_aim;
角度和速度 · 源摘录
aim:
    group_angle_offset += iatan2(
        (player_pos.cur.y - bullet_template.origin.y),
        (player_pos.cur.x - bullet_template.origin.x)
    );

no_aim:
    vector2_near(
        bullet_template.velocity,
        (group_angle_offset + bullet_template.angle),
        speed
    );
    bullet_spawn_angle = (group_angle_offset + bullet_template.angle);

环宏 · 目标和速度源 · 角度表示

检查维护的源

TH04 项目记录的检查使用指定的 Turbo C++ 4.0J 工具链,编译包含这个辅助函数的弹幕生成代码。

2,139匹配字节

编译的子弹生成代码与原始可执行文件的2,139字节部分相匹配。

两个干净的构建

两个单独的构建产生相同的匹配字节。 记录的输入哈希值与此处显示的维护源匹配。

2026年10月3日录得。 阅读编译器检查.

目标身份和分析范围
原始目标 MAIN.EXE,156,258字节,DOS MZ
佣工入境 0x2cfc8 在REA的加载图像
原始文件偏移量 0x1e7c8,返回 address_to_file_offset
记录代码检查 REA4.1.0*2026年10月7日

编译器检查覆盖完整的弹幕添加代码段,包括这个辅助函数。检查还比较它在 map 文件中的贡献,以及重叠的重定位项。

REA 验证了加载段为 0x1000 的静态 DOS 加载映像。地址描述的是分析映像。函数结果在一个 517 字节范围内报告了两段代码,共 503 字节;上方节选的指令都位于这些已报告的代码范围内。

源码名称和编译器检查来自 TH04 检查点 0d72e980。较早的重建分析使用项目自身的脚本;这个页面新增了用 REA 对同一原始目标的检查。游戏文件的身份由仓库清单确定,独立原始转储的来源仍未确定。

图中说明的是角度计算。完整游戏的控制、时序和 PC-98 硬件行为,在重建项目中有各自的运行时检查。

完整的TH04重建

项目用维护中的 C++ 和汇编构建游戏的四个 DOS 程序。除了弹幕生成,源码还覆盖关卡、Boss、渲染、输入、菜单、结局和分数处理。

探索 TH04 代码仓库

顶部