blog di jamchamb
L’estate scorsa ho iniziato il reverse engineering di Animal Crossing per GameCube per esplorare la possibilità di creare mods per il gioco. Ho anche voluto documentare il processo per creare tutorial per le persone interessate all’hacking della ROM e al reverse engineering. In questo post esploro le caratteristiche di debug degli sviluppatori che sono ancora rimaste nel gioco, e come ho scoperto una cheat combo che può essere usata per sbloccarle.
new_Debug_mode
Guardando alcuni simboli di debug rimasti, ho notato funzioni e nomi di variabili che contengono la parola “debug”, e ho pensato che sarebbe stato interessante vedere quali funzionalità di debug potrebbero essere rimaste nel gioco. Se ci fossero funzioni di debug o per sviluppatori che potrei attivare, potrebbe anche aiutare nel processo di creazione di mods.
La prima funzione a cui ho dato un’occhiata è new_Debug_mode
.È chiamata dalla funzione entry
, che viene eseguita subito dopo la fine della schermata Nintendotrademark. Tutto ciò che fa è allocare una struttura di 0x1C94
byte e salvare il suo puntatore.
Dopo che viene chiamata in entry
, un valore di 0 è impostato all’offset 0xD4
nella struttura allocata, proprio prima che mainproc
sia chiamato.
Per vedere cosa succede quando il valore non è zero, ho patchato l’istruzione li r0, 0
a80407C8C
a li r0, 1
. I byte grezzi per l’istruzione li r0, 0
sono 38 00 00 00
, dove il valore assegnato è alla fine dell’istruzione, quindi puoi semplicemente cambiarlo in 38 00 00 01
per ottenere li r0, 1
. Per un modo più affidabile di assemblare le istruzioni, potreste usare qualcosa come kstool
:
Potete applicare questa patch nell’emulatore Dolphin andando nella scheda “Patches” delle proprietà del gioco e inserendola così:
Impostare questo valore a 1 faceva apparire un interessante grafico in fondo allo schermo:
Sembrava un performance meter, con le piccole barre in fondo allo schermo che crescevano e si riducevano. (Più tardi, quando ho cercato i nomi delle funzioni che disegnano il grafico, ho scoperto che in effetti mostrano le metriche per l’uso della CPU e della memoria). Impostando un valore superiore a 1, mytown non si caricava più, quindi non sembrava che ci fosse molto altro da fare con questo.
Modalità zuru
Ho iniziato a guardare in giro altri riferimenti a cose relative al debug, e ho visto qualcosa chiamato “modalità zuru” apparire alcune volte. Le diramazioni verso blocchi di codice che avevano funzionalità di debug spesso controllavano una variabile chiamata zurumode_flag
.
Nella funzione game_move_first
raffigurata sopra, zzz_LotsOfDebug
(un nome che ho inventato) viene chiamata solo se zurumode_flag
non è zero.
Cercando le funzioni relative a questo valore si ottengono le seguenti:
zurumode_init
zurumode_callback
zurumode_update
zurumode_cleanup
A prima vista sono tutte un po’ oscure, che girano intorno a vari bit in offset in una variabile chiamata osAppNMIBuffer
.Ecco un primo sguardo a cosa fanno queste funzioni:
zurumode_init
- Imposta
zurumode_flag
a 0 - Controlla alcuni bit in
osAppNMIBuffer
- Memorizza un puntatore alla funzione
zurumode_callback
nella strutturapadmgr
- Chiama
zurumode_update
zurumode_update
- Controlla alcuni bit in
osAppNMIBuffer
- Aggiorna incondizionatamente
zurumode_flag
in base a questi bit - Stampa una stringa di formato alla console del SO.
Questo genere di cose è solitamente utile per dare un contesto al codice, ma c’erano un sacco di caratteri non stampabili nella stringa. L’unico testo riconoscibile era “zurumode_flag” e “%d”.
Credendo che potesse essere un testo giapponese che usava una codifica di caratteri multi-byte, ho fatto passare la stringa attraverso uno strumento di rilevamento della codifica dei caratteri e ho scoperto che era codificata Shift-JIS. La stringa tradotta significa solo “zurumode_flag hasbeen changed from %d to %d”. Questo non fornisce molte nuove informazioni, ma sapere dell’uso di Shift-JIS sì, poiché ci sono molte altre stringhe nei binari e nelle tabelle delle stringhe che usano questa codifica.
zurumode_callback
- Chiama
zerumode_check_keycheck
- Controlla alcuni bit in
osAppNMIBuffer
- Stampa il valore di
zurumode_flag
da qualche parte - Chiama
zurumode_update
zerumode_check_keycheck
che prima non compariva a causa della diversa grafia.. che cos’è?
Un’enorme funzione complessa che fa un sacco di giochetti di bit su valori senza nome.A questo punto ho deciso di fare marcia indietro e cercare altre funzioni e variabili relative al debug, poiché non ero nemmeno sicuro del significato di zuru mode. Non ero nemmeno sicuro di cosa significasse qui “key check”. Potrebbe essere una chiave crittografica?
Di nuovo al debug
In questo periodo ho notato che c’era un problema nel modo in cui ho caricato i simboli di debug in IDA. Il file foresta.map
sul disco del gioco contiene un mucchio di indirizzi e nomi per funzioni e variabili. Inizialmente non avevo notato che gli indirizzi per ogni sezione ripartivano da zero, così ho impostato un semplice script per aggiungere una voce di nome per ogni linea nel file.
Ho impostato nuovi script IDA per sistemare il caricamento della mappa dei simboli per le diverse sezioni del programma:.text
, .rodata
, .data
, e .bss
. La sezione .text
è dove ci sono tutte le funzioni, così ho impostato lo script per rilevare automaticamente le funzioni ad ogni indirizzo quando si imposta un nome questa volta.
Per le sezioni dati, l’ho impostato per creare un segmento per ogni oggetto binario (come m_debug.o
, che sarebbe il codice compilato per qualcosa chiamato m_debug
), e impostare lo spazio e i nomi per ogni pezzo di dati.Questo mi dà molte più informazioni di quelle che avevo prima, anche se ora ho dovuto definire manualmente il datatype per ogni pezzo di dati, poiché ho impostato ogni oggetto dati come un semplice array di byte. (Con il senno di poi, sarebbe stato meglio almeno assumere che ogni dato con una dimensione che è un multiplo di 4 byte contenesse numeri interi a 32 bit, poiché ce ne sono molti, e molti contengono indirizzi a funzioni e dati che sono importanti per costruire riferimenti incrociati. Queste sono interessanti perché voglio che la modalità debug mostri qualcosa di più utile del grafico delle prestazioni. Fortunatamente, c’erano riferimenti incrociati da queste voci di dati a un enorme pezzo di codice che controlla debug_print_flg
.
Utilizzando il debugger Dolphin, ho impostato un breakpoint sulla funzione dove debug_print_flg
viene controllato (a 8039816C
) per vedere come funziona il controllo. Il breakpoint non ha mai colpito.
Controlliamo perché: questa funzione è chiamata da game_debug_draw_last
. Indovinate quale valore viene controllato prima di chiamarla in modo condizionato? zurumode_flag
. Che diavolo è?
Ho impostato un breakpoint su questo controllo (80404E18
) e si è rotto immediatamente. Il valore di zurumode_flag
era zero, quindi normalmente saltava la chiamata a questa funzione. Ho eliminato l’istruzione di diramazione (l’ho sostituita con un’istruzione che non fa nulla) per vedere cosa sarebbe successo quando la funzione sarebbe stata chiamata.
Nel debugger Dolphin potete fare questo mettendo in pausa il gioco, cliccando con il tasto destro su un’istruzione e poi cliccando “Insert nop”:
Non è successo nulla. Poi ho controllato cosa sta accadendo all’interno della funzione, e ho trovato un’altra dichiarazione di diramazione che potrebbe cortocircuitare oltre tutte le cose interessanti a 803981a8
. Ho eliminato anche quella, e la lettera “D” è apparsa nell’angolo in alto a destra dello schermo.
C’era un sacco di altro codice interessante in questa funzione a 8039816C
(l’ho chiamata zzz_DebugDrawPrint
), ma nessuno di questi veniva chiamato. Se guardate il grafico di questa funzione, potete vedere che c’è una serie di diramazioni che saltano i blocchi in tutta la funzione:
NOPPANDO più di queste istruzioni di diramazione, ho iniziato a vedere diverse cose stampate sullo schermo:
La prossima domanda è come attivare queste funzioni di debug senza modificare il codice.Inoltre, zurumode_flag
appare di nuovo per alcune diramazioni fatte in questa funzione di debug draw.Ho aggiunto un’altra patch in modo che zurumode_flag
sia sempre impostato su 2 in zurumode_update
, perché di solito viene confrontato specificamente con 2 quando non viene confrontato con 0.Dopo aver riavviato il gioco, ho visto questo messaggio “msg. no” visualizzato in alto a destra dello schermo.
Il numero 687 è l’ID dell’ultimo messaggio visualizzato. Ho controllato questo usando un visualizzatore di tabelle semplici che ho fatto all’inizio, ma potete anche controllarlo con un editor di tabelle di stringhe completo di GUI che ho fatto per l’hacking della ROM. Ecco come appare il messaggio nell’editor:
A questo punto era chiaro che capire la modalità zuru non era più evitabile; era direttamente collegato alle caratteristiche di debug del gioco.
La modalità zuru rivisitata
Tornando a zurumode_init
, inizializza alcune cose:
-
0xC(padmgr_class)
è impostato all’indirizzo dizurumode_callback
-
0x10(padmgr_class)
è impostato all’indirizzo dipadmgr_class
stesso -
0x4(zuruKeyCheck)
è impostato all’ultimo bit di una parola caricata da0x3C(osAppNMIBuffer)
.
Ho cercato il significato di padmgr
ed è l’abbreviazione di “gamepad manager”. Questo suggerisce che ci potrebbe essere una speciale combinazione di tasti (pulsanti) da inserire sul gamepad per attivare la modalità zuru, o forse qualche speciale dispositivo di debug o caratteristica della console di sviluppo che potrebbe essere usata per inviare un segnale per attivarla.
zurumode_init
viene eseguito solo la prima volta che il gioco viene caricato (premere il pulsante di reset non lo attiva).
Impostare un breakpoint a 8040efa4
, dove è impostato 0x4(zuruKeyCheck)
, possiamo vedere che durante il boot senza tenere premuto alcun tasto, sarà impostato a 0. Sostituendo questo con 1 succede una cosa interessante:
La lettera “D” appare di nuovo nell’angolo in alto a destra (verde invece che giallo questa volta), e ci sono anche alcune informazioni sulla build:
Una patch per avere 0x4(zuruKeyCheck)
sempre impostato su 1 all’avvio:
8040ef9c 38c00001
Questo sembra essere il modo corretto per ottenere la modalità zuru inizializzata. Dopo di che, ci possono essere diverse azioni che dobbiamo fare per ottenere certe informazioni di debug da visualizzare. Avviando il gioco e camminando in giro e parlando con un abitante del villaggio non ha mostrato nessuna delle visualizzazioni menzionate in precedenza (oltre alla lettera “D” nell’angolo).
I probabili sospetti sono zurumode_update
e zurumode_callback
.
zurumode_update
zurumode_update
viene prima chiamato da zurumode_init
, e poi viene ripetutamente chiamato dazurumode_callback
.
Controlla nuovamente l’ultimo bit di 0x3C(osAppNMIBuffer)
e poi aggiorna zurumode_flag
in base al suo valore.
Se il bit è zero, il flag è impostato a zero.
Se no, la seguente istruzione viene eseguita con r5
che è il valore completo di 0x3c(osAppNMIBuffer)
:
Questo estrae il 28° bit da r5
e lo salva in r3
.Poi viene aggiunto 1 al risultato, così il risultato finale è sempre 1 o 2.
zurumode_flag
viene poi confrontato con il risultato precedente, a seconda di quanti del 28° e dell’ultimo bit sono impostati in 0x3c(osAppNMIBuffer)
: 0, 1, o 2.
Questo valore viene scritto in zurumode_flag
. Se non ha cambiato nulla, la funzione termina e restituisce il valore attuale della bandiera. Se cambia il valore, viene eseguita una catena di blocchi di codice molto più complessa.
Viene stampato un messaggio in giapponese: questo è il messaggio “zurumode_flag è stato cambiato da %d a %d” menzionato prima.
Poi viene chiamata una serie di funzioni con argomenti diversi a seconda che il flag sia stato cambiato a zero o no. L’assemblaggio di questa parte è noioso, quindi lo pseudocodice di esso assomiglia a questo:
Nota che se il flag è zero, JC_JUTDbPrint_setVisible
riceve un argomento di 0. Se il flag non è zero e il bit 25 o il bit 31 sono impostati in 0x3C(osAppNMIBuffer)
, alla funzionesetVisible
viene passato un argomento di 1.
Questa è la prima chiave per attivare la modalità zuru: l’ultimo bit di 0x3C(osAppNMIBuffer)
deve essere impostato a 1 per rendere visibili i display di debug e impostare zurumode_flag
a un valore diverso da zero.
zurumode_callback
zurumode_callback
è a 8040ee74
ed è probabilmente chiamato da una funzione relativa al gamepad. Impostando un breakpoint su di esso nel debugger di Dolphin, il callstack mostra che è effettivamente chiamato da padmgr_HandleRetraceMsg
.
Una delle prime cose che fa è eseguire zerucheck_key_check
. È complesso, ma nel complesso sembra leggere e poi aggiornare il valore di zuruKeyCheck
. Ho deciso di vedere come quel valore viene usato nel resto della funzione di callback prima di andare oltre nella funzione di controllo dei tasti.
Poi controlla nuovamente alcuni bit in 0x3c(osAppNMIBuffer)
. Se il bit 26 è impostato, o se il bit 25 è impostato e padmgr_isConnectedController(1)
ritorna non-zero, l’ultimo bit in 0x3c(osAppNMIBuffer)
viene impostato a 1!
Se nessuno di questi bit è impostato, o se il bit 25 è almeno impostato ma padmgr_isConnectedController(1)
restituisce 0, allora controlla se il byte a 0x4(zuruKeyCheck)
è 0. Se lo è, allora azzera l’ultimo bit nel valore originale e lo scrive di nuovo in 0x3c(osAppNMIBuffer)
.Altrimenti, imposta ancora l’ultimo bit a 1.
In pseudo-codice questo assomiglia a:
Dopo di che, se il bit 26 non è impostato, va in corto circuito per chiamare zurumode_update
e poi finisce.
Se è impostato, allora se 0x4(zuruKeyCheck)
non è zero, carica una stringa di formato dove sembra che stia per stampare: “ZURU %d/%d”.
Recap
Ecco cosa succede:
padmgr_HandleRetraceMsg
chiama il zurumode_callback
.La mia ipotesi è che “handle retrace message” significa che ha appena scansionato la pressione dei tasti sul controller. Ogni volta che esegue la scansione, potrebbe chiamare una serie di callback diversi.
Quando zurumode_callback
viene eseguito, controlla la pressione del tasto corrente (pulsante).Questo sembra controllare un pulsante specifico o una combinazione di pulsanti.
L’ultimo bit nel buffer NMI viene aggiornato in base a specifici bit nel suo valore corrente, così come il valore di uno dei zuruKeyCheck
byte (0x4(zuruKeyCheck)
).
Quindi zurumode_update
viene eseguito e controlla quel bit. Se è 0, il flag di modalità zuru sarà impostato a 0. Se è 1, il flag di modalità viene aggiornato a 1 o 2 in base al fatto che il bit 28 sia impostato.
I tre modi per attivare la modalità zuru sono:
- Il bit 26 è impostato in
0x3C(osAppNMIBuffer)
- Il bit 25 è impostato in
0x3C(osAppNMIBuffer)
, e un controller è collegato alla porta 2 -
0x4(zuruKeyCheck)
non è zero
osAppNMIBuffer
Chiedevo cosa fosse osAppNMIBuffer
, Ho iniziato cercando “NMI”, e ho trovato riferimenti a “non-maskable interrupt” nel contesto di Nintendo. Si è scoperto che l’intero nome della variabile compare anche nella documentazione per gli sviluppatori del Nintendo 64:
osAppNMIBuffer è un buffer di 64 byte che viene cancellato su un reset a freddo. Se il sistema si riavvia a causa di un NMI, questo buffer rimane invariato.
Fondamentalmente, questo è un piccolo pezzo di memoria che persiste attraverso i riavvii soft. Un gioco può usare questo buffer per memorizzare qualsiasi cosa voglia finché la console è accesa.Il gioco originale di Animal Crossing è stato effettivamente rilasciato su Nintendo 64, quindi ha senso che qualcosa del genere appaia nel codice.
Passando al binario boot.dol
(tutto ciò che precede è da foresta.rel
), ci sono molti riferimenti a osAppNMIBuffer
nella funzione main
. Una rapida occhiata mostra che ci sono serie di controlli che possono risultare in vari bit di 0x3c(osAppNMIBuffer)
che vengono impostati con operazioni OR.
I valori interessanti dell’operando OR a cui guardare sarebbero:
- Bit 31: 0x01
- Bit 30: 0x02
- Bit 29: 0x04
- Bit 28: 0x08
- Bit 27: 0x10
- Bit 26: 0x20
Ricorda che i bit 25, 26, e 28 sono particolarmente interessanti: 25 e 26 determinano se la modalità zuru è abilitata, e il bit 28 determina il livello del flag (1 o 2).Anche il bit 31 è interessante, ma principalmente sembra essere aggiornato in base ai valori degli altri.
Bit 26
Primo: a 800062e0
c’è un’istruzione ori r0, r0, 0x20
sul valore del buffer a 0x3c
. Questo imposterà il bit 26, che risulta sempre nell’abilitazione della modalità zuru.
Per impostare il bit, l’ottavo byte restituito da DVDGetCurrentDiskID
deve essere 0x99
.Questo ID si trova all’inizio dell’immagine del disco del gioco, ed è caricato a80000000
in memoria. Per una versione normale del gioco, l’ID assomiglia a questo:
47 41 46 45 30 31 00 00 GAFE01..
Patching dell’ultimo byte dell’ID a 0x99
provoca il seguente risultato all’avvio del gioco:
E nella console del sistema operativo, viene stampato quanto segue:
06:43:404 HW\EXI_DeviceIPL.cpp:339 N: ZURUMODE2 ENABLE08:00:288 HW\EXI_DeviceIPL.cpp:339 N: osAppNMIBuffer=0x00000078
Tutte le altre patch possono essere rimosse, e la lettera D appare di nuovo nell’angolo in alto a destra dello schermo, ma nessuno degli altri display di debug è attivato.
Bit 25
Il bit 25 è usato insieme all’esecuzione del controllo del controller della porta 2. Cosa lo fa attivare?
Questo risulta avere lo stesso controllo usato per il bit 28: la versione deve essere maggiore o uguale a 0x90
. Se il bit 26 è stato impostato (l’ID è 0x99
), anche questi due bit saranno impostati, e la modalità zuru sarà comunque abilitata.
Se la versione è tra 0x90
e 0x98
, però, la modalità zuru non è immediatamente abilitata.Ricordando il controllo fatto in zurumode_callback
, sarà abilitata solo se il bit 25 è impostato e padmgr_isConnectedController(1)
ritorna non-zero.Quando un controller è collegato alla porta 2 (l’argomento di isConnectedController
è indicizzato a zero), la modalità zuru viene attivata. La lettera D e le informazioni sulla costruzione vengono visualizzate sulla schermata del titolo, e… la pressione dei pulsanti sul secondo controller controlla i display di debug!
Alcuni pulsanti fanno anche cose oltre a cambiare il display, come aumentare la velocità del gioco.
zerucheck_key_check
L’ultimo mistero è 0x4(zuruKeyCheck)
. Si scopre che questo valore viene aggiornato dalla gigantesca funzione complessa mostrata prima:
Utilizzando il debugger Dolphin, sono stato in grado di determinare che il valore controllato da questa funzione è un insieme di bit corrispondenti alla pressione dei pulsanti sul secondo controller. La traccia della pressione dei pulsanti è memorizzata in un valore di 16 bit a 0x2(zuruKeyCheck)
. Quando non c’è nessun controllore inserito, il valore è 0x7638
.
I 2 byte che contengono i flag per la pressione dei pulsanti del controllore 2 sono caricati e poi aggiornati vicino all’inizio di zerucheck_key_check
. Il nuovo valore viene passato con il registro r4
da padmgr_HandleRetraceMsg
quando chiama la funzione di callback.
Giù verso la fine di zerucheck_key_check
, c’è un altro posto dove 0x4(zuruKeyCheck)
viene aggiornato. Non è apparso nella lista dei riferimenti incrociati perché sta usando r3
come indirizzo di base, e possiamo solo capire cos’è r3
guardando a cosa è impostato ogni volta che questa funzione sta per essere chiamata.
A 8040ed88
il valore di r4
è scritto in 0x4(zuruKeyCheck)
. Viene caricato dalla stessa posizione e poi XORd con 1 appena prima. Ciò che questo dovrebbe fare è alternare il valore del byte (in realtà solo l’ultimo bit) tra 0 e 1. (Se è 0, il risultato dello XOR con 1 sarà 1. Se è 1, il risultato dello XOR sarà 1. Se è 1, il risultato sarà 0. Cercate la tabella della verità per XOR per vedere questo.)
Non ho notato questo comportamento guardando i valori di memoria prima, ma proverò a rompere su questa istruzione nel debugger per vedere cosa succede. Il valore originale è caricato a 8040ed7c
.
Senza toccare alcun pulsante sui controller, non tocco questo punto di interruzione durante la schermata del titolo. Per raggiungere questo blocco di codice, il valore di r5
deve essere 0xb
prima dell’istruzione di ramo che lo precede (8040ed74
). Dei molti percorsi diversi che portano a quel blocco, ce n’è uno che imposterà r5
a 0xb
prima di esso, a 8040ed68
.
Nota che per raggiungere il blocco che imposta r5
a 0xB
, r0
deve essere uguale a 0x1000
poco prima. Seguendo i blocchi della catena fino all’inizio della funzione, possiamo vedere i vincoli necessari per raggiungere questo blocco:
- 8040ed74:
r5
deve essere0xB
- 8040ed60:
r0
deve essere0x1000
- 8040ebe8:
r5
deve essere0xA
- 8040ebe4:
r5
deve essere minore di0x5B
- 8040eba4:
r5
deve essere maggiore di0x7
- 8040eb94:
r6
deve essere 1 - 8040eb5c:
r0
non deve essere 0 - 8040eb74: I valori dei pulsanti della porta 2 devono essere cambiati
Qui raggiungiamo il punto in cui i vecchi valori dei pulsanti sono caricati e i nuovi valori memorizzati. In seguito ci sono un paio di operazioni applicate ai nuovi e vecchi valori:
old_vals = old_vals XOR new_valsold_vals = old_vals AND new_vals
L’operazione XOR segnerà tutti i bit che sono cambiati tra i due valori. L’operazione AND poi maschera il nuovo ingresso per disinserire tutti i bit che non sono attualmente impostati. Il risultato in r0
è l’insieme dei nuovi bit (tasti premuti) nel nuovo valore. Se non è vuoto, siamo sulla strada giusta.
Perché r0
sia 0x1000
, il 4° dei 16 bit della traccia del pulsante deve essere appena cambiato.Impostando un punto di interruzione dopo l’operazione XOR/AND posso capire quale pulsante lo causa: è il pulsante START.
La prossima domanda è come ottenere r5
per iniziare come 0xA
. r5
e r6
sono caricati da0x0(zuruKeyCheck)
all’inizio della funzione di controllo dei tasti, e aggiornati verso la fine quando non si inserisce il blocco di codice che attiva 0x4(zuruKeyCheck)
.
Ci sono alcuni posti appena prima dove r5
viene impostato a 0xA
:
8040ed50
8040ed00
8040ed38
8040ed38
-
8040ed34
:r0
deve essere0x4000
(il pulsante B è stato premuto) -
8040ebe0
:r5
deve essere0x5b
-
8040eba4
:r5
deve essere maggiore di0x7
- come prima da qui in poi…
r5
deve iniziare a 0x5b
8040ed00
-
8040ecfc
:r0
deve essere0xC000
(A e B premuti) -
8040ebf8
:r5
deve essere >= 9 -
8040ebf0
:r5
deve essere minore di 10 -
8040ebe4
:r5
deve essere minore di0x5b
-
8040eba4
:r5
deve essere maggiore di0x7
- come prima da qui in poi…
r5
deve iniziare da 9
8040ed50
-
8040ed4c
:r0
deve essere0x8000
(A è stato premuto) -
8040ec04
:r5
deve essere minore di0x5d
-
8040ebe4
:r5
deve essere maggiore di0x5b
-
8040eba4
:r5
deve essere maggiore di0x7
- come prima da qui in poi…
r5
deve iniziare da 0x5c
Sembra che ci sia una sorta di stato tra la pressione dei tasti, e poi una certa sequenza di combo di tasti deve essere inserita, finendo con START. Sembra che A e/o B vengano subito prima di START.
Seguendo il percorso del codice che imposta r5
a 9, emerge uno schema: r5
è un valore incrementale che può essere aumentato quando il valore corretto della pressione del pulsante viene trovato in r0
, o resettato a 0. I casi più strani in cui non è un valore tra 0x0
e 0xB
si verificano quando si gestiscono passi a più pulsanti, come premere A e B allo stesso tempo. Una persona che cerca di inserire questa combinazione di solito non preme entrambi i pulsanti nello stesso esatto momento in cui si verifica la traccia del pad, quindi deve gestire uno dei due pulsanti prima dell’altro.
Continuando con i diversi percorsi del codice:
-
r5
è impostato su 9 quando viene premuto RIGHT a8040ece8
. -
r5
è impostato su 8 quando C-stick destro è premuto a8040eccc
. -
r5
è impostato su 7 quando C-stick sinistro è premuto a8040ecb0
. -
r5
è impostato su 6 quando LEFT è premuto a8040ec98
. -
r5
è impostato su 5 (e r6 su 1) quando DOWN è premuto a8040ec7c
. -
r5
è impostato su 4 quando C-stick up è premuto a8040ec64
. -
r5
è impostato su 3 quando C-stick down è premuto a8040ec48
. -
r5
è impostato su 2 quando UP è premuto a8040ec30
. -
r5
è impostato su 1 (er6
su 1) quando Z è premuto a8040ec1c
.
La sequenza corrente è:
Z, UP, C-DOWN, C-UP, DOWN, LEFT, C-LEFT, C-RIGHT, RIGHT, A+B, START
Un’altra condizione è controllata prima del controllo Z: mentre il pulsante appena premuto deve essere Z, le bandiere correnti devono essere 0x2030
: anche i paraurti sinistro e destro devono essere premuti (hanno valori di 0x10
e 0x20
). Inoltre, l’UP/DOWN/LEFT/RIGHT sono i pulsanti delD-pad, non lo stick analogico.
Il codice cheat
La combo completa è:
- Tieni premuti i paraurti L+R e premi Z
- D-UP
- C-DOWN
- C-UP
- D-DOWN
- D-LEFT
- C-LEFT
- C-RIGHT
- D-RIGHT
- A+B
- START
Funziona! Attacca un controller alla seconda porta e inserisci il codice, e il display di debug apparirà. Dopo di che puoi iniziare a premere i pulsanti sul secondo (o anche terzo) controller per iniziare a fare cose.
Questa combo funzionerà senza patchare il numero di versione del gioco, puoi anche usarla su una normale copia retail del gioco senza alcun cheat tool o console mods. Inserendo la combo una seconda volta si disattiva la modalità zuru.
Il messaggio “ZURU %d/%d” in zurumode_callback
è usato per stampare lo stato di questa combinazione se la inserite quando l’ID del disco è già 0x99
(presumibilmente per il debug del codice cheat stesso). Il primo numero è la vostra posizione attuale nella sequenza, corrispondente a r5
. Il secondo è impostato su 1 mentre certi pulsanti della sequenza sono tenuti premuti, questi potrebbero corrispondere a quando r6
è impostato su 1.
La maggior parte dei display non spiega cosa sono sullo schermo, quindi per capire cosa stanno facendo dovete trovare le funzioni che li gestiscono. Per esempio, la lunga linea diasterischi blu e rossi che appaiono in cima allo schermo sono segnaposti per visualizzare lo stato delle diverse missioni. Quando una ricerca è attiva, alcuni numeri appariranno lì, indicando lo stato della ricerca.
Lo schermo nero che appare quando si preme Z è una console per stampare messaggi di debug, ma in particolare per cose di basso livello come errori di allocazione della memoria e di heap o altre eccezioni negative. Il comportamento di fault_callback_scroll
suggerisce che potrebbe essere per mostrare questi errori prima che il sistema venga riavviato. Non ho innescato nessuno di questi errori, ma sono riuscito a fargli stampare un paio di caratteri spazzatura con alcuni NOP. Penso che questo sarebbe molto utile per stampare messaggi di debug personalizzati in seguito:
Dopo aver fatto tutto questo, ho scoperto che ottenere la modalità debug patchando l’ID versione a 0x99
è già noto: https://tcrf.net/Animal_Crossing#Debug_Mode. (Hanno anche alcune buone note su cosa sono i vari display, e altre cose che si possono fare usando un controller nella porta 3.)Per quanto posso dire, la combinazione cheat non è stata ancora pubblicata, però.
Questo è tutto per questo post. Ci sono ancora alcune caratteristiche per sviluppatori che vorrei esplorare, come la schermata di debug della mappa e la schermata di selezione dell’emulatore NES, e come attivarle senza usare patch.
Pubblicherò anche degli scritti sull’inversione dei sistemi di dialogo, eventi e missioni allo scopo di fare mods.
Aggiornamento: Le diapositive per il discorso che ho fatto su questo possono essere trovate qui.