---
title: 'TH04 Reverse Engineering: modelli di proiettile DOS-REA'
url: https://rea.tools/it/showcase/th04/
---

[Casa](https://rea.tools/it/) / [Vetrina](https://rea.tools/it/showcase/) / TH04

Caso di studio · Touhou 4: Lotus Land Story

# Ricostruzione di TH04 modelli di proiettile.

Un anello di proiettili inizia con un calcolo dell'angolo corto. Utilizzare REA per ispezionare la funzione DOS originale, quindi seguire le sue istruzioni in C++leggibile.

PC-98 \* DOS a 16 bit \* C++ e assemblaggio

[Visualizza la ricostruzione TH04](https://github.com/N0zoM1z0/th04)

Dalle istruzioni originali alla fonte controllata

1. 01 \* Gioco originale

   **PRINCIPALE.EXE**

   L'eseguibile PC-98 e il suo helper bullet-angle.
2. 02 \* Ispezionare con REA

   **Istruzioni + riferimenti**

   Verificare il codice DOS caricato e leggere il calcolo dell'angolo dall'eseguibile.
3. 03 \* Leggere il C++

   **Spazio → obiettivo → ruota**

   Collegare ogni operazione alla ricostruzione mantenuta.
4. 04 \* Controllare la fonte

   **2,139 byte corrispondenti**

   I controlli del compilatore registrati coprono il codice di generazione bullet che contiene questo helper.

REA restituisce il codice originale e le sue prove di analisi. Il progetto TH04 fornisce i controlli di origine e compilatore mantenuti.

## Come un anello mira al giocatore

L'attacco di Elly richiede un anello mirato con un conteggio iniziale di 16. Un aiutante calcola la direzione di ogni proiettile. Lo stesso anello può affrontare una direzione fissa o girare verso il giocatore.

Il tuo agente di codifica

Usa REA per scoprire come TH04 crea un anello di proiettili e lo mira al giocatore. Spiega il calcolo e mostra il codice.

Un prompt di esempio, con l'eseguibile originale e il checkout di ricostruzione disponibili localmente.

TH04 memorizza una rotazione completa in senso orario in 256 unità angolari. Per 16 punti, ogni indice aggiunge 16 unità: `0, 16, 32, …, 240`. Aggiungendo la direzione del giocatore ruota insieme quelle posizioni.

![Due diagrammi di un anello a 16 proiettili. L'anello fisso inizia dall'angolo zero. L'aggiunta di una direzione del giocatore di 40 ruota ogni direzione dello stesso importo; il primo punto evidenziato punta verso il giocatore. Entrambi mantengono una spaziatura di 16 unità angolari o 22,5 gradi.](https://rea.tools/assets/localized/it/figures/th04-bullet-ring.svg)

Un'illustrazione della matematica dell'angolo recuperato per il conteggio 16, la direzione del giocatore 40 e la rotazione del modello 0. [Apri figura](https://rea.tools/assets/localized/it/figures/th04-bullet-ring.svg)

### Il modello di cottura nella fonte recuperata

Elly's ring attack \* fonte estratto

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

Queste linee selezionano il modello prima della difficoltà e della messa a punto delle prestazioni. [Leggi la fonte dell'attacco](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/boss/elly_phase_ring16.cpp#L26).

## Cosa restituisce REA

Controlliamo `bullet_velocity_and_angle_set()`, la funzione che imposta la velocità e l'angolo di ogni proiettile. REA restituisce le sue istruzioni, pseudocodice e riferimenti. Tre parti di questo risultato spiegano il modello.

1. #### Controllare l'immagine DOS prima di leggere il suo codice

   Agente → REA

   ```
   open_binary
   {"path": "MAIN.EXE"}

   inspect_native_load_image
   {}
   ```

   REA → agente \* campi selezionati

   ```
   status: "verified"
   format: "dos-mz"
   load_segment: 4096
   header_bytes: 6144
   module_bytes: 150114
   ```

   Il controllo lega il codice caricato e i record di rilocazione all'eseguibile fornito. Il percorso sopra è abbreviato per la visualizzazione.
2. #### Leggi l'anello moltiplica e dividi

   Agente → REA · `analyze_function`

   ```
   {"procedure": "0x2cfc8"}
   ```

   REA → agente \* estratto di istruzioni

   ```
   0x2d0eb: MOV AX, SI
   0x2d0ed: SHL AX, 0x8
   0x2d0f0: MOV DL, byte ptr [0x53af]
   0x2d0f9: IDIV BX
   ```

   `SI` contiene l'indice proiettile. Lo spostamento lo moltiplica per 256, quindi la divisione utilizza il conteggio letto da `DS:53AF`. I nomi di origine mantenuti quel campo `bullet_template.count`. La sequenza completa appare sotto.
3. #### Trova dove il modello gira verso il giocatore

   Stesso risultato di funzione \* estratto aim

   ```
   0x2d190: SUB AX, word ptr [0x53a6]
   0x2d198: SUB AX, word ptr [0x53a4]
   0x2d19d: CALLF 0x1000:1da8
   0x2d1a2: ADD word ptr [BP + -0x2], AX
   ```

   Corrispondente mantenuto C++

   ```
   group_angle_offset += iatan2(
     player_pos.cur.y - bullet_template.origin.y,
     player_pos.cur.x - bullet_template.origin.x
   );
   ```

   Gli operandi di istruzione espongono due differenze di coordinate e un assistente di direzione. Il suo valore di ritorno viene aggiunto all'angolo dell'anello relativo. I nomi di origine rendono leggibili quegli indirizzi; REA consente all'agente di controllare le operazioni originali dietro di loro.

## Dall'assembly al C++leggibile

Selezionare un passaggio che corrisponda alle istruzioni originali con il calcolo dell'angolo. Questo riepilogo segue il percorso dell'anello mirato; la fonte completa gestisce anche spread, stack e modelli casuali.

REA \* istruzioni originali a 16 bit

```
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++ leggibile \* riepilogo dell'anello mirato

```
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 \* Leggere l'indice proiettile.** L'argomento stack viene caricato in `SI`. In un anello a 16 proiettili, questo indice membro va da 0 a 15.

**02 \* Spazio le direzioni intorno a un giro completo.** `SHL AX,8` moltiplicato per 256. Il conteggio è zero-esteso in una parola, quindi `IDIV` divide il prodotto per quel conteggio. Per il conteggio 16, ogni indice avanza di 16 unità angolari.

**03 \* Aggiungi la direzione del giocatore.** Le due sottrazioni formano le coordinate player-minus-origin. L'assistente di direzione restituisce un angolo in `AX`; aggiungendo si gira l'intero anello verso il giocatore.

**04 \* Aggiungere la rotazione del modello.** `ADD AL,[0x53ad]` aggiunge l'angolo del modello. Il modello iniziale di Elly lo imposta a zero; altri modelli possono utilizzare lo stesso helper con una rotazione.

**05 \* Mantenere l'angolo finale in un byte.** Il negozio scrive `AL` al campo angolo di spawn. I valori si chiudono dopo 255, proprio come fa il cast di byte senza segno. La sorgente circostante converte anche questo angolo e velocità in velocità.

L'assemblaggio seleziona il ramo dell'anello di mira, il calcolo dell'obiettivo e l'archivio finale. Il C++ sopra è un riassunto leggibile. I controlli del compilatore riportati di seguito si applicano all'origine mantenuta nel progetto collegato.

Leggi l'anello mantenuto e la fonte aim

Macro anello \* Ramo TH04, estratto

```
angle_offset = (i * BULLET_ANGLE_FULL_TURN) / count;
if (i >= (count - 1)) {
    group_complete = true;
}
goto aim_or_no_aim;
```

Angolo e velocità \* fonte estratto

```
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);
```

[Anello macro](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add_impl.hpp#L4) · [Sorgente di mira e velocità](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add.cpp#L422) · [Rappresentazione angolare](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/types.hpp#L6)

## Verifica della fonte mantenuta

I controlli registrati del progetto TH04 compilano il codice di generazione bullet, incluso questo helper, con la sua toolchain Turbo C++ 4.0 J bloccata.

### 2,139 byte corrispondenti

Il codice di generazione bullet compilato corrisponde a una sezione di 2.139 byte dell'eseguibile originale.

### Due build pulite

Due build separate producono gli stessi byte corrispondenti. Gli hash di input registrati corrispondono alla sorgente mantenuta mostrata qui.

Registrato il 3 ottobre 2026. [Leggi i controlli del compilatore](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/reconstruction/product/TH04_SEMANTIC_BULLET_GENERATION_V1236.md#validation).

Identità target e ambito di analisi

|  |  |
| --- | --- |
| Obiettivo originale | `MAIN.EXE`, 156,258 byte, DOS MZ |
| Helper entry | `0x2cfc8` nell ' immagine caricata di REA |
| Offset file originale | `0x1e7c8`, restituito da `address_to_file_offset` |
| Ispezione del codice registrato | REA 4.1.0 \* 7 Ottobre 2026 |

I controlli del compilatore coprono la sezione completa del codice bullet-add, incluso questo helper. Confrontano anche il suo contributo alla MAPPA e il trasferimento sovrapposto.

REA verificato l'immagine di carico statico DOS al segmento di carico `0x1000`. I suoi indirizzi descrivono quell'immagine di analisi. Il risultato della funzione riporta 503 byte del corpo in due intervalli su un intervallo di 517 byte; le istruzioni selezionate sopra si trovano in quegli intervalli riportati.

I nomi dei sorgenti e i controlli del compilatore provengono dal checkpoint TH04 `0d72e980`. Le precedenti osservazioni di ricostruzione utilizzavano gli script di analisi del progetto; questa pagina aggiunge una nuova ispezione REA dello stesso obiettivo originale. L'identità del gioco in dotazione è bloccata dal suo repository manifest, con la provenienza indipendente di pristine-dump ancora aperta.

La figura illustra il calcolo dell'angolo. I controlli di gioco completi, i tempi e il comportamento hardware PC-98 hanno controlli di runtime separati nel progetto di ricostruzione.

## La ricostruzione completa TH04

Il progetto costruisce quattro prodotti DOS del gioco da mantenuto C++ e assembly. Oltre alla generazione di proiettili, la fonte copre fasi, boss, rendering, input, menu, finali e gestione dei punteggi.

[Esplora il repository TH04](https://github.com/N0zoM1z0/th04)

- [Fonte di generazione di proiettili](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add.cpp#L292)L'helper completo, altri tipi di pattern e template tuning.
- [DOS costruire ed eseguire guida](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/DOS_BUILD.md)Crea il sorgente e testalo nel flusso di lavoro dell'emulatore PC-98 del progetto.
- [Stato attuale del progetto](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/RE_HANDOFF.md)Verifica DOS e la porta x64 nativa separata.
