Animal Crossing Developer Mode Ingeniería inversa

Usando el código en un GameCube real

El verano pasado, comencé la ingeniería inversa Animal Crossing para GameCube. Quería explorar la posibilidad de crear mods para este juego. Además, quería documentar el proceso para crear tutoriales para personas interesadas en piratear ROM e ingeniería inversa. En esta publicación, hablaré sobre las características de depuración del desarrollador que permanecieron en el juego, y también compartiré cómo descubrí combinaciones de trucos que se pueden usar para desbloquearlas.

new_Debug_mode


Al estudiar los símbolos de depuración restantes, noté los nombres de las funciones y variables que contenían la palabra "depuración", y decidí que sería interesante ver si quedaba alguna funcionalidad de depuración en el juego. Si logro activar las funciones de depuración o desarrollo, esto me ayudará en el proceso de creación de modificaciones.

La primera función que noté fue new_Debug_mode . Se llama mediante la función de entry , que comienza inmediatamente después de que finaliza la pantalla del logotipo de Nintendo. Todo lo que hace es colocar la estructura de bytes 0x1C94 y guardar un puntero en ella.

Después de que se llama en la entry en la estructura alojada en el desplazamiento 0xD4 inmediatamente antes de llamar a mainproc valor 0.


Para ver qué sucede cuando el valor no es cero, 80407C8C instrucción li r0, 0 en 80407C8C , reemplazándola por li r0, 1 . Los bytes sin procesar de la instrucción li r0, 0 son 38 00 00 00 donde el valor asignado se encuentra al final de la instrucción, por lo que podría reemplazar los bytes con 38 00 00 01 y obtener li r0, 1 . Como una forma más confiable de crear instrucciones, puede usar algo como kstool :

$ kstool ppc32be "li 0, 1"
li 0, 1 = [ 38 00 00 01 ]


En el emulador Dolphin, este parche se puede aplicar yendo a la pestaña "Parches" en las propiedades del juego e ingresándolo de la siguiente manera:


Después de asignar el valor 1, apareció un gráfico interesante en la parte inferior de la pantalla:



Parecía un indicador de rendimiento: las pequeñas barras en la parte inferior de la pantalla aumentaron o disminuyeron. (Más tarde, cuando miré los nombres de las funciones que dibujan este gráfico, descubrí que en realidad muestran las métricas de uso de CPU y memoria).

Fue genial, pero no particularmente útil. Después de asignar el valor 1, mi ciudad dejó de cargarse, por lo que no se pudo hacer nada más aquí.

Modo Zuru


Nuevamente comencé a buscar otras referencias a las funciones de depuración, y varias veces me encontré con algo llamado "modo zuru". Las ramas de bloques de código con funcionalidad de depuración a menudo verificaban la variable zurumode_flag .

función game_move_first

zzz_LotsOfDebug (el nombre que se me ocurrió) en la función game_move_first que se muestra arriba solo se llama cuando zurumode_flag no zurumode_flag igual a cero.

Buscando funciones asociadas con este valor, encontré estas:

  • zurumode_init
  • zurumode_callback
  • zurumode_update
  • zurumode_cleanup

A primera vista, su propósito es misterioso, hacen malabarismos con los bits de una variable llamada osAppNMIBuffer .

Así es como se veía el trabajo de estas funciones a primera vista:

zurumode_init


  • Establece zurumode_flag en 0
  • Comprueba varios bits en osAppNMIBuffer
  • Guarda un puntero a la función zurumode_callback en la estructura padmgr
  • Llamadas zurumode_update

zurumode_update


  • Comprueba varios bits en osAppNMIBuffer
  • Dependiendo del valor de estos bits, zurumode_flag actualizaciones de zurumode_flag
  • Imprime una cadena de formato en la consola del sistema operativo.

Esto suele ser útil para dar contexto al código, pero había muchos caracteres no imprimibles en la línea. El único texto reconocible fue "zurumode_flag" y "% d".

cadena de formato del modo zuru

Suponiendo que podría ser texto japonés con codificación de caracteres multibyte, pasé la cadena a través de la herramienta de reconocimiento de codificación y descubrí que la cadena estaba codificada con Shift-JIS. En la traducción, la línea simplemente significaba "El valor de zurumode_flag ha cambiado de% d a% d". Esto no nos da mucha información nueva, pero ahora sabemos que se usa Shift-JIS: en archivos binarios y tablas de filas hay muchas más líneas en esta codificación.

