Registrarse

[ASM] Pt-EN | Editar la efectividad entre tipos e insertar el Tipo Hada (¡compatible con PokePlat!)

Mikelan98

WaH used to be a bigger place...

Antes de nada, aclarar que pido créditos si usáis esta investigación para modificar vuestra ROM. Así mismo, aclaro que sólo funciona para la versión inglesa (la que funciona con PokePlat) porque me da muchísima flojera portar esto a una ROM española.


Introducción

Me ha costado 24 horas resolverlo, pero he encontrado un método para ampliar la tabla de efectividades entre tipos sin tener que expandir ningún overlay de la ROM. Hay que tener en cuenta que, a la hora de compilar PokePlat, los overlays se recortan automáticamente hasta ocupar el tamaño original, por lo que el método MeroMero no sería aplicable en este caso (además de que hay serias dificultades para hallar espacio libre lo suficientemente grande como para realojar la tabla). Si esto de la "tabla de efectividades" te está sonando a chino, recomiendo que te pases por éste thread para que sepas de lo que hablamos, pues es prácticamente el mismo mecanismo en HGSS. Sin embargo, al no existir espacio libre en la RAM, esta estrategia no nos sirve.

¿Qué estrategia vamos a seguir entonces? Pues una estrategia que funciona muy bien cuando tenemos largas cadenas de datos binarios en el arm9 o los overlays y queremos ampliarlas: la reducción de bytes a nibbles. Cabe destacar que esta estrategia es válida sólo cuando merezca la pena en ciertos casos, es decir, cuando esta reducción sea más práctica que buscar espacios en blanco. Como la tabla de efectividades es muy grande y además queremos ampliarla con bastantes bytes (teóricamente más de 30 bytes) podemos seguir esta estrategia.

Para poder aplicar esta estrategia tenemos que saber muy bien cuándo y cómo se leen esos datos, para modificar todas las rutinas involucradas. Por ello, el tracing nos va a ser de gran utilidad en esta investigación.


Fundamento

Antes de seguir, insisto en que es conveniente repasar el thread de HGSS, pero resumiendo, la tabla se compone de bytes formando tripletes AA | DD | EE, donde AA es el ID del tipo del ataque, DD es el ID del tipo del Pokémon que lo recibe, y EE es la efectividad, la cual puede ser 00=inmune, 05=poco eficaz, 0A=neutro, 14=muy eficaz.

La rutina original para leer los datos de la tabla se activa cuando se lanza un ataque (que no sea de estado) y funciona de la siguiente manera: en primer lugar, el juego almacena en la pila (stack) los valores del tipo de ataque y el/los tipo/s del Pokémon que recibe el ataque. A continuación, el juego se introduce en un bucle en el que el valor más importante a tener en cuenta se localiza en el Registro 0. Ese valor empieza el bucle con el número 0x0226ECD4, que es donde empieza la tabla de efectividades (es decir, donde empieza el primer triplete). En otro registro también hay un número, que comienza en 0 y va aumentando de uno en uno, que bien podríamos definir como un localizador que nos indica el loop actual, pero sólo funciona de apoyo para cálculos de la rutina. Pues bien, el código del juego empieza leyendo el primer byte que nos encontramos en la tabla (que corresponde al tipo de ataque del primer triplete) y lo comprueba con el valor que habíamos guardado previamente en la pila.

¿Es el mismo valor? Entonces hemos encontrado uno de los posibles tripletes que corresponden a lo que busca el juego, y el juego pasa a comparar el segundo valor del triplete para confirmarlo.

¿No corresponde alguno de esos 2 valores? Primero leemos el primer byte del siguiente triplete. Si es FF, abortamos el loop, pues el juego entiende que ya ha terminado de leer toda la tabla. Si es distinto de FF, el juego vuelve a hacer el loop pero sumando +3 al registro que antes tenía el valor 0x0226ECD4, de modo que ahora tenemos la localización del segundo triplete y, de hecho, leeremos el segundo triplete. Así, vamos comparando hasta localizar un triplete cuyos dos primeros valores coincidan con lo que busca el juego, es decir, cuando tengamos la localización del triplete que busca el juego (tipo del ataque y tipo del defensor).

