---
title: 'TH04 Ingeniería inversa: Patrones de viñetas DOS-REA'
url: https://rea.tools/es/showcase/th04/
---

[Inicio](https://rea.tools/es/) / [Vitrinas](https://rea.tools/es/showcase/) / TH04

Estudio de caso \* Touhou 4: Historia de Lotus Land

# Reconstruyendo los TH04 patrones de bala.

Un anillo de balas comienza con un cálculo de ángulo corto. Use REA para inspeccionar la función DOS original, luego siga sus instrucciones en C ++ legible.

PC-98 · DOS de 16 bits \* C++ y ensamblado

[Ver la reconstrucción del TH04](https://github.com/N0zoM1z0/th04)

De las instrucciones originales a la fuente verificada

1. 01 \* Juego original

   **PRINCIPAL.EXE**

   El ejecutable PC-98 y su ayudante de ángulo de bala.
2. 02 \* Inspeccionar con REA

   **Instrucciones + referencias**

   Verifique el código DOS cargado y lea el cálculo del ángulo del ejecutable.
3. 03 \* Leer el C++

   **Espacio → apuntar → rotar**

   Conecte cada operación a la reconstrucción mantenida.
4. 04 \* Verifique la fuente

   **2.139 bytes coincidentes**

   Las comprobaciones grabadas del compilador cubren el código de generación de viñetas que contiene este ayudante.

REA devuelve el código original y su evidencia de análisis. El proyecto TH04 proporciona las comprobaciones de código fuente y compilador mantenidas.

## Cómo apunta un anillo al jugador

El ataque de Elly solicita un anillo apuntado con un conteo inicial de 16. Un ayudante calcula la dirección de cada bala. El mismo anillo puede mirar en una dirección fija o girar hacia el jugador.

Su agente de codificación

Usa REA para descubrir cómo TH04 crea un anillo de balas y lo apunta al jugador. Explique el cálculo y muestre el código.

Un indicador de ejemplo, con el ejecutable original y el pago de reconstrucción disponibles localmente.

TH04 almacena un giro completo en el sentido de las agujas del reloj en 256 unidades de ángulo. Para 16 viñetas, cada índice agrega 16 unidades: `0, 16, 32, …, 240`. Al agregar la dirección del jugador, se rotan esas posiciones juntas.

![Dos diagramas de un anillo de 16 balas. El anillo fijo comienza en ángulo cero. Agregar una dirección del jugador de 40 gira cada dirección en la misma cantidad; la primera viñeta resaltada apunta hacia el jugador. Ambos mantienen un espaciado de 16 unidades de ángulo, o 22.5 grados.](https://rea.tools/assets/localized/es/figures/th04-bullet-ring.svg)

Una ilustración de la matemática del ángulo recuperado para el conteo 16, la dirección del jugador 40 y la rotación de la plantilla 0. [Figura abierta](https://rea.tools/assets/localized/es/figures/th04-bullet-ring.svg)

### La plantilla de cocción en la fuente recuperada

Ataque anular de Elly \* extracto de la fuente

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

Estas líneas seleccionan el patrón antes de la dificultad y el ajuste del rendimiento. [Lea la fuente del ataque](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/boss/elly_phase_ring16.cpp#L26).

## Qué devuelve REA

Inspeccionamos `bullet_velocity_and_angle_set()`, la función que establece la velocidad y el ángulo de cada bala. REA devuelve sus instrucciones, pseudocódigo y referencias. Tres partes de ese resultado explican el patrón.

1. #### Verifique la imagen de DOS antes de leer su código

   Agente → REA

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

   inspect_native_load_image
   {}
   ```

   REA → agente \* campos seleccionados

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

   La comprobación vincula el código cargado y los registros de reubicación al ejecutable suministrado. La ruta anterior se acorta para su visualización.
2. #### Lee el anillo multiplica y divide

   Agente → REA · `analyze_function`

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

   REA → extracto de instrucciones del agente ·

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

   `SI` sostiene el índice de bala. El desplazamiento lo multiplica por 256, luego la división usa el recuento leído de `DS:53AF`. La fuente mantenida nombra ese campo `bullet_template.count`. La secuencia completa aparece a continuación.
3. #### Encuentra dónde se dirige el patrón hacia el jugador

   Resultado de la misma función · extracto del objetivo

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

   Correspondiente C ++ mantenido

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

   Los operandos de instrucción exponen dos diferencias de coordenadas y un ayudante de dirección. Su valor de retorno se suma al ángulo relativo del anillo. Los nombres de origen hacen que esas direcciones sean legibles; REA permite al agente verificar las operaciones originales detrás de ellas.

## Del ensamblado al C ++ legible

Seleccione un paso para que coincida con las instrucciones originales con el cálculo del ángulo. Este resumen sigue la ruta del anillo objetivo; la fuente completa también maneja spreads, pilas y patrones aleatorios.

REA \* instrucciones originales de 16 bits

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

Resumen de anillo dirigido legible por 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 \* Lea el índice de viñetas.** El argumento de la pila se carga en `SI`. En un anillo de 16 balas, este índice de miembros va de 0 a 15.

**02 \* Espacie las direcciones alrededor de una vuelta completa ·**  `SHL AX,8` multiplica por 256. El conteo es cero, extendido a una palabra, luego `IDIV` divide el producto por ese conteo. Para el conteo 16, cada índice avanza 16 unidades de ángulo.

**03 \* Agregue la dirección del jugador.** Las dos restas forman coordenadas jugador-menos-origen. El ayudante de dirección devuelve un ángulo en `AX`; agregarlo gira todo el anillo hacia el jugador.

**04 \* Añadir la rotación del patrón.** `ADD AL,[0x53ad]` agrega el ángulo de la plantilla. La plantilla inicial de Elly la establece en cero; otros patrones pueden usar el mismo ayudante con una rotación.

**05 \* Mantenga el ángulo final en un byte.** La tienda escribe `AL` al campo de ángulo de generación. Los valores se ajustan después de 255, tal como lo hace la conversión de bytes sin signo. La fuente circundante también convierte este ángulo y velocidad en velocidad.

El conjunto selecciona la rama del anillo objetivo, el cálculo del objetivo y el almacén final. El C++ anterior es un resumen legible. Las comprobaciones del compilador a continuación se aplican a la fuente mantenida en el proyecto vinculado.

Lea el anillo mantenido y apunte la fuente

Macro de anillo \* Rama TH04, extracto

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

Ángulo y velocidad · extracto de la fuente

```
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 de anillo](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add_impl.hpp#L4) · [Objetivo y fuente de velocidad](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add.cpp#L422) · [Representación de ángulos](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/types.hpp#L6)

## Comprobación de la fuente mantenida

Las comprobaciones grabadas del proyecto TH04 compilan el código de generación de viñetas, incluido este ayudante, con su cadena de herramientas Turbo C++ 4.0 J anclada.

### 2.139 bytes coincidentes

El código compilado de generación de viñetas coincide con una sección de 2139 bytes del ejecutable original.

### Dos construcciones limpias

Dos compilaciones separadas producen los mismos bytes coincidentes. Los hashes de entrada grabados coinciden con la fuente mantenida que se muestra aquí.

Grabado el 3 de octubre de 2026. [Leer las comprobaciones del compilador](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/reconstruction/product/TH04_SEMANTIC_BULLET_GENERATION_V1236.md#validation).

Identidad del objetivo y alcance del análisis

|  |  |
| --- | --- |
| Objetivo original | `MAIN.EXE`, 156,258 bytes, DOS MZ |
| Entrada auxiliar | `0x2cfc8` en la imagen cargada de REA |
| Desplazamiento del archivo original | `0x1e7c8`, devuelto por `address_to_file_offset` |
| Inspección de código grabado | REA 4.1.0 \* 7 de octubre de 2026 |

Las comprobaciones del compilador cubren la sección completa de código de adición de viñetas, incluido este ayudante. También comparan su contribución al MAPA y la reubicación superpuesta.

REA verificó la imagen de carga estática de DOS en el segmento de carga `0x1000`. Sus direcciones describen esa imagen de análisis. El resultado de la función informa 503 bytes de cuerpo en dos rangos en un intervalo de 517 bytes; las instrucciones seleccionadas anteriormente se encuentran en esos rangos informados.

Los nombres de origen y las comprobaciones del compilador provienen del punto de control TH04 `0d72e980`. Las observaciones de reconstrucción anteriores utilizaron los propios scripts de análisis del proyecto; esta página agrega una nueva inspección REA del mismo objetivo original. La identidad del juego suministrado está anclada por su manifiesto de repositorio, con la procedencia independiente de volcado prístino aún abierta.

La figura ilustra el cálculo del ángulo. Los controles de juego completo, el tiempo y el comportamiento del hardware PC-98 tienen comprobaciones de tiempo de ejecución separadas en el proyecto de reconstrucción.

## La reconstrucción completa del TH04

El proyecto construye los cuatro productos DOS del juego a partir de C++ y ensamblado mantenidos. Más allá de la generación de viñetas, la fuente cubre etapas, jefes, renderizado, entrada, menús, finales y manejo de partituras.

[Explore el repositorio TH04](https://github.com/N0zoM1z0/th04)

- [Fuente de generación de balas](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/src/main/bullet/add.cpp#L292)El ayudante completo, otros tipos de patrones y ajuste de plantillas.
- [Guía de compilación y ejecución de DOS](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/DOS_BUILD.md)Compile el código fuente y pruébelo en el flujo de trabajo del emulador PC-98 del proyecto.
- [Estado actual del proyecto](https://github.com/N0zoM1z0/th04/blob/0d72e9801a5c14c10384efcbb6734ceccb74ee63/docs/RE_HANDOFF.md)Verificación DOS y el puerto x64 nativo separado.
