Registrarse

[NDS] Ingeniería inversa de los recursos de Pokémon Dash (Nintendo DS) - PokemonDash2Unity - Importer para Unity Engine

Manurocker95

Doctorando en Ingeniería Biomédica & Game Dev
Miembro insignia
¡Buenas una vez más! Hoy vengo con otro pequeño proyecto de ingeniería inversa, esta vez centrado en Pokémon Dash, uno de los primeros títulos de Pokémon publicados para Nintendo DS y desarrollado por la mítica "Ambrella". En esta ocasión se ha investigado con la ROM europea (PAL) del juego, ya que me interesaba ver cómo almacena sus gráficos, modelos 3D, texturas y sonidos. El objetivo de esta investigación, una vez más, ha sido desarrollar un importer en Unity Engine que me permita extraer y, valga la redundancia, importar los recursos desde la ROM y ver cómo se organizan dentro del juego de Nintendo DS.

1. Estructura general

Como ocurre con otros juegos de Nintendo DS, Pokémon Dash utiliza NitroFS para organizar sus archivos. Este sistema dispone de una tabla de nombres y directorios bien documentados (FNT) y una tabla de asignación de archivos (FAT), que permiten localizar los recursos dentro de la ROM. En la versión europea, el identificador del juego es APDP, almacenado en el offset 0x0C de la cabecera.

Por tanto en la cabecera completa podemos encontrar:

Código:
Offset  Tamaño  Contenido

0x00    12      Título: POKEMON_DASH
0x0C 4 Game Code: APDP
0x1E 1 Revisión de la ROM
0x40 4 Offset de la FNT
0x44 4 Tamaño de la FNT
0x48 4 Offset de la FAT
0x4C 4 Tamaño de la FAT
0x15E 2 CRC16 de la cabecera
Cada entrada de la FAT ocupa 8 bytes, divididos en dos valores de 32 bits que indican el inicio y el final de un archivo dentro de la ROM. Esto es importante porque los recursos no tienen por qué encontrarse en posiciones fijas entre distintas versiones del juego: su ubicación se obtiene a través de estas tablas.

2. Modelos 3D y archivos RelocMod

Una de las partes más interesantes es la forma en la que Pokémon Dash almacena sus modelos 3D. En lugar de utilizar directamente archivos NSBMD convencionales, los modelos identificados hasta ahora se encuentran dentro de un archivo llamado archive.archive, que actúa como contenedor de múltiples recursos.

Este archivo comienza con la firma NITRO Archive 1 y dispone de una tabla que permite localizar cada uno de sus miembros.

Código:
Offset  Tamaño  Contenido

0x00    16      Firma: NITRO Archive 1
0x10 4 Número de entradas
0x20 12 × N Tabla de entradas
Cada entrada ocupa 12 bytes:

Código:
+0x00 u32 Offset del nombre
+0x04 u32 Offset de los datos
+0x08 u32 Tamaño del recurso
Dentro encontramos archivos .rel, entre ellos recursos cuyos nombres comienzan por mdl_, utilizados para almacenar modelos. Estos archivos contienen otra estructura identificada mediante la firma NITRO RelocMod 1.

¿Qué es un RelocMod?

Se trata de un módulo que agrupa datos binarios junto con una tabla de reubicaciones. En lugar de interpretar todos sus valores como direcciones absolutas, el juego dispone de una lista que identifica las posiciones donde se encuentran las referencias internas.

La cabecera contiene los siguientes campos relevantes:

Código:
Offset  Tamaño  Contenido

0x00    16      Firma: NITRO RelocMod 1
0x10 4 Offset del bloque de datos
0x14 4 Tamaño descomprimido
0x20 4 Offset de tabla de reubicación
0x24 4 Número de reubicaciones
0x30 4 Indicador de compresión
Cada entrada de la tabla de reubicaciones ocupa 4 bytes y señala una posición dentro del bloque de datos que contiene una referencia.

Algunas de estas estructuras pueden encontrarse comprimidas mediante LZ10, por lo que primero es necesario recuperar los datos originales.

Código:
archive.archive
├── Tabla de archivos
├── mdl_*.rel
│ └── NITRO RelocMod 1
│ ├── Cabecera
│ ├── Tabla de reubicaciones
│ └── Bloque de datos
│ ├── Geometría GX
│ ├── Texturas
│ ├── Paletas
│ └── Referencias internas
└── Otros recursos
Este esquema representa la organización lógica de los datos, no necesariamente su disposición física exacta dentro de cada módulo.

3. Geometría y comandos GX

Otra peculiaridad es que la geometría no se almacena simplemente como una lista convencional de vértices e índices. Los modelos contienen listas de comandos GX, que corresponden al sistema gráfico de Nintendo DS. Estas instrucciones describen operaciones como establecer coordenadas, normales, colores, coordenadas de textura y comenzar o finalizar primitivas geométricas.

Entre los comandos identificados encontramos:

Código:
Código  Comando       Función

0x20    COLOR         Color del vértice
0x21 NORMAL Normal del vértice
0x22 TEXCOORD Coordenadas UV
0x23 VTX_16 Posición del vértice
0x40 BEGIN_VTXS Inicio de primitiva
0x41 END_VTXS Fin de primitiva
En este sistema, los comandos se empaquetan en palabras de 32 bits, con hasta cuatro identificadores de instrucción por palabra, seguidos de sus respectivos parámetros.

Para reconstruir un modelo es necesario interpretar esta secuencia, recuperar los vértices y convertir las primitivas gráficas en triángulos. Además, un mismo módulo puede contener varias piezas geométricas relacionadas con diferentes texturas o materiales. Por tanto, no siempre existe una correspondencia directa entre un archivo .rel y una única malla.

4. Texturas y paletas de Nintendo DS

