Registrarse

[NDS] Expansion del ARM9

Estado
Cerrado para nuevas respuestas.

Kaiser de Emperana

Called in hand
Bueno, no llegue a terminar esto, pero no lo voy a tocar por un tiempo así que decidí postearlo.

Estuve intentando crear una plantilla que permita compilar código en C o ASM e insertarlo en una rom de Pokemon Platino; y lo logré. El problema es que si bien el código es cargado en la ram, puede ser llamado en cualquier momento y todo parece funcionar perfecto al principio, llega un momento en que el juego crashea. No terminé de comprobar al 100% porque es que pasa pero estoy bastante seguro que es porque la dirección de RAM que estoy usando para cargar el cósigo es usada por otra cosa (probablemente el Heap the malloc).

Así que bueno, para que esto termine siendo funcional del todo hayq ue cambiar la dirección de la rom donde el código es cargado de alguna forma, pero me quedé sin tiempo, por lo que, como mencioné al principio, estoy haciendo este post.

Plantilla
Bueno, todo lo que hice está en esta plantilla https://github.com/kaisermg5/pt_c_template.
Para usarla necesitan:
  • Cygwin
  • Python 3.5 o superior
  • DevkitARM (poniendo sus binarios en path)
  • Make

Luego ponen su rom de pokemon platino en la carpeta del repositorio con el nombre "baserom.nds" y ejecutan make. El juego parcheado aparecerá en la carpeta build.

¿Como funciona?
Bueno, mi idea era crear una especie de overlay que se cargue al principio del juego donde estaría al código de la plantilla. Pero como expandir la tabla de overlays es bastante complicado lo que decidí fue simplemente ignorar el sistema de overlays, guardar el código en un archivo cualquiera del juego (yo elegí "data/test.atr") y cargarlo simplemente copiando sus contenidos en la RAM (sin usar las funciones de carga de overlays).

El problema es que para cargar el código en RAM, necesitaba justamente insertar código en el ARM9, que bueno, si se pudiera hacer eso como que este tema no sería necesario... Así que lo que hice fue copiar el código del bucle principal del juego (que está en el ARM9) a la estensión que estoy creando (el código de la plantilla) y de esta forma podría usar el espacio del bucle original, para cargar el código de la extensión en memoria y luego pasar a ejecutar la copia del bucle principal.

Todo esto está hecho y funciona, sólo que como mencione anteriormente, con el paso del tiempo el juego crashea.

Compilación
Bueno, lo que mencioné antes es la teoría, ahora vendría tocar hablar de la práctica. Como necesito tanto remplazar sobreescribir parte del código del ARM9, como insertar un código aparte, lo que necesito es compilar dos archivos binarios distintos. El código del parche del ARM9 está dentro de la carpeta "arm9_patch" y el código de la extensión dentro de la carpeta "src" y las direcciones en donde los dos serán insertados están en "arm9_patch/main_patch.ld" y "arm9_extension.ld" respectivamente.

Cuando se ejecute "make" se compilarán dos binarios y un script de python los insertará en el rom y hará los cambios necesarios para que funcione. La configuración de ese script de python está en el archivo "tools/patcher_config.py" y se ve así:

Código:
from patcher_utils import Rom, RomPatch

[COLOR="Gray"]# Usage: 
# RomPatch(offset, expression)

# Expressions:
# Binary string: "00 11 22 33"
# File: "#f{filename}"
# Symbol from the compiled extension (check build/offsets.txt): #w{symbol_name}
# Thumb function (the same as the previous one, but ticks the least significant bit to the offset): #t{symbol_name}

# You can use symbols in the offset calculations by making a call to: Rom.get_symbol('symbol_name')[/COLOR]