zurumode_callback


  • Llamadas zerumode_check_keycheck
  • Comprueba varios bits en osAppNMIBuffer
  • El valor zurumode_flag se zurumode_flag
  • Llamadas zurumode_update

zerumode_check_keycheck hasta que nos conocimos debido a una ortografía diferente ... ¿qué es?

zerumode_check_keycheck

Una gran función compleja que hace mucho más trabajo en bits con valores sin nombre.

En este punto, decidí dar un paso atrás y estudiar otras funciones y variables de depuración, porque no estaba seguro de la importancia del modo zuru. Además, no entendí qué significa "verificación de clave" aquí. ¿Es posible que esta sea una clave criptográfica?

Volver a la depuración


Alrededor de este tiempo, noté un problema con mi forma de cargar símbolos de depuración en la IDA. El archivo foresta.map en el disco del juego contiene muchas direcciones y nombres de funciones y variables. Al principio, no vi que las direcciones para cada sección comenzaran nuevamente desde cero, así que escribí un script simple que agrega una entrada de nombre para cada línea del archivo.

Escribí nuevos scripts de IDA para arreglar la carga de tablas de símbolos para diferentes secciones del programa: .text , .rodata , .data y .bss . La sección .text contiene todas las funciones, por lo que esta vez, cuando configuré el nombre, el script reconocería automáticamente las funciones en cada dirección.

En las secciones de datos, ahora creó un segmento para cada objeto binario (por ejemplo, m_debug.o , que se suponía que era un código compilado para algo llamado m_debug ), y estableció el espacio y los nombres para cada pieza de datos.

Esto me dio mucha más información, pero tuve que configurar manualmente el tipo de datos para cada pieza de datos, porque definí cada objeto de datos como una simple matriz de bytes. (Mirando hacia atrás, entiendo que sería mejor suponer que los fragmentos de 4 bytes contenían enteros de 32 bits, porque había muchas, y muchas, contenían direcciones de funciones y datos importantes para construir referencias cruzadas).

Al estudiar el nuevo segmento .bss para m_debug_mode.o , encontré varias variables de la forma quest_draw_status y event_status . Esto es interesante porque quería información útil, no solo un gráfico de rendimiento, que se mostrara en modo de depuración. Afortunadamente, a partir de estos registros de datos hubo referencias cruzadas a una gran parte del código que verifica debug_print_flg .

Usando un depurador en el emulador Dolphin, establecí un punto de interrupción en la ubicación de la función donde debug_print_flg verificó 8039816C (en 8039816C ) para comprender cómo funciona esta verificación. Pero el programa nunca pasó a este punto de quiebre.

Veamos por qué sucede esto: game_debug_draw_last llama a esta función. ¿Adivina qué valor se verifica antes de su llamada condicional? zurumode_flag ! Que diablos esta pasando

comprobar zurumode_flag

Establecí un punto de interrupción en este control ( 80404E18 ) y funcionó de inmediato. El valor de zurumode_flag era cero, por lo que en la ejecución normal, el programa habría perdido la llamada a esta función. En su lugar, inserté una instrucción de bifurcación NOP (la reemplacé con una instrucción que no hace nada) para verificar qué sucede cuando se llama a la función.

En el depurador Dolphin, esto se puede hacer pausando el juego, haciendo clic derecho en las instrucciones y seleccionando "Insertar nop":

Depurador de delfines

No paso nada Luego verifiqué lo que estaba sucediendo dentro de la función y descubrí otra construcción de ramificación que eludió todo lo interesante que sucedía en 803981a8 . También inserté NOP, y la letra "D" apareció en la esquina superior derecha de la pantalla.

Modo de depuración letra D

En esta función en 8039816C (lo llamé zzz_DebugDrawPrint ), todavía hay un montón de código interesante, pero no se llama. Si observa esta función en forma de gráfico, puede ver que hay una serie de operadores de ramificación que omiten bloques de código en toda la función:

Sucursales en zzz_DebugDrawPrint

Después de insertar NOP en lugar de varias otras construcciones de ramificación, comencé a ver varias cosas interesantes en la pantalla:

Se imprimen más cosas de depuración

La siguiente pregunta fue cómo activar esta funcionalidad de depuración sin cambiar el código.