Una vez el juego ha encontrado ese triplete (si no ha encontrado ninguno, se realizará un daño de efectividad neutra), se almacena el valor de la localización del triplete (sí, el valor del registro que empezaba en 0x0226ECD4 y que ahora tiene otro offset: justo el offset en el que se encuentra el triplete necesario). Ese valor se "entierra" en la pila para, poco después, usarlo para leer 2 bytes más allá de ese offset (offset + 0x2), lo que le da al juego el tercer byte del triplete seleccionado: la efectividad. Con eso se hacen cálculos más profundos, pero es algo que no nos interesa.

Por otro lado, la parte gráfica de la Pokédex (para que muestre el tipo hada) necesita también un retoque de ASM. Si miramos tal cual, sin modificar nada, veremos que los Pokémon de tipo hada son mostrados como tipos fantasma.

Nos vamos al overlay 21, al offset 0xE40A. Justo en ese offset se encuentran dos instrucciones que llamo yo "de limpieza", teóricamente no sirven de absolutamente nada, pero son vestigios de ensamblador. Lo que hacen ese par de comandos es hacer shift hacia un lado y luego hacia otro para borrar el halfword superior, pero que en el caso de la tabla con la que va a trabajar, no sirve para nada. Este par de instrucciones es muy probable que esté ahí porque, a la hora de programar en C el juego, se hayan reasignado esos valores de una variable de 32 bit a una de 16 bit.

Pero volvamos al tema que nos interesa. Justo antes de estas dos instrucciones, el programa lee una tabla con 17 posibles valores, cuyos bytes indican un "salto" del registro PC (program counter, la instrucción que está leyendo el procesador en ese momento). Una vez que se hayan "limpiado" esos valores, se le añade al registro PC ese valor, por lo que el programa se puede ramificar en 17 posibles caminos, cada uno simplemente carga un valor concreto a un registro (que será el registro que determine el icono del tipo a cargar).

Pues bien, la tabla que he mencionado tiene un posible valor para el tipo hada, pero es el mismo valor que el tipo fantasma. Por tanto, aunque exista una entrada en la tabla, no existe una ramificación del código para el tipo hada. Pero no os preocupéis en absoluto. Esas ramificaciones ocupan sólo 4 bytes. ¿Entendéis ahora por qué he mencionado esas 2 instrucciones inútiles en el código? Vamos a borrarlas para hacerle hueco a la ramificación del tipo hada.


Modificaciones

Nuestro objetivo será reducir en nibbles alguna parte de la tabla. ¿Cuál? Pues la única que es matemáticamente posible de reducir a nibbles, la de las efectividades. ¿Por qué? Porque si nos damos cuenta, todos los valores son múltiplos de 5. Podemos usar como valores de efectividad 0, 1, 2 y 4 y multiplicarlos con una instrucción. De esta manera, podemos almacenarlos en nibbles. Pues manos a la obra.

En los offsets 0x1A01A y 0x1A074 del overlay 16 (cada uno pertenece a una subrutina, y cada subrutina se encarga de uno de los dos tipos del Pokémon que recibe el ataque) vamos a puentear el código, es decir, hacer un branch y llevarnos el código a otra parte de la ROM, donde haya suficiente hueco libre. En el arm9 hay unas cuantas líneas de 00, que si bien no he comprobado que sea seguro modificarlas, no he encontrado bugs o crasheos al testear el resultado.

A fin de cuentas, la tabla al final se dividirá en dos subtablas, una con pares (en vez de tripletes) AA | DD, y más abajo, todos los valores de efectividad (divididos entre 5) seguidos. Lo podéis ver en la siguiente imagen.


En el offset 0x1A01A del overlay 16 insertaremos el siguiente código:

Código:
C0 46 00 49 88 47 01 94 0F 02 C0 46

Y en el offset 0x1A074, el siguiente:

Código:
00 49 88 47 01 94 0F 02 C0 46 C0 46

De esta forma, estamos redirigiendo al juego al offset 0x020F9400, correspondiente al arm9 en la RAM, donde insertaremos el siguiente código (lo explico para que aprendáis).