Las texturas de los modelos utilizan formatos compatibles con el hardware gráfico de Nintendo DS.

A diferencia de una imagen convencional de 24 o 32 bits por píxel, muchas de estas texturas están indexadas mediante paletas de colores. Esto significa que los datos gráficos almacenan índices y que los colores reales se obtienen consultando una paleta independiente.

Entre los formatos contemplados encontramos:

Código:
Código  Formato

1       A3I5
2 Paleta de 4 colores
3 Paleta de 16 colores
4 Paleta de 256 colores
5 Textura comprimida 4x4
6 A5I3
7 Color directo
Las paletas utilizan colores de 16 bits, con componentes RGB de 5 bits. También existen formatos que combinan índices de paleta con distintos niveles de transparencia. En los recursos vistos parseando la ROM, los descriptores de textura contienen información sobre el tamaño de los datos, sus dimensiones y el formato utilizado.

Código:
Offset  Tipo  Contenido

+0x00   u32   Referencia a píxeles
+0x04 u32 Tamaño de los datos
+0x08 u16 Ancho
+0x0A u16 Alto
+0x0C u32 Formato de textura
Algunas texturas también pueden almacenar sus píxeles mediante compresión LZ10.

Cabe señalar que el formato comprimido de texturas 4x4 todavía no está resuelto en el código analizado, por lo que no todos los recursos gráficos pueden considerarse completamente documentados.

5. Audio: Nitro Sound y archivos SDAT

En el apartado sonoro, Pokémon Dash utiliza Nitro Sound, el sistema de audio de Nintendo DS.

Los recursos están agrupados en archivos .sdat, identificados mediante la firma SDAT. Estos contenedores reúnen secuencias musicales, bancos de instrumentos, muestras de sonido y otros datos necesarios para la reproducción.

Código:
SDAT
├── SYMB Nombres de recursos
├── INFO Información y referencias
├── FAT Localización de archivos
└── FILE Datos de sonido
├── SSEQ Secuencias musicales
├── SSAR Archivos de secuencias
├── SBNK Bancos de instrumentos
└── SWAR Bancos de muestras
En la cabecera del SDAT encontramos, entre otros, estos offsets:

Código:
Offset  Tipo  Contenido

0x00    4     Firma SDAT
0x04 u16 BOM
0x08 u32 Tamaño total
0x10 u32 Offset de SYMB
0x18 u32 Offset de INFO
0x20 u32 Offset de FAT
0x24 u32 Tamaño de FAT
La tabla FAT del contenedor de sonido contiene registros de 16 bytes, de los cuales los primeros ocho indican el offset y el tamaño del recurso.

Música mediante secuencias

Un detalle importante es que la música no tiene por qué almacenarse como una grabación de audio completa. Los archivos SSEQ contienen instrucciones musicales que indican qué notas deben reproducirse, con qué instrumentos y bajo qué condiciones. Los instrumentos se definen en bancos SBNK, mientras que sus muestras de sonido se encuentran en archivos SWAR. De esta manera, una misma muestra puede reutilizarse en diferentes notas, instrumentos o composiciones, reduciendo considerablemente el espacio necesario en la ROM.
Los archivos SSAR permiten agrupar varias secuencias dentro de un mismo recurso. En los nombres identificados aparecen categorías relacionadas con música, efectos de sonido y voces de Pikachu.

Muestras de sonido SWAR

Cada archivo SWAR contiene una tabla de referencias a muestras individuales.

En la estructura analizada, el número de muestras se encuentra en +0x38 respecto al inicio del archivo, seguido de una tabla de offsets de 32 bits a partir de +0x3C.

Las muestras incluyen información sobre su codificación, frecuencia de reproducción y puntos de repetición.

Código:
Offset  Tipo  Contenido

+0x00   u8    Tipo de codificación
+0x01 u8 Indicador de loop
+0x02 u16 Frecuencia de muestreo
+0x06 u16 Inicio del loop
+0x08 u32 Longitud restante
+0x0C ... Datos de audio
Los formatos de codificación identificados son PCM8, PCM16 y ADPCM.

Este último utiliza compresión diferencial para reducir el tamaño de las muestras. Por ello, recuperar los sonidos originales implica reconstruir las muestras PCM a partir de los datos comprimidos y sus parámetros de predicción.

6. Localización de los recursos en la ROM PAL

Una vez entendidas estas estructuras, podemos localizar los modelos y sonidos sin depender de direcciones absolutas previamente conocidas. Para los modelos, el recorrido es:

Código:
Cabecera NDS
└── FNT / FAT
└── archive.archive
└── Tabla de entradas
└── mdl_*.rel
└── RelocMod
└── Datos GX
La dirección absoluta de un módulo se calcula sumando el offset de archive.archive en la ROM y el desplazamiento de la entrada dentro de ese archivo. En el caso del audio:

Código:
Cabecera NDS
└── FNT / FAT
└── Archivo .sdat
└── FAT de SDAT
└── SSEQ / SSAR / SBNK / SWAR
De manera equivalente, la dirección absoluta de un recurso sonoro se obtiene sumando la posición del archivo .sdat dentro de la ROM y el offset indicado por su propia tabla FAT. Esto permite trabajar con los archivos originales de la versión europea y reconstruir las relaciones entre los diferentes recursos sin tener que extraerlos manualmente uno por uno.

7. Resultado

Como una imagen vale más que mil palabras, aquí está el resultado de toda esta investigación:
1794613658269.png


ue.ncg.png


¡Y nada más por ahora! Espero que este pequeño vistazo a las entrañas de Pokémon Dash resulte interesante para quienes disfrutan investigando los formatos y sistemas internos de Nintendo DS (Ya nos hemos casi movido entre todas las plataformas :cool: ).
 
Arriba