---
title: 'Ingénierie inverse TH04: Modèles de puces DOS-REA'
url: https://rea.tools/fr/showcase/th04/
---

[Accueil](https://rea.tools/fr/) / [Vitrines](https://rea.tools/fr/showcase/) / TH04

Étude de cas · Touhou 4: L'histoire de Lotus Land

# Reconstruction TH04's motifs de balles.

Un anneau de balles commence par un court calcul d'angle. Utilisez REA pour inspecter la fonction DOS d'origine, puis suivez ses instructions en C ++ lisible.

PC-98 \* DOS 16 bits \* C++ et assemblage

[Voir le TH04 reconstruction](https://github.com/N0zoM1z0/th04)

Des instructions originales à la source vérifiée

1. 01 \* Jeu original

   **PRINCIPALE.EXE**

   L'exécutable PC-98 et son assistant d'angle de balle.
2. 02 \* Inspecter avec le modèle REA

   **Instructions + références**

   Vérifiez le code DOS chargé et lisez le calcul de l'angle à partir de l'exécutable.
3. 03 \* Lire le C++

   **Espace → viser → tourner**

   Reliez chaque opération à la reconstruction maintenue.
4. 04 \* Vérifiez la source

   **2 139 octets correspondants**

   Les vérifications du compilateur enregistrées couvrent le code de génération de puces qui contient cette aide.

REA renvoie le code d'origine et ses preuves d'analyse. Le projet TH04 fournit les vérifications de source et de compilateur maintenues.

## Comment un anneau vise le joueur

L'attaque d'Elly demande un anneau visé avec un compte initial de 16. Un assistant calcule la direction de chaque balle. Le même anneau peut faire face à une direction fixe ou se tourner vers le joueur.

Votre agent de codage

Utilisez REA pour découvrir comment TH04 crée un anneau de balles et le dirige vers le joueur. Expliquez le calcul et montrez le code.

Un exemple d'invite, avec l'exécutable d'origine et la récupération de reconstruction disponibles localement.

TH04 stocke un tour complet dans le sens des aiguilles d'une montre en 256 unités d'angle. Pour 16 puces, chaque index ajoute 16 unités: `0, 16, 32, …, 240`. L'ajout de la direction du joueur fait pivoter ces positions ensemble.

![Deux diagrammes d'un anneau de 16 balles. L'anneau fixe commence à l'angle zéro. L'ajout d'une direction de joueur de 40 fait pivoter toutes les directions de la même quantité; la première puce en surbrillance pointe vers le joueur. Les deux conservent un espacement de 16 unités d'angle, soit 22,5 degrés.](https://rea.tools/assets/localized/fr/figures/th04-bullet-ring.svg)

Une illustration des calculs d'angle récupérés pour le compte 16, la direction du joueur 40 et la rotation du gabarit 0. [Figure ouverte](https://rea.tools/assets/localized/fr/figures/th04-bullet-ring.svg)

### Le modèle de tir dans la source récupérée

L'attaque de l'anneau d'Elly \* extrait de la source

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

Ces lignes sélectionnent le motif avant le réglage de la difficulté et des performances. [Lire la source de l'attaque](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/boss/elly_phase_ring16.cpp#L26).

## Quels sont les retours de REA

Nous inspectons `bullet_velocity_and_angle_set()`, la fonction qui définit la vitesse et l'angle de chaque balle. REA renvoie ses instructions, son pseudocode et ses références. Trois parties de ce résultat expliquent le schéma.

1. #### Vérifiez l'image DOS avant de lire son code

   Agent → REA

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

   inspect_native_load_image
   {}
   ```

   REA → agent \* champs sélectionnés

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

   La vérification lie le code chargé et les enregistrements de relocalisation à l'exécutable fourni. Le chemin ci-dessus est raccourci pour l'affichage.
2. #### Lire la multiplication et la division de l'anneau

   Agent → REA · `analyze_function`

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

   REA → extrait d'instructions de l'agent ·

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

   `SI` tient l'index de la balle. Le décalage le multiplie par 256, puis la division utilise le décompte lu à partir de `DS:53AF`. Les noms de source conservés dans ce champ `bullet_template.count`. La séquence complète apparaît ci-dessous.
3. #### Trouvez où le motif se tourne vers le joueur

   Même fonction résultat · extrait de but

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

   C++maintenu correspondant

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

   Les opérandes d'instruction exposent deux différences de coordonnées et un assistant de direction. Sa valeur de retour est ajoutée à l'angle relatif de l'anneau. Les noms de source rendent ces adresses lisibles; REA permet à l'agent de vérifier les opérations d'origine derrière elles.

## De l'assemblage au C++lisible

Sélectionnez une étape pour faire correspondre les instructions d'origine avec le calcul de l'angle. Ce résumé suit le chemin de l'anneau visé; la source complète gère également les spreads, les piles et les motifs aléatoires.

REA \* instructions 16 bits d'origine

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

Résumé lisible de l'anneau visé 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 \* Lire l'index des puces.** L'argument de pile est chargé dans `SI`. Dans un anneau à 16 puces, cet indice de membre va de 0 à 15.

**02 \* Espacez les directions autour d'un tour complet.** `SHL AX,8` multiplie par 256. Le nombre est égal à zéro-étendu en un mot, puis `IDIV` divise le produit par ce nombre. Pour le compte 16, chaque index avance de 16 unités d'angle.

**03 \* Ajoutez la direction du joueur.** Les deux soustractions forment les coordonnées d'origine du joueur-moins. L'assistant de direction renvoie un angle dans `AX`; en l'ajoutant, il tourne tout l'anneau vers le joueur.

**04 \* Ajouter la rotation du motif.** `ADD AL,[0x53ad]` ajoute l'angle du modèle. Le modèle initial d'Elly le met à zéro; d'autres modèles peuvent utiliser le même assistant avec une rotation.

**05 \* Gardez l'angle final dans un octet.** Le magasin écrit `AL` au champ d'angle d'apparition. Les valeurs s'enroulent après 255, tout comme le cast d'octets non signés. La source environnante convertit également cet angle et cette vitesse en vitesse.

L'ensemble sélectionne la branche de la bague visée, le calcul de la visée et la mémorisation finale. Le C++ ci-dessus est un résumé lisible. Les vérifications du compilateur ci-dessous s'appliquent à la source maintenue dans le projet lié.

Lire l'anneau maintenu et la source de visée

Macro annulaire \* Branche TH04, extrait

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

Angle et vélocité · extrait de la source

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

[Macro d'anneau](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add_impl.hpp#L4) · [Source de visée et de vélocité](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add.cpp#L422) · [Représentation angulaire](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/types.hpp#L6)

## Vérification de la source maintenue

Les vérifications enregistrées du projet TH04 compilent le code de génération de puces, y compris cet assistant, avec sa chaîne d'outils Turbo C++ 4.0 J épinglée.

### 2 139 octets correspondants

Le code de génération de puces compilé correspond à une section de 2 139 octets de l'exécutable d'origine.

### Deux constructions propres

Deux builds distincts produisent les mêmes octets correspondants. Les hachages d'entrée enregistrés correspondent à la source maintenue illustrée ici.

Enregistré le 3 octobre 2026. [Lire les vérifications du compilateur](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/reconstruction/product/TH04_SEMANTIC_BULLET_GENERATION_V1236.md#validation).

Identité cible et portée de l'analyse

|  |  |
| --- | --- |
| Cible d'origine | `MAIN.EXE`, 156 258 octets, sous-titres MZ |
| Entrée d'aide | `0x2cfc8` dans l'image chargée de REA |
| Décalage du fichier d'origine | `0x1e7c8`, retourné par `address_to_file_offset` |
| Inspection du code enregistré | REA 4.1.0 · 7 octobre 2026 |

Les vérifications du compilateur couvrent la section complète d'ajout de puces de code, y compris cette aide. Ils comparent également sa contribution à la CARTE et sa relocalisation qui se chevauchent.

REA a vérifié l'image de charge DOS statique au segment de charge `0x1000`. Ses adresses décrivent cette image d'analyse. Le résultat de la fonction rapporte 503 octets de corps dans deux plages sur une étendue de 517 octets; les instructions sélectionnées ci-dessus se trouvent dans ces plages rapportées.

Les noms des sources et les vérifications du compilateur proviennent du point de contrôle TH04 `0d72e980`. Les observations de reconstruction antérieures utilisaient les propres scripts d'analyse du projet; cette page ajoute une nouvelle inspection REA de la même cible d'origine. L'identité du jeu fourni est épinglée par son manifeste de référentiel, avec une provenance de vidage vierge indépendante toujours ouverte.

La figure illustre le calcul de l'angle. Les contrôles complets du jeu, le timing et le comportement du matériel PC-98 ont des vérifications d'exécution distinctes dans le projet de reconstruction.

## La reconstruction complète du TH04

Le projet construit les quatre produits DOS du jeu à partir de C++ et d'assembly maintenus. Au-delà de la génération de puces, la source couvre les étapes, les boss, le rendu, la saisie, les menus, les fins et la gestion des partitions.

[Explorez le référentiel TH04](https://github.com/N0zoM1z0/th04)

- [Source de génération de balles](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add.cpp#L292)L'assistant complet, d'autres types de motifs et le réglage des modèles.
- [Guide de construction et d'exécution DOS](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/DOS_BUILD.md)Créez la source et testez-la dans le flux de travail de l'émulateur PC-98 du projet.
- [État actuel du projet](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/RE_HANDOFF.md)Vérification DOS et port x64 natif séparé.