STR R0, [SP,#arg_4] . . . . . . . . . (Instrucción cortada al puentear y reescrita aquí)
PUSH {R3-R5} . . . . . . . . . . . . . (Guardamos los registros anteriores para no perderlos y poder operar con ellos)
LDR R3, =byte_226ECD4 . . . . .(Cargamos el offset de la tabla de efectividades)
SUBS R2, R2, R3 . . . . . . . . . . . .(Restamos ése offset al registro 2, que contenía el offset del triplete, de modo que obtenemos el ID del triplete)
LSRS R2, R2, #1 . . . . . . . . . . . . (Multiplicamos este ID x2)
MOVS R4, 0x10C . . . . . . . . . . . (Hay una distancia de 0x10C desde el inicio de la primera subtabla al inicio de la segunda)
ADDS R3, R3, R4 . . . . . . . . . . . (Al sumar estos dos registros, tenemos el offset de inicio de la segunda subtabla)
LSRS R2, R2, #1 . . . . . . . . . . . . (Dividimos el ID entre 2. ¡Paso crítico! Va a haber números impares dividiéndose, estamos haciendo la reducción)
LDRB R3, [R3,R2] . . . . . . . . . . .(Leemos el byte correspondiente de sumar el offset de la segunda subtabla y el ID/2)
BCS loc_20F941E . . . . . . . . . . . (¡IMPORTANTE! Esta función ramifica la rutina dependiendo de si el ID era par o impar)
MOVS R5, #0xF0 . . . . . . . . . . . .(Cargamos en el registro 5 ese valor, que en binario corresponde a 0b11110000)
ANDS R3, R5 . . . . . . . . . . . . . . .(Realizamos un AND del byte leído y del valor 0b11110000, así nos quedamos con el primer nibble, aka la mitad superior del byte)
LSRS R2, R3, #4 . . . . . . . . . . . . (Dividimos este último valor entre 16 para que se quede con la forma 0b0000XXXX)
B loc_20F9424
MOVS R5, #0xF . . . . . . . . . . . . .(Cargamos en el registro 5 ese valor, que en binario corresponde a 0b00001111)
ANDS R3, R5 . . . . . . . . . . . . . . .(Realizamos un AND del byte leído y del valor 0b00001111, así nos quedamos con el segundo nibble, aka la mitad inferior del byte)
MOV R2, R3 . . . . . . . . . . . . . . . .(Como aquí no tenemos que dividir, hay que traspasar directamente el valor al registro 2, sin modificarlo)
MOVS R5, #5
MULS R2, R5 . . . . . . . . . . . . . . .(Multiplicamos x5 el valor de la efectividad, de modo que quede con su valor final)
MOV R5, LR
ADDS R5, #8
MOV LR, R5 . . . . . . . . . . . . . . . .(Estas tres instrucciones mueven el link register un poco más adelante, ya que había datos binarios donde lo he puenteado)
POP {R3-R5} . . . . . . . . . . . . . . . (Recuperamos los valores originales de esos registros antes de la rutina)
MOV R0, R5 . . . . . . . . . . . . . . . .(Instrucción cortada al puentear y reescrita aquí)
MOV R1, R7 . . . . . . . . . . . . . . . .(Instrucción cortada al puentear y reescrita aquí)
LDR R1, =(sub_225B63C+1)
BX R1 ; sub_225B63C . . . . . . . .(Reconducimos al juego a las rutinas originales)


Esta es la churretada de bytes que corresponden a este código, que insertaremos en el offset 0xF9400 de nuestro arm9.bin:

Código:
01 90 38 B4 0C 4B D2 1A 52 08 86 24 64 00 1B 19 52 08 9B 5C 03 D2 F0 25 2B 40 1A 09 02 E0 0F 25 2B 40 1A 46 05 25 6A 43 75 46 06 35 AE 46 38 BC 28 46 39 46 01 49 08 47 D4 EC 26 02 3D B6 25 02

Aún no hemos terminado, pues tenemos que "re-adaptar" todas las funciones de la tabla para que, en vez de sumar +3 al terminar el loop, sólo sumen +2.

En el offset 0x19FB6 del overlay 16 → C046
En el offset 0x1A084 del overlay 16 → 6100 C046
En el offset 0x1A766 del overlay 16 → C046


Y por último, pero no menos importante, la nueva tabla, que va en 0x33B94 del overlay 16. Os dejo directamente la tabla actualizada con el tipo hada.

Código:
00 05 00 08 0A 0A 0A 0B 0A 0C 0A 0F 0A 06 0A 05 0A 10 0A 08 0B 0A 0B 0B 0B 0C 0B 04 0B 05 0B 10 0D 0B 0D 0D 0D 0C 0D 04 0D 02 0D 10 0C 0A 0C 0B 0C 0C 0C 03 0C 04 0C 02 0C 06 0C 05 0C 10 0C 08 0F 0B 0F 0C 0F 0F 0F 04 0F 02 0F 10 0F 08 0F 0A 01 00 01 0F 01 03 01 02 01 0E 01 06 01 05 01 11 01 08 03 0C 03 03 03 04 03 05 03 07 03 08 04 0A 04 0D 04 0C 04 03 04 02 04 06 04 05 04 08 02 0D 02 0C 02 01 02 06 02 05 02 08 0E 01 0E 03 0E 0E 0E 11 0E 08 06 0A 06 0C 06 01 06 03 06 02 06 0E 06 07 06 11 06 08 05 0A 05 0F 05 01 05 04 05 02 05 06 05 08 07 00 07 0E 07 11 07 08 07 07 10 10 10 08 11 01 11 0E 11 07 11 11 11 08 08 0A 08 0B 08 0D 08 0F 08 05 08 08 FE FE 00 07 01 07 10 09 01 09 06 09 11 09 03 09 08 09 09 10 09 01 09 11 09 08 09 0A 09 03 FF FF 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 FF FF 11 11 44 41 14 41 14 41 41 10 41 14 11 41 14 11 14 14 44 11 44 11 11 44 44 11 11 04 41 40 14 41 44 41 14 41 01 14 11 14 14 14 41 14 41 04 12 44 11 44 12 11 14 41 00 00 11 14 44 44 11 1F FF FF FF FF FF

Si queréis modificarla, os lo vuelvo a explicar. Hay un primer bloque, ordenado en AA | DD, que termina en FF FF, una serie de 00 00 y luego otra vez FF FF. Si queréis insertar más relaciones, quitad el primer FF FF y escribid vuestras nuevas relaciones, pero terminad siempre la última relación con FF FF (para que el juego deje de leer ahí y no empiece a leer los 00).

En la tabla inferior, que comienza después del último FF FF (en 11 11 44 41...) se encuentran las relaciones, correspondientes al mismo orden de los tripletes, donde 0=inefectivo, 1=poco efectivo, 2=neutro, 4=muy efectivo.

Por ejemplo, el segundo triplete es 00 08, que corresponde a Normal | Acero (es decir, un ataque de tipo normal dirigido a un Pokémon de tipo acero). Su efectividad estará en el segundo nibble de la segunda subtabla, es decir, 1 (poco efectivo).

En cuanto al overlay 21 (para que se muestre el tipo hada en la Pokédex) simplemente es borrar 4 bytes, desplazar unos cuantos e insertar 4 bytes. Pero como es más complicado explicaros dónde hay que cortar que daros un puñado de bytes y la dirección de dónde tenéis que sobreescribir, ahí van. Offset 0xE408 del overlay 21:

Código:
49 88 8F 44 22 00 26 00 2A 00 2E 00 32 00 36 00 3A 00 3E 00 42 00 66 00 46 00 4A 00 4E 00 52 00 56 00 5A 00 5E 00 62 00 00 20 70 47 06 20 70 47 0E 20 70 47 0A 20 70 47 08 20 70 47 05 20 70 47 0B 20 70 47 07 20 70 47 09 20 70 47 01 20 70 47 03 20 70 47 02 20 70 47 04 20 70 47 0F 20 70 47 0D 20 70 47 10 20 70 47 0C 20 70 47 12 20 70 47

Cabe mencionar que tenéis que añadir los recursos gráficos correspondientes en resource/zukan (el icono del tipo hada debe aparecer el último, por detrás de esa extraña barra amarilla).

A partir de aquí os toca: modificar el icono ??? que aparece en el menú Pokémon, modificar los datos de los Pokémon para asignarle el tipo hada a los que haya que asignárselo, editar los ataques para lo mismo, y modificar los colores de la movebox. Eso lo dejo en vuestras manos, y especialmente en las de Mimi y Bag que son los que más se han esforzado en conseguir esto, pese a que no han podido seguir por culpa del ASM hasta que a mí me ha dado la gana de hacerlo.

Se aceptan sugerencias para modificar el thread. Gracias y buenas noches. Sed buenos.
 
Última edición:

Drive

Cazador de subnormales
Staff
Redactor/a
Respuesta: PT ING | ASM | Editar la efectividad entre tipos e insertar el Tipo Hada (¡compatible con PokePlat!)

Lo que más me ha gustado ha sido esto:



No es en plan "ponedlo y ya está", sino que has tenido la consideración de que el ser humano es un ser inteligente y que si nos explicas las cosas podemos llegar a aprender.

Parece una tontería lo que digo, pero en la mayoría de tutos es así.
 
Última edición:

BagBoy

3D Artist | NDS Hacker
Respuesta: PT ING | ASM | Editar la efectividad entre tipos e insertar el Tipo Hada (¡compatible con PokePlat!)

Voy a poner mi grano de arena y a terminar de explicar cómo editar la parte gráfica, para sustituir la movebox de los ataques y el icono '???' de los pokémon, por el icono y los colores del tipo Hada.

Empezamos con el icono

Abrimos la ROM con Tinke 0.9.2 y nos vamos a battle/graphic/pl_batt_obj.narc. Pulsamos en Descomprimir para ver lo que contiene el narc, y nos vamos al archivo 'pl_batt_obj_74.RLCN'. Este es el archivo que contiene las paletas con los colores de los distintos tipos.
Lo seleccionamos y pulsamos en Extraer. Ahora tenemos que abrir este archivo que acabamos de guardar, con la herramienta ConsoleTool para poder editar los colores del tipo ??? y sustituirlos por los 3 colores del icono de tipo Hada que queramos insertar, seleccionando uno a uno los 3 colores y editando sus valores RGB desde el panel de la derecha. Si al editar por ejemplo el primer valor, os vais al siguiente y el primero se cambia por otro ligeramente distinto al que teníais, no os asustéis, es que los valores que uséis en los colores de las paletas DEBEN ser múltiplos de 8 para que funcionen, y la herramienta se encarga de ajustarlos automáticamente.


Una vez que hayamos preparado la paleta con los colores del icono que vamos a insertar, debemos guardarla con un nombre distinto para no perder la paleta original (para tenerla de copia de seguridad, nunca se sabe), y para ello pulsaremos en este botón del menú superior izquierdo.

Ahora que tenemos la nueva paleta, vamos a insertarla en el narc. Nos vamos de nuevo a Tinke, seleccionamos el archivo 74 pero esta vez pulsamos en Cambiar archivo. Seleccionamos la nueva paleta que hemos editado con ConsoleTool, y ya estaría insertado el nuevo archivo con los colores del icono que usaremos a continuación. Para asegurarnos de que, efectivamente, tiene los nuevos colores, podemos pulsar en el botón Ver, luego en 'Display all palettes', y se nos abrirá una ventana en la que veremos las 3 paletas que contiene el archivo, y nuestros tonos rosas en la tercera de ellas.


Ahora vamos a insertar la imagen de nuestro icono. Seguimos en Tinke, y nos vamos al archivo 236 del narc. Veremos que es un archivo .bin comprimido. Dentro está la imagen del icono ???. Lo pulsamos, y le damos al botón Descomprimir. Ahora, como ya hemos abierto anteriormente la paleta para trabajar con ella y revisar que estaba correctamente insertada, pulsaremos en el archivo resultante que acabamos de descomprimir (el que tiene este icono ) y pulsamos en el botón Ver.
Se nos mostrará una imagen (aunque no lo parezca) a la que debemos editar el tamaño usando Tinke para que se muestre como debería verse.
Cambiamos los valores del largo y el ancho de la imagen, y el número de paleta que el icono está usando, por los que os muestro aquí.

Y ya se debería de poder ver bien el icono ??? con los colores que nosotros elegimos para el tipo Hada. Ahora solo queda insertar nuestro icono. Es tan simple como pulsar en el botón Import, seleccionar nuestra imagen, y ya estaría.
Recordad que el icono debe medir exactamente lo que he especificado en la imagen de arriba, 32x16. Eso sí, hemos cambiado los 3 colores del icono, pero no los de las letras, por lo que si vais a insertar un icono hecho por vosotros debe usar los mismos 2 colores que usan las letras de los iconos originales para formar la palabra Fairy.
Ahora que ya tenemos nuestro nuevo icono insertado, queda un último paso MUY IMPORTANTE que a la mayoría de la gente inexperta le suele pasar, y es volver a dejarlo todo como estaba, me explico. Para poder acceder a todos estos archivos hemos tenido que descomprimirlos, ¿no? Pues ahora hay que volver a comprimirlos de nuevo, o de lo contrario no se habrá guardado ningún cambio que hayamos hecho, o lo que es peor, puede afectar al resto de archivos si guardamos la ROM sin haberlo dejado todo como estaba al principio porque habremos alterado el tamaño de los archivos gráficos que la ROM usa para trabajar.
Así que pulsamos en el archivo 'pl_batt_obj_236.bin', pero esta vez lo haremos con el que tiene este icono y pulsamos en Comprimir. Hacemos lo mismo con el archivo principal del propio narc, y ya podremos guardar la ROM (con otro nombre, nunca sobreescribas, por si acaso) y ver qué tal ha quedado.



Ahora vamos con el marco de los ataques

Ya sabéis, este marco...


Esto es ligeramente más sencillo de hacer, porque os dejaré el código que debéis usar para cambiar los colores del tipo ??? por los del nuevo tipo Hada, pero también dejaré la explicación de cómo funciona el código para que vosotros mismos podáis editar los colores que yo he usado si así lo deseáis.
Lo primero es irnos a Tinke, abrir la ROM, y con el buscador tenemos que encontrar el Overlay9 11, tal y como muestro aquí.


Ahora debemos seleccionar el overlay9_11, que debería ser el primero de la lista que ha aparecido a la izquierda, y pulsamos en Extraer.
Y con un editor hexadecimal, lo abrimos, y buscamos la línea del offset 290.


Pinchad al comienzo de la línea, en el 7A, y sobrescribid toda la línea con este código, usando Ctrl + B, ya que si usáis Ctrl +V lo que haréis será insertar más datos en el overlay, en lugar de sobrescribir los que ya hay, y lo último que queremos es expandir el archivo y que luego deje de funcionar por cambiarle el tamaño.
Guardad los cambios cuando hayáis terminado.

Código:
DF 7E 3F F2 1E 6A DD 59 5B CD 17 C1 D6 B4 3F FB
Este código es la nueva paleta asignada a los ataques de tipo ???, que ahora pasan a ser de tipo Hada, y está formada por 8 colores.
:​
Cada 4 bytes es un color de la paleta, cuyos valores hexadecimales han sido convertidos a Little Endian, es decir, que los valores del primer color (por ejemplo) son en realidad 7E DF, pero hay que pasarlos a little endian para que el juego los reconozca como debe. Y así con los demás colores. Si quieres saber cómo convertir un color RGB a hexadecimal para poder pasarlo a Little Endian y así poder insertar tus propios colores a la paleta, puedes usar esta herramienta.
Ahora que ya tenemos la nueva paleta de colores editada para nuestra movebox del tipo Hada, solo nos quedará volver a insertar el overlay en su sitio. Volvemos a Tinke, seleccionamos de nuevo el Overlay9_11, y pulsamos en Cambiar archivo. Seleccionamos nuestro overlay editado, y una vez insertado ya podremos guardar la ROM y ver que funciona perfectamente. Recordad que debéis editar los pokémon que vayan a ser de tipo Hada y los ataques con otra herramienta (podéis usar el editor de ataques que contiene DS PokeHack Studio. Buscáis el ataque al que queréis cambiar el tipo, lo ponéis en el tipo ???, le dais a Save, y luego a Ok. Guardáis la ROM con un nombre distinto, y ya estaría).

Resultado


Con esto ya queda totalmente explicado cómo insertar por completo el tipo Hada en Pokemon Platinum USA.
No dejo links para descargar ninguna de las herramientas mencionadas porque las tenéis todas en la web de Whack a Hack.

Un placer haber colaborado en esta investigación.
 
Arriba