Además, en algunas construcciones de rama, zurumode_flag ocurre nuevamente en esta función de dibujo de depuración. zurumode_update otro parche para que en zurumode_update bandera zurumode_update siempre zurumode_flag asignado el valor 2, porque cuando no se compara con 0, se compara específicamente con el valor 2.

Después de reiniciar el juego, vi en la esquina superior derecha de la pantalla un mensaje "mensaje". no ".

visualización del número de mensaje

El número 687 es el identificador de registro del mensaje mostrado más recientemente. Lo verifiqué usando el programa de visualización de tablas que escribí al comienzo del análisis, pero también puede verificarlo usando el editor de tablas de cadenas con una GUI completa , que escribí para piratear ROM. Así es como se ve la publicación en el editor:

Mensaje 687 en el editor de tabla de cadenas

En este punto, quedó claro que el estudio del modo zuru ya no se eliminaba, está directamente relacionado con las funciones de depuración del juego.

Volver al modo Zuru nuevamente


zurumode_init inicializa varias cosas:

  • 0xC(padmgr_class) asigna el valor de la dirección zurumode_callback
  • 0x10(padmgr_class) asigna el valor de dirección de padmgr_class
  • 0x4(zuruKeyCheck) asigna el valor del último bit en la palabra cargada desde 0x3C(osAppNMIBuffer) .

Descubrí qué padmgr , una abreviatura de "gamepad manager". Esto significa que puede haber una combinación especial de teclas (botones) que se pueden ingresar en el gamepad para activar el modo zuru, o algún tipo de dispositivo de depuración o función de la consola del desarrollador que se puede usar para enviar una señal para activarlo.

zurumode_init se ejecuta solo en el primer arranque del juego (cuando se presiona el botón de reinicio, no funciona).

Después de establecer un punto de interrupción en la dirección 8040efa4 , en la que se asigna el valor 0x4(zuruKeyCheck) , podemos ver que al cargar sin presionar teclas, el valor se establece en 0. Si lo reemplaza por 1, sucede algo interesante:

Pantalla de título con modo zuru

La letra "D" aparece nuevamente en la esquina superior derecha (esta vez es verde, no amarilla), y también se muestra información de ensamblaje:

[CopyDate: 02/08/01 00:16:48 ]
[Date: 02-07-31 12:52:00]
[Creator:SRD@SRD036J]


Un parche que siempre establece 0x4(zuruKeyCheck) en 1 al principio tiene este aspecto:

8040ef9c 38c00001

Esta parece ser la forma correcta de inicializar el modo zuru. Después de esto, pueden ser necesarias varias acciones para lograr la visualización de cierta información de depuración. Al comenzar el juego, dar un paseo y hablar con un aldeano, no veremos ninguno de los mensajes mencionados anteriormente (excepto la letra "D" en la esquina).

Los sospechosos más probables son zurumode_update y zurumode_callback .

zurumode_update


zurumode_update llama primero en zurumode_init y luego se llama constantemente por zurumode_callback .

Comprueba nuevamente el último bit 0x3C(osAppNMIBuffer) y luego, en función de este valor, actualiza zurumode_flag .

Si el bit es cero, la bandera se pone a cero.

Si no, se ejecuta la siguiente instrucción, con el valor completo 0x3c(osAppNMIBuffer) siendo r5 :

extrwi r3, r5, 1, 28

Extrae el bit 28 de r5 y lo almacena en r3 .

Luego se agrega 1 al resultado, es decir, el resultado final es siempre 1 o 2.

Luego, zurumode_flag compara con el resultado anterior, dependiendo de cuántos de los bits 28 y últimos se establecen en 0x3c(osAppNMIBuffer) : 0, 1 o 2.

Este valor se escribe en zurumode_flag . Si no cambia nada, la función sale y devuelve el valor del indicador actual. Si cambia el valor, se ejecuta una cadena de bloques de código mucho más compleja.

Se muestra un mensaje en japonés: el mismo "valor de zurumode_flag cambió de% d a% d", del que hablamos anteriormente.

Luego se llama a una serie de funciones con diferentes argumentos, dependiendo de si el indicador se ha convertido en cero o no. El código de ensamblador de esta parte es monótono, por lo que mostraré su pseudocódigo:

 if (flag_changed_to_zero) { JC_JUTAssertion_changeDevice(2) JC_JUTDbPrint_setVisible(JC_JUTDbPrint_getManager(), 0) } else if (BIT(nmiBuffer, 25) || BIT(nmiBuffer, 31)) { JC_JUTAssertion_changeDevice(3) JC_JUTDbPrint_setVisible(JC_JUTDbPrint_getManager(), 1) } 

Tenga en cuenta que si el indicador es cero, el argumento 0 se pasa a JC_JUTDbPrint_setVisible.

Si el indicador no es igual a cero, y el bit 25 o el bit 31 se establecen en 0x3C(osAppNMIBuffer) , entonces setVisible pasa al argumento 1.

Esta es la primera clave para activar el modo zuru: el último bit 0x3C(osAppNMIBuffer) debe establecerse en 1 para mostrar la información de depuración y establecer zurumode_flag valor distinto de cero.

zurumode_callback


zurumode_callback se encuentra en 8040ee74 y probablemente es llamado por una función relacionada con el gamepad. Después de insertar un punto de interrupción en el depurador Dolphin, la pila de llamadas nos muestra que en realidad se está llamando desde padmgr_HandleRetraceMsg .

Una de sus primeras acciones fue ejecutar zerucheck_key_check . Esta función es compleja, pero parece que, en general, está diseñada para leer y actualizar el valor de zuruKeyCheck . Antes de pasar a la función de comprobación de teclas, decidí comprobar cómo se usa este valor en el resto de la función de devolución de llamada.

Luego, nuevamente comprueba algunos bits en 0x3c(osAppNMIBuffer) . Si se establece el bit 26, o si se establece el bit 25 y padmgr_isConnectedController(1) devuelve un valor distinto de cero, ¡el último bit en 0x3c(osAppNMIBuffer) en 1!

Si ninguno de estos bits está establecido, o el bit 25 está establecido, pero padmgr_isConnectedController(1) devuelve 0, entonces la función verifica si el byte en la dirección 0x4(zuruKeyCheck) es igual a cero. Si es igual, restablece el último bit en el valor original y lo vuelve a 0x3c(osAppNMIBuffer) en 0x3c(osAppNMIBuffer) . De lo contrario, aún establece el último bit en 1.

En pseudocódigo, se ve así:

 x = osAppNMIBuffer[0x3c] if (BIT(x, 26) || (BIT(x, 25) && isConnectedController(1)) || zuruKeyCheck[4] != 0) { osAppNMIBuffer[0x3c] = x | 1 // set last bit } else { osAppNMIBuffer[0x3c] = x & ~1 // clear last bit } 

Después de eso, si el bit 26 no está establecido, la función procede a llamar a zurumode_update y luego sale.

Si se establece el bit, entonces si 0x4(zuruKeyCheck) no 0x4(zuruKeyCheck) igual a cero, entonces carga una cadena de formato en la que muestra lo siguiente: "ZURU% d /% d".

Para resumir el subtotal


Esto es lo que pasa:

padmgr_HandleRetraceMsg llama a zurumode_callback . Supongo que este "mensaje de seguimiento de manejo" significa que simplemente escanea las pulsaciones de teclas del controlador. Con cada escaneo, puede causar una serie de devoluciones de llamada diferentes.

Cuando zurumode_callback ejecuta zurumode_callback verifica las pulsaciones de teclado (botones) actuales. Parece que está comprobando un botón específico o una combinación de botones.

El último bit en el Buffer NMI se actualiza dependiendo de los bits específicos en su valor actual, así como del valor de uno de los bytes 0x4(zuruKeyCheck) ).

Luego se zurumode_update y comprueba este bit. Si es 0, el indicador de modo zuru se establece en 0. Si es 1, el indicador de modo cambia a 1 o 2, dependiendo de si el bit 28 está configurado.

Hay tres formas de activar el modo zuru:

  1. El bit 26 se establece en 0x3C(osAppNMIBuffer)
  2. El bit 25 se establece en 0x3C(osAppNMIBuffer) y el controlador está conectado al puerto 2
  3. 0x4(zuruKeyCheck) no 0x4(zuruKeyCheck) cero

osAppNMIBuffer


Interesado en lo que significa osAppNMIBuffer , comencé a buscar "NMI" y encontré enlaces en el contexto de Nintendo a "interrupción no enmascarable". Resulta que el nombre de esta variable se menciona por completo en la documentación del desarrollador para Nintendo 64:

