¡Buenas una vez más! Vengo a traer otro proyecto que tengo de hacía tiempo de Ingeniería inversa y que dejé en el tintero documentandolo. Bien, se trata de Pokémon The Band of Thieves & 1000 Pokémon (とうぞくと1000びきのポケモン), de Nintendo 3DS. Para el que no lo conozca, es un videojuego de acción y estrategia desarrollado por Marvelous AQL y publicado por The Pokémon Company, lanzado exclusivamente en Japón el 5 de junio de 2014 para Nintendo 3DS. Se distribuyó gratuitamente a través de la Nintendo eShop durante un periodo limitado, hasta el 30 de septiembre de ese mismo año, sin llegar a publicarse oficialmente en Europa ni América.
Como ya he hecho en otros proyectos de ingeniería inversa, esta investigación se ha realizado a través de un parser e importer personalizado para Unity Engine, con el objetivo de interpretar directamente los archivos originales del juego y reconstruir sus recursos dentro del editor. Para ello, ha sido necesario estudiar las cabeceras binarias, los offsets, las estructuras internas y los diferentes métodos de codificación utilizados por NintendoWare CTR, incluyendo las particularidades del hardware gráfico PICA200 de Nintendo 3DS.
Dicho esto, vamos a entrar en la parte técnica y ver cómo están organizados los archivos del juego y qué hemos podido descubrir de cada uno de ellos. Por un lado, tenemso que echar un ojo a los archivos que tenemos en Romfs de la ROM:
1. Estructura General
A diferencia de otros títulos de Pokémon para Nintendo 3DS, The Band of Thieves & 1000 Pokémon (desde ahora PTBOT) no utiliza los formatos habituales de los juegos principales de la saga, sino que se apoya principalmente en NintendoWare CTR, el conjunto de bibliotecas y formatos gráficos, sonoros y de interfaz proporcionado por Nintendo para el desarrollo de videojuegos en esta consola. Esto significa que buena parte de sus recursos internos están organizados en formatos como CGFX, BCLIM, BCFNT, BCSTM o DARC, cada uno con sus propias estructuras binarias, tablas de referencias y sistemas de almacenamiento:
Como se puede ver, y como ya he mencionado anteriormente, estamos ante formatos completamente diferentes a los que solemos encontrar en los juegos principales de Pokémon para Nintendo 3DS, como Pokémon X/Y o Pokémon Sol/Luna. En realidad, los recursos de PTBOT se parecen mucho más a los utilizados por otros títulos desarrollados con las bibliotecas NintendoWare CTR, que proporcionaban a los estudios una serie de formatos y herramientas estandarizados para trabajar con el hardware de la consola.
¿Y por qué se utilizan estos formatos? Básicamente, porque NintendoWare CTR facilitaba considerablemente el desarrollo para Nintendo 3DS. En lugar de tener que diseñar un sistema propio para almacenar modelos, animaciones, texturas, sonidos o interfaces, los desarrolladores podían aprovechar las herramientas oficiales de Nintendo, que ya estaban preparadas para las características y limitaciones de la consola. Por ejemplo, CGFX permite organizar modelos, materiales y animaciones, mientras que BCLIM almacena imágenes en formatos compatibles con el procesador gráfico PICA200, incluyendo técnicas de compresión y organización de píxeles adaptadas al hardware.
Esto también explica por qué encontramos tantos formatos diferentes dentro de la RomFS: cada uno está especializado en un tipo de recurso y responde a unas necesidades concretas. No significa que PTBOT utilice un motor de Nintendo como tal, sino que sus desarrolladores aprovecharon las bibliotecas y herramientas de NintendoWare CTR para construir su propio juego.
Desde el punto de vista de la ingeniería inversa, esto tiene una ventaja interesante: parte del conocimiento obtenido al estudiar estos formatos puede reutilizarse para investigar otros juegos de Nintendo 3DS que también empleen NintendoWare CTR, aunque cada título pueda introducir sus propias modificaciones o estructuras adicionales.
2. Contenedores CGFX: modelos y animaciones
Sabiendo que
Lo interesante es que estas referencias se almacenan habitualmente como offsets relativos, es decir, desplazamientos calculados desde una posición concreta de la estructura, en lugar de direcciones absolutas. Por tanto, para localizar correctamente cada recurso, necesitamos interpretar el desplazamiento y calcular su posición real dentro del archivo.
Una vez resueltas estas referencias, podemos establecer una representación simplificada de la organización interna de CGFX:
¿Por qué se organiza de esta manera?
Principalmente, porque un modelo 3D no está formado únicamente por sus vértices y polígonos. También necesita materiales, texturas, información de huesos y, en muchos casos, animaciones. Al separar estos elementos en estructuras independientes y relacionarlos mediante referencias, el formato permite organizar múltiples recursos dentro de un mismo contenedor sin duplicar necesariamente toda su información.
Por ejemplo, un objeto
Por tanto, el proceso de importación no consiste simplemente en extraer un modelo del archivo CGFX, sino en recorrer sus diccionarios, resolver las referencias internas y reconstruir las relaciones entre los diferentes recursos para convertirlos en objetos que Unity o el motor de juegos o render, pueda utilizar.
3. Texturas PICA200 y orden Morton
Siguiendo la premisa mencionada, las texturas se organizan en bloques de 8 × 8 píxeles mediante una distribución Morton (tiled/swizzled), optimizada para el procesador gráfico PICA200 de Nintendo 3DS. Además, algunas utilizan compresión ETC1 o ETC1A4 para reducir el consumo de memoria, quedando de la siguiente manera:
BCLIM: texturas con cabecera al final
Otra peculiaridad de los recursos gráficos de PTBOT la encontramos en los archivos .bclim, utilizados principalmente para almacenar texturas de interfaz. A diferencia de otros formatos, estos archivos almacenan los datos gráficos al principio y la cabecera al final, concretamente en los últimos
Además, las dimensiones utilizadas para almacenar las texturas pueden ser mayores que las dimensiones visibles, generalmente ajustándose a potencias de dos y respetando un mínimo de 8 × 8 píxeles. Esto permite adaptar los datos a la organización de memoria del procesador gráfico PICA200, aunque parte de la textura no llegue a mostrarse en pantalla.
Esta estructura se puede observar en al menos 535 recursos BCLIM encontrados dentro de la RomFS del juego.
4. Archivos de interfaz DARC / NWLYT
En este juego, determinados ".nwlyt" son contenedores DARC en little-endian. Su cabecera ocupa
Cada entrada de tabla ocupa 12 bytes. El primer u32 combina un offset de nombre de 24 bits con el indicador de directorio
Hay que tener encuenta aun así que extraer imágenes de un archivo no equivale a reconstruir completamente el layout y sus animaciones.
5. Bancos de texto BTXT
Los archivos
Cada entrada de 8 bytes:
La representación de texto es UTF-16 y puede incluir unidades de control. PTBOT conserva tres variantes:
6. Fuentes, audio y recursos adicionales
Fuentes: PTBOT utiliza archivos
Audio: Los archivos
Otros recursos: Además de los formatos anteriores, encontramos estructuras como
7. Resultados
Como una imagen vale más que mil palabras, aquí está el resultado de toda esta investigación:
Y nada, eso es prácticamente todo. Si alguien quiere juguetear con lso archivos, no tenéis que bajaros la ROM y empezar a sacar modelos y sprites.
Como ya he hecho en otros proyectos de ingeniería inversa, esta investigación se ha realizado a través de un parser e importer personalizado para Unity Engine, con el objetivo de interpretar directamente los archivos originales del juego y reconstruir sus recursos dentro del editor. Para ello, ha sido necesario estudiar las cabeceras binarias, los offsets, las estructuras internas y los diferentes métodos de codificación utilizados por NintendoWare CTR, incluyendo las particularidades del hardware gráfico PICA200 de Nintendo 3DS.
Dicho esto, vamos a entrar en la parte técnica y ver cómo están organizados los archivos del juego y qué hemos podido descubrir de cada uno de ellos. Por un lado, tenemso que echar un ojo a los archivos que tenemos en Romfs de la ROM:
1. Estructura General
A diferencia de otros títulos de Pokémon para Nintendo 3DS, The Band of Thieves & 1000 Pokémon (desde ahora PTBOT) no utiliza los formatos habituales de los juegos principales de la saga, sino que se apoya principalmente en NintendoWare CTR, el conjunto de bibliotecas y formatos gráficos, sonoros y de interfaz proporcionado por Nintendo para el desarrollo de videojuegos en esta consola. Esto significa que buena parte de sus recursos internos están organizados en formatos como CGFX, BCLIM, BCFNT, BCSTM o DARC, cada uno con sus propias estructuras binarias, tablas de referencias y sistemas de almacenamiento:
Código:
Extensión / recurso Firma o estructura Función
------------------- ------------------------------------- ------------------------------------------
.nwmdl CGFX → DATA / DICT / CMDL Modelos y materiales
.nwanm, .nwanmm CGFX → CANM Animación esquelética / material
.nwptl CGFX Recursos de partículas
.nwlyt DARC Archivo de diseño UI con miembros internos
.bclim CLIM al final + imag Texturas de interfaz
.bcfnt CFNT / FINF / TGLP Fuentes y atlas de glifos
.bcstm CSTM / INFO / SEEK / DATA Audio de streaming
.btxt Sin magic fijo; cabecera y entradas Bancos de texto japonés
Recursos de sonido CSAR Archivo de sonido
Base de datos ICDB Datos internos
Binarios de shader DVLB Código gráfico
Iconos en bruto Dimensiones inferidas del tamaño Gráficos de iconos
¿Y por qué se utilizan estos formatos? Básicamente, porque NintendoWare CTR facilitaba considerablemente el desarrollo para Nintendo 3DS. En lugar de tener que diseñar un sistema propio para almacenar modelos, animaciones, texturas, sonidos o interfaces, los desarrolladores podían aprovechar las herramientas oficiales de Nintendo, que ya estaban preparadas para las características y limitaciones de la consola. Por ejemplo, CGFX permite organizar modelos, materiales y animaciones, mientras que BCLIM almacena imágenes en formatos compatibles con el procesador gráfico PICA200, incluyendo técnicas de compresión y organización de píxeles adaptadas al hardware.
Esto también explica por qué encontramos tantos formatos diferentes dentro de la RomFS: cada uno está especializado en un tipo de recurso y responde a unas necesidades concretas. No significa que PTBOT utilice un motor de Nintendo como tal, sino que sus desarrolladores aprovecharon las bibliotecas y herramientas de NintendoWare CTR para construir su propio juego.
Desde el punto de vista de la ingeniería inversa, esto tiene una ventaja interesante: parte del conocimiento obtenido al estudiar estos formatos puede reutilizarse para investigar otros juegos de Nintendo 3DS que también empleen NintendoWare CTR, aunque cada título pueda introducir sus propias modificaciones o estructuras adicionales.
2. Contenedores CGFX: modelos y animaciones
Sabiendo que
NintendoWare CTR utiliza el contenedor CGFX para almacenar recursos gráficos, podemos comenzar a analizar su estructura interna. Lo primero que encontramos es una cabecera identificada por la misma firma CGFX, que nos permite reconocer el archivo y obtener información sobre su organización. A partir de esta cabecera podemos localizar el bloque DATA, que contiene una serie de referencias a diccionarios DICT. Estos diccionarios funcionan como índices: cada entrada identifica un recurso mediante su nombre y una referencia a su posición dentro del archivo.Lo interesante es que estas referencias se almacenan habitualmente como offsets relativos, es decir, desplazamientos calculados desde una posición concreta de la estructura, en lugar de direcciones absolutas. Por tanto, para localizar correctamente cada recurso, necesitamos interpretar el desplazamiento y calcular su posición real dentro del archivo.
Una vez resueltas estas referencias, podemos establecer una representación simplificada de la organización interna de CGFX:
Código:
CGFX
└── DATA
├── DICT: Models ──────────► CMDL
│ ├── SOBJ (shapes / meshes)
│ ├── Skeleton / bones
│ └── Material references
├── DICT: Textures ────────► TXOB
├── DICT: Materials ───────► MTOB
├── DICT: SkeletalAnims ───► CANM
└── Otros diccionarios
Principalmente, porque un modelo 3D no está formado únicamente por sus vértices y polígonos. También necesita materiales, texturas, información de huesos y, en muchos casos, animaciones. Al separar estos elementos en estructuras independientes y relacionarlos mediante referencias, el formato permite organizar múltiples recursos dentro de un mismo contenedor sin duplicar necesariamente toda su información.
Por ejemplo, un objeto
CMDL representa un modelo, pero para reconstruirlo necesitamos interpretar sus objetos de geometría SOBJ, sus referencias a materiales MTOB y las texturas TXOB que correspondan. Si además dispone de un esqueleto, debemos reconstruir la jerarquía de huesos y sus transformaciones. Algo similar ocurre con las animacionesCANM, que almacenan información sobre los elementos animados, las curvas, los fotogramas clave y sus métodos de interpolación. Estos datos deben interpretarse y asociarse correctamente con los objetos correspondientes para reproducir el movimiento original.Por tanto, el proceso de importación no consiste simplemente en extraer un modelo del archivo CGFX, sino en recorrer sus diccionarios, resolver las referencias internas y reconstruir las relaciones entre los diferentes recursos para convertirlos en objetos que Unity o el motor de juegos o render, pueda utilizar.
3. Texturas PICA200 y orden Morton
Siguiendo la premisa mencionada, las texturas se organizan en bloques de 8 × 8 píxeles mediante una distribución Morton (tiled/swizzled), optimizada para el procesador gráfico PICA200 de Nintendo 3DS. Además, algunas utilizan compresión ETC1 o ETC1A4 para reducir el consumo de memoria, quedando de la siguiente manera:
Código:
Código PICA Formato Bits por píxel
----------- -------- --------------
0 RGBA8 32
1 RGB8 24
2 RGBA5551 16
3 RGB565 16
4 RGBA4 16
5 LA8 16
6 HILO8 16
7 L8 8
8 A8 8
9 LA4 8
10 L4 4
11 A4 4
12 ETC1 4
13 ETC1A4 8
Otra peculiaridad de los recursos gráficos de PTBOT la encontramos en los archivos .bclim, utilizados principalmente para almacenar texturas de interfaz. A diferencia de otros formatos, estos archivos almacenan los datos gráficos al principio y la cabecera al final, concretamente en los últimos
0x28 bytes del archivo. Esta cabecera contiene las firmas CLIM e imag, además de información como las dimensiones de la imagen, su formato y el tamaño de los datos gráficos.
Código:
Offset Tipo Campo
+0x00 char[4] CLIM
+0x04 u16 BOM (0xFEFF)
+0x0C u32 Tamaño total del archivo
+0x14 char[4] imag
+0x1C u16 Ancho visible
+0x1E u16 Alto visible
+0x20 u32 Formato CLIM
+0x24 u32 Tamaño de los texels
Esta estructura se puede observar en al menos 535 recursos BCLIM encontrados dentro de la RomFS del juego.
4. Archivos de interfaz DARC / NWLYT
En este juego, determinados ".nwlyt" son contenedores DARC en little-endian. Su cabecera ocupa
0x1C bytes y contiene la información necesaria para localizar las tablas de entradas y los datos.
Código:
Campo Tipo / tamaño Descripción
------------ ------------- ----------------------
Magic char[4] `darc`
BOM u16 `0xFEFF`
Header size u16 `0x1C`
Version u32 Versión del contenedor
File size u32 Tamaño total
Table offset u32 Inicio de tabla
Table size u32 Longitud de tabla
Data offset u32 Inicio de datos
0x01000000. Los dos campos siguientes representan (offset, size) para archivos o (parentIndex, endIndex) para directorios. Los nombres se leen de un pool UTF-16LE.
Código:
darc (.nwlyt)
├── Header (0x1C)
├── Entry table (12 bytes × N)
├── UTF-16LE name pool
└── File data
├── timg/*.bclim
├── *.bclyt
├── *.bclan
└── Otros miembros
5. Bancos de texto BTXT
Los archivos
data/jpn/text/*.btxt son especialmente interesantes porque no tienen una firma ASCII fija. El parser valida la forma de su cabecera y las tablas internas.
Código:
Offset Tipo Significado observado
------ ------------ ---------------------------------------------------
0x00 u32 0
0x04 u32 Valor no identificado (0xFF121024 / 0xFF100527)
0x08 u16 Número de entradas
0x0A u16 Segundo contador, igual al anterior
0x0C entry[8] × N Tabla de entradas
Código:
Offset relativo Tipo Campo
--------------- ---- -------------------------------------------------------------
+0x00 u32 Flag (observado como 1)
+0x04 u8 MetricA (métrica similar a anchura; significado incompleto)
+0x05 u8 LineCount (líneas separadas por LF)
+0x06 u8 MetricC (desconocido)
+0x07 u8 Índice de un byte
Raw (texto original), Markup (controles legibles y reversibles) y Plain (presentación simplificada). Los controles de ruby se representan como {ruby:base|reading} y {ruby1:base|reading} para los códigos U+1700 y U+1701; los controles desconocidos se conservan como {XXXX}.6. Fuentes, audio y recursos adicionales
Fuentes: PTBOT utiliza archivos
BCFNT identificados mediante la firma CFNT, que contienen estructuras como FINF y TGLP. Estas permiten almacenar información tipográfica y atlas de glifos, donde se encuentran las representaciones gráficas de los caracteres utilizados por el juego.Audio: Los archivos
BCSTM utilizan la firma CSTM y se organizan en bloques como INFO, SEEK y DATA. Estos almacenan la configuración del sonido, la información necesaria para su reproducción y los datos de audio, incluyendo características como los puntos de repetición (loop). También encontramos recursos CSAR, utilizados para organizar archivos y referencias de sonido.Otros recursos: Además de los formatos anteriores, encontramos estructuras como
ICDB, relacionadas con datos internos del juego, y DVLB, utilizadas para almacenar código de shaders destinado al procesador gráfico PICA200. También existen iconos almacenados directamente como datos gráficos, sin necesidad de utilizar un contenedor convencional.7. Resultados
Como una imagen vale más que mil palabras, aquí está el resultado de toda esta investigación:
Y nada, eso es prácticamente todo. Si alguien quiere juguetear con lso archivos, no tenéis que bajaros la ROM y empezar a sacar modelos y sprites.