---
title: TH04逆向工程：DOS子彈模式-REA
url: https://rea.tools/zh-TW/showcase/th04/
---

[首頁](https://rea.tools/zh-TW/) / [案例展示](https://rea.tools/zh-TW/showcase/) / TH04

個案研究·東方4：蓮花地故事

# 重建TH04 子彈圖案。

一圈彈幕從一段簡短的角度計算開始。用 REA 檢查原始 DOS 函式，再把指令對應到可讀的 C++ 程式碼。

PC-98·16位DOS · C++和彙編

[檢視TH04重建](https://github.com/N0zoM1z0/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 度。](https://rea.tools/assets/localized/zh-TW/figures/th04-bullet-ring.svg)

圖中演示還原出的角度計算：子彈數量為 16，玩家方向為 40，模板旋轉角為 0。[檢視大圖](https://rea.tools/assets/localized/zh-TW/figures/th04-bullet-ring.svg)

### 還原原始碼中的發射模板

Elly 的環形攻擊 · 原始碼節選

```
bullet_template.angle = 0;
bullet_template.group = BG_RING_AIMED;
bullet_template.count = 16;
bullet_template_tune();
```

這些線在難度和效能調整之前選擇模式。 [閱讀攻擊的來源](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/boss/elly_phase_ring16.cpp#L26).

## 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。

**02 · 在整圈中等分方向。** `SHL AX,8` 將索引乘以 256。數量先零擴充套件為一個字，再由 `IDIV` 用乘積除以數量。數量為 16 時，每個索引前進 16 個角度單位。

**03 · 加上玩家方向。** 兩次減法得到玩家座標與原點座標的差。方向輔助函式在 `AX` 中返回角度；加上這個值，就能把整圈彈幕轉向玩家。

**04 · 加上模板旋轉角。** `ADD AL,[0x53ad]` 加上模板中的角度。Elly 的初始模板將它設為零；其他彈幕可以用同一個輔助函式進行旋轉。

**05 · 用一個位元組儲存最終角度。** 儲存指令把 `AL` 寫入子彈生成角度欄位。值超過 255 後迴繞，與無符號位元組轉換一致。周圍原始碼還會根據這個角度和速率計算速度分量。

彙編節選展示自機狙環形分支、瞄準計算和最終儲存。上面的 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);
```

[環宏](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add_impl.hpp#L4) · [目標和速度源](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add.cpp#L422) · [角度表示](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/types.hpp#L6)

## 檢查維護的源

TH04 專案記錄的檢查使用指定的 Turbo C++ 4.0J 工具鏈，編譯包含這個輔助函式的彈幕生成程式碼。

### 2,139匹配位元組

編譯的子彈生成程式碼與原始可執行檔案的2,139位元組部分相匹配。

### 兩個乾淨的構建

兩個單獨的構建產生相同的匹配位元組。 記錄的輸入雜湊值與此處顯示的維護源匹配。

2026年10月3日錄得。 [閱讀編譯器檢查](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/reconstruction/product/TH04_SEMANTIC_BULLET_GENERATION_V1236.md#validation).

目標身份和分析範圍

|  |  |
| --- | --- |
| 原始目標 | `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 程式碼儲存庫](https://github.com/N0zoM1z0/th04)

- [子彈生成源](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add.cpp#L292)完整的助手，其他模式型別和模板調整。
- [DOS構建和執行指南](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/DOS_BUILD.md)構建原始碼並在專案的PC-98模擬器工作流中進行測試。
- [當前專案狀態](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/RE_HANDOFF.md)DOS驗證和單獨的原生x64移植版。