osAppNMIBuffer es un búfer de 64 bytes que se borra al reiniciar en frío. Si el sistema se reinicia debido a NMI, el estado de este búfer no cambia.

De hecho, esta es una pequeña porción de memoria guardada durante un reinicio "suave" (con el botón de reinicio). El juego puede usar este búfer para almacenar datos mientras la consola está en la red. El Animal Crossing original se lanzó en Nintendo 64, por lo que es lógico que algo similar debería haber aparecido en el código.

Si vamos al archivo binario boot.dol (todo lo que se muestra arriba estaba en foresta.rel ), entonces su función main tiene muchos enlaces a osAppNMIBuffer . Una mirada rápida muestra que hay una serie de comprobaciones que pueden conducir a 0x3c(osAppNMIBuffer) valores de diferentes bits 0x3c(osAppNMIBuffer) utilizando operaciones OR.

Los siguientes valores de operando OR pueden ser interesantes:

  • Bit 31: 0x01
  • Bit 30: 0x02
  • Bit 29: 0x04
  • Bit 28: 0x08
  • Bit 27: 0x10
  • Bit 26: 0x20

Recordamos que los bits 25, 26 y 28 son especialmente interesantes: 25 y 26 determinan si el modo zuru está activado, y el bit 28 determina el nivel de bandera (1 o 2).
El bit 31 también es interesante, pero parece que cambia según los valores de los demás.

Bit 26

En primer lugar: en la dirección 800062e0 hay una instrucción ori r0, r0, 0x20 con un valor de búfer de 0x3c . Establece el bit 26, que siempre activa el modo zuru.

Bit de ajuste 26

Para establecer el bit, el octavo byte devuelto por DVDGetCurrentDiskID debe ser 0x99 . Este identificador se encuentra al comienzo de la imagen de disco del juego y se carga en la memoria al 80000000 . En un lanzamiento regular del juego, el ID se ve así:

47 41 46 45 30 31 00 00 GAFE01..

Reemplazando el último byte del identificador con un 0x99 para 0x99 , obtenemos la siguiente imagen al iniciar el juego:

ID de la versión del juego 0x99

Y lo siguiente se muestra en la consola del sistema operativo:

06:43:404 HW\EXI_DeviceIPL.cpp:339 N[OSREPORT]: ZURUMODE2 ENABLE
08:00:288 HW\EXI_DeviceIPL.cpp:339 N[OSREPORT]: osAppNMIBuffer[15]=0x00000078


Todos los demás parches se pueden eliminar, después de lo cual la letra D aparece nuevamente en la esquina superior derecha de la pantalla, pero ya no se activan los mensajes de depuración.

Bit 25

El bit 25 se usa junto con la verificación del puerto del controlador 2. ¿Qué hace que se encienda?

Bit 25 y 28

Resulta que debería usar la misma verificación que para el bit 28: la versión debe ser mayor o igual a 0x90 . Si se establece el bit 26 (ID es 0x99 ), ambos bits también se establecerán y el modo zuru seguirá activado.

Sin embargo, si la versión está en el rango de 0x90 a 0x98 , entonces el modo zuru no se activa instantáneamente. zurumode_callback la comprobación realizada en zurumode_callback : el modo se habilitará solo si se establece el bit 25 y padmgr_isConnectedController(1) devuelve un valor distinto de cero.

Después de que el controlador está conectado al puerto 2 (el argumento isConnectedController tiene indexación cero), se activa el modo zuru. La letra D y la información sobre el ensamblaje aparecen en la pantalla inicial, y podemos ... ¡controlar la visualización de la depuración utilizando los botones del segundo controlador!

Algunos botones realizan acciones que no solo cambian la pantalla, sino que también, por ejemplo, aumentan la velocidad del juego.

zerucheck_key_check


El último misterio sigue siendo 0x4(zuruKeyCheck) . Resulta que este valor se actualiza con una enorme función compleja que mostré arriba:

zerumode_check_keycheck

Usando el depurador del emulador Dolphin, pude determinar que el valor verificado por esta función es un conjunto de bits correspondientes a las pulsaciones de botón en el segundo controlador.

El seguimiento de los clics de los botones se almacena en un valor de 16 bits a 0x2(zuruKeyCheck) . Cuando el controlador no está conectado, el valor es 0x7638 .