PATCHES = [
	# Patch ARM9 code
	[COLOR="Blue"]RomPatch(0x4d58, '#f{build/arm9_patch.bin}'),[/COLOR]

	# Patch fat.bin to replace "data/test.atr"
	[COLOR="DarkOrange"]RomPatch(0x432c00 + 0x818, '#w{__arm9_extension_rom_start}#w{__arm9_extension_rom_end}'),[/COLOR]

	# Insert ARM9 extension
	[COLOR="Cyan"]RomPatch(Rom.get_symbol('__arm9_extension_rom_start'), '#f{build/arm9_extension.bin}'),[/COLOR]

	# Replace script command: Nop1 
	# RomPatch(Rom.get_symbol('JumpTable_ScriptHandler') + 4, '#t{CustomScrCmd_CallSpecial}'),
]
Lo importante son las líneas pintadas en color (todos los offsets menciandos se refieren al offset de la rom abierta desde un editor hexadecimal, no cuando está corriendo en el emulador):
Azul: Copia el binario compilado del código del parche, en el offset 0x4d58 (lugar donde se encuentra el código del bucle principal del ARM9)
Najanja: Escribe dos words en el offset 0x433418, la primera es el offset donde se inserta el binario de la extension del ARM9 y la segunda es el offset donde la misma termina.
Cyan: Copia el binario compilado de la extensión en el offset que se menciona en la línea anterior.

Pintado en gris hay una explicación de como es que funciona este sistema, pero la verdad no se si se entiende del todo, si alguien tiene una duda que me pregunte por discord, perfil o (mejor aún) este tema.

Básicamente esto es lo mismo que abrir el rom con un editor hexadecimal y copiar los datos y punteros a mano, con la diferencia de que con este método, se lo configura una vez y ya está.


Cerrando
Bueno, no se si me estoy dejando algo, pero creo que eso es todo. Se que no es la mejor documentación del mundo, pero algo es algo. Cualquier duda consulten.

Espero que esto le sirva a alguien.

Saludos
 

ElToby

RomHacker de NDS
Uff super interesante.
Dejame decirte que cuando tenga mas tiempo le echare una ojeada.
Seguro me servira a mi y a muchas personas en un futuro.
Se te agradece el trabajo.
 

Mikelan98

WaH used to be a bigger place...
Sujétame el cubata (?)

El código en ese offset se reescribe cada vez que el juego debe cargar el overworld (mapas, permisos, NPCs... todo). Así que, efectivamente, es código importante que no se debe "chafar". Cuando tenga tiempo, intentaré hacer algo parecido con HGSS (creo que te lo dije, pero las rutinas de carga de overlays están encontradas en DPPt, por lo que se puede hacer con ASM puro y duro).
 

Kaiser de Emperana

Called in hand
Sujétame el cubata (?)

El código en ese offset se reescribe cada vez que el juego debe cargar el overworld (mapas, permisos, NPCs... todo). Así que, efectivamente, es código importante que no se debe "chafar". Cuando tenga tiempo, intentaré hacer algo parecido con HGSS (creo que te lo dije, pero las rutinas de carga de overlays están encontradas en DPPt, por lo que se puede hacer con ASM puro y duro).
Dudo mucho que sea código lo que hay en ese offset (0x0238000, para los que no hayan mirado), ya que en el y9 no hay ningun overlay que contenga esa región y además en Pokeplat nada de código es compilado para estar en esa zona. Pero si que tendría mucho sentido que lo si está en esa zona esté de alguna forma relacionado con los overworlds, siendo de el juego crashea siempre que intenta cargar un mapa xD

Y sobre los overlays, el problema no es que no haya podido usar las funciones originales del juego, no las usé por dos razones:
- Quería basicamente crea un segundo ARM9, o sea una parte que esté constantemente cargada (Lo cual la verdad ahora me parece que sería bastante complicado de lograr).
- Usar la tabla original de overlays implicaría tener que remplazar algún overlay existente, porque expandir la tabla del y9 tiene exactamente el mismo problema que estoy teniendo ahora; agrandarlo es posible, pero después no tengo una zona de la ram donde ponerlo x'D

Además como puse en el post no tuve ningún problema para cargar el código en ram, simplemente abrí un archivo y copié sus contenidos a la misma. Y de esta forma el y9 queda tal y como estaba, y yo podría crear todas las regiones de código que quiera sin tener que remplazar ningún overlay original.
 
Estado
Cerrado para nuevas respuestas.
Arriba