Se descargan 2 bytes que contienen las banderas de las pulsaciones del botón del controlador y luego se actualizan al comienzo de zerucheck_key_check . El nuevo valor se pasa con el registro r4 función padmgr_HandleRetraceMsg cuando llama a la función de devolución de llamada.

clave de verificación final

Cerca del final de zerucheck_key_check hay otro lugar donde 0x4(zuruKeyCheck) actualiza 0x4(zuruKeyCheck) . , r3 , r3 , .

8040ed88 r4 0x4(zuruKeyCheck) . XOR- 1. — ( — ) 0 1. ( 0,
XOR 1 1. 1, 0. . XOR.)

clave de verificación final

Anteriormente, cuando estudié los valores en la memoria, no noté este comportamiento, pero intentaré romper esta instrucción en el depurador para comprender lo que está sucediendo. El valor original se carga en 8040ed7c.

Sin tocar los botones del controlador, no llegaré a este punto de interrupción en la pantalla inicial. Para entrar en este bloque de código, el valor r5debe ser igual 0xbantes de la instrucción de bifurcación que va antes del punto de interrupción ( 8040ed74). De los muchos caminos diferentes que conducen a este bloque, solo uno asigna un r5valor 0xbfrente a él en la dirección 8040ed68.

Configuración de r5 a 0xb

Tenga en cuenta que para alcanzar el bloque que asigna el r5valor 0xB, el valor r0debe ser igual justo antes 0x1000. Siguiendo los bloques en la cadena hasta el comienzo de la función, podemos ver todas las restricciones necesarias para lograr este bloque:

  • 8040ed74: el valor r5debe ser igual0xB
  • 8040ed60: el valor r0debe ser igual0x1000
  • 8040ebe8: el valor r5debe ser igual0xA
  • 8040ebe4: el valor r5debe ser menor0x5B
  • 8040eba4: el valor r5debe ser mayor0x7
  • 8040eb94: el valor r6debe ser 1
  • 8040eb5c: el valor r0no debe ser 0
  • 8040eb74: los valores del botón del puerto 2 deberían cambiar

Rastreando la ruta del código

Aquí hemos llegado al punto en el que se cargan los viejos valores de los botones y se guardan los nuevos valores. Luego vienen un par de operaciones aplicadas a los valores nuevos y antiguos: la operación XOR marca todos los bits que han cambiado entre los dos valores. Luego, la operación AND enmascara la nueva entrada para establecer todos los bits que no están configurados actualmente en el estado 0. El resultado es un conjunto de bits nuevos (pulsaciones de botón) en el nuevo valor. Si no está vacío, estamos en el camino correcto. Para marcar la diferencia , el cuarto de los botones de seguimiento de 16 bits debe cambiar. Después de insertar un punto de interrupción después de la operación XOR / AND, descubrí que el botón INICIO activa este estado. La siguiente pregunta es cómo lograr que sea inicialmente igual

old_vals = old_vals XOR new_vals
old_vals = old_vals AND new_vals


r0

r00x1000

r50xA. r5y r6se cargan desde 0x0(zuruKeyCheck)el comienzo de la función de prueba clave y se actualizan más cerca del final, cuando no ingresamos en el bloque de código que incluye 0x4(zuruKeyCheck).

Hay varios lugares antes de esto donde r5se asigna el valor 0xA:

  • 8040ed50
  • 8040ed00
  • 8040ed38

8040ed38

  • 8040ed34: el valor r0debe ser igual 0x4000( se presiona el botón B)
  • 8040ebe0: el valor r5debe ser igual0x5b
  • 8040eba4: el valor r5debe ser mayor0x7
  • entonces todo sigue como antes ...

r5 debería comenzar con 0x5b

8040ed00

  • 8040ecfc: el valor r0debe ser igual 0xC000(se presionan A y B)
  • 8040ebf8: el valor r5debe ser> = 9
  • 8040ebf0: el valor r5debe ser inferior a 10
  • 8040ebe4: el valor r5debe ser menor0x5b
  • 8040eba4: r5debería ser más0x7
  • entonces todo sigue como antes ...

r5 debería comenzar a las 9

8040ed50

  • 8040ed4c: el valor r0debe ser igual 0x8000(botón A presionado)
  • 8040ec04: el valor r5debe ser menor0x5d
  • 8040ebe4: el valor r5debe ser mayor0x5b
  • 8040eba4: el valor r5debe ser mayor0x7
  • entonces todo sigue como antes ...

r5debería comenzar con 0x5c

Parece que hay algún tipo de estado entre las pulsaciones de teclas, después de lo cual debe ingresar una cierta secuencia de combos desde los botones, que termina presionando START. Parece que A y / o B deberían ir justo antes de INICIAR.

Si traza la ruta del código que establece el r5valor en 9, entonces surge un patrón: r5- es un valor creciente que puede aumentar cuando se encuentra un r0valor adecuado o cero. Los casos más extraños cuando este no es un valor en el rango de 0x0a0xB, se produce cuando se procesan pasos con varios botones, por ejemplo, cuando se presionan simultáneamente A y B. Una persona que intenta ingresar a este combo generalmente no puede presionar ambos botones exactamente al mismo tiempo mientras rastrea el gamepad, por lo que debe procesar el botón que se presiona. el primero

Continuamos explorando diferentes rutas de código:

  • r5toma el valor 9 cuando se presiona DERECHA en la dirección 8040ece8.
  • r5toma el valor 8 cuando se presiona el botón derecho C en la dirección 8040eccc.
  • r5toma el valor 7 cuando se presiona el botón izquierdo C en la dirección 8040ecb0.
  • r5toma el valor 6 cuando se presiona IZQUIERDA en la dirección 8040ec98.
  • r5toma el valor 5 (y r6 toma el valor 1) cuando se presiona ABAJO en la dirección 8040ec7c.
  • r5 4, C 8040ec64 .
  • r5 3, C 8040ec48 .
  • r5 2, UP 8040ec30 .
  • r5 1 ( r6 1), Z 8040ec1c .

:

Z, UP, C-DOWN, C-UP, DOWN, LEFT, C-LEFT, C-RIGHT, RIGHT, A+B, START

Z : Z, 0x2030 : ( 0x10 0x20 ). , UP/DOWN/LEFT/RIGHT — D-pad, .

-


:

  1. Sostenga los parachoques L + R y presione Z
  2. D-up
  3. C-ABAJO
  4. C-up
  5. D-down
  6. D-izquierda
  7. C-izquierda
  8. C-DERECHA
  9. D-DERECHA
  10. A + B
  11. START

Funciona! Conecte el controlador al segundo puerto e ingrese el código, después de lo cual aparece la información de depuración. Después de eso, puede comenzar a presionar los botones en el segundo (o incluso tercero) controlador para realizar varias acciones.

Este combo funcionará sin parchear el número de versión del juego. Incluso se puede usar en una copia minorista regular del juego sin herramientas de trampa o mods de consola. Volver a ingresar combos desactiva el modo zuru.

Usando el código en un GameCube real

«ZURU %d/%d» zurumode_callback , , ID 0x99 (, -). — , r5 . 1, , , r6 1.

, , , . , — . , - , .

La pantalla negra que se muestra al presionar Z es una consola para mostrar mensajes de depuración, específicamente para aspectos de bajo nivel, como la asignación de memoria, errores de almacenamiento dinámico y otras malas excepciones. Por comportamiento, fault_callback_scrollse puede suponer que se usa para mostrar estos errores antes de que el sistema se reinicie. No inicia ninguno de estos errores, pero puede hacer que impriman un par de caracteres basura con varios NOP. Creo que en el futuro será muy útil para mostrar sus propios mensajes de depuración:

JUTConsole personajes basura

Después de hacer todo esto, descubrí que otras personas ya conocen el modo de depuración parcheando el ID de la
versión 0x99: https://tcrf.net/Animal_Crossing#Debug_Mode . (También hay buenas notas en el enlace que indican varios mensajes, y habla sobre otras cosas que se pueden hacer con el controlador en el puerto 3.) Sin embargo, hasta donde sé, nadie ha publicado la combinación de trucos todavía.

Eso es todo Hay otras características del desarrollador que me gustaría explorar, como la pantalla de depuración de la tarjeta y la pantalla de selección del emulador NES, y cómo activarlas sin usar parches.

Pantalla de selección de mapa


Además, publicaré artículos sobre ingeniería inversa de sistemas de diálogo, eventos y misiones con el objetivo de crear mods.

Source: https://habr.com/ru/post/es413967/


All Articles