Letzten Sommer habe ich mit dem Reverse Engineering von Animal Crossing für den GameCube begonnen. Ich wollte die Möglichkeit untersuchen, Mods für dieses Spiel zu erstellen. Außerdem wollte ich den Prozess dokumentieren, um Tutorials für Leute zu erstellen, die sich für das Hacken von ROMs und Reverse Engineering interessieren. In diesem Beitrag werde ich über die Entwickler-Debugging-Funktionen sprechen, die im Spiel verblieben sind, und darüber, wie ich Cheat-Combos entdeckt habe, mit denen sie freigeschaltet werden können.
new_Debug_mode
Als ich die verbleibenden Debugging-Symbole studierte, bemerkte ich die Namen der Funktionen und Variablen, die das Wort "Debug" enthielten, und entschied, dass es interessant wäre zu sehen, ob noch Debugging-Funktionen im Spiel vorhanden waren. Wenn es mir gelingt, die Debugging- oder Entwicklungsfunktionen zu aktivieren, hilft mir dies beim Erstellen von Mods.
Die erste Funktion, die mir aufgefallen ist, war
new_Debug_mode
. Es wird von der
entry
aufgerufen, die unmittelbar nach dem Ende des Nintendo-Logo-Bildschirms startet. Sie platziert
0x1C94
die
0x1C94
und speichert einen Zeiger darauf.
Nachdem der
entry
in der gehosteten Struktur mit dem Offset
0xD4
unmittelbar vor dem Aufruf von
mainproc
Wert 0 gesetzt.
Um zu sehen, was passiert, wenn der Wert nicht Null ist, habe ich den
li r0, 0
bei
80407C8C
gepatcht und durch
li r0, 1
. Die
li r0, 0
des
li r0, 0
sind
38 00 00 00
wobei der zugewiesene Wert am Ende des Befehls steht, sodass ich die Bytes einfach durch
38 00 00 01
ersetzen und
li r0, 1
. Als zuverlässigere Methode zum Erstellen von Anweisungen können Sie so etwas wie
kstool
:
$ kstool ppc32be "li 0, 1"
li 0, 1 = [ 38 00 00 01 ]
Im Dolphin-Emulator kann dieser Patch angewendet werden, indem Sie in den Spieleigenschaften auf die Registerkarte "Patches" gehen und ihn wie folgt eingeben:
Nach dem Zuweisen des Werts 1 wurde am unteren Bildschirmrand ein interessantes Diagramm angezeigt:
Es sah aus wie ein Leistungsindikator: Die kleinen Balken am unteren Bildschirmrand nahmen zu oder ab. (Als ich mir später die Namen der Funktionen ansah, die dieses Diagramm zeichnen, stellte ich fest, dass sie tatsächlich Metriken für die CPU- und Speichernutzung anzeigen.)
Es war großartig, aber nicht besonders hilfreich. Nachdem ich den Wert 1 zugewiesen hatte, wurde meine Stadt nicht mehr geladen, sodass hier nichts anderes getan werden konnte.
Zuru-Modus
Ich suchte erneut nach anderen Verweisen auf Debugging-Funktionen und stieß mehrmals auf etwas, das als „Zuru-Modus“ bezeichnet wurde. Zweige von Codeblöcken mit Debugging-Funktionalität überprüften häufig die Variable
zurumode_flag
.
zzz_LotsOfDebug
(der Name, den ich mir
game_move_first
habe) in der
game_move_first
gezeigten Funktion
zurumode_flag
nur aufgerufen, wenn
zurumode_flag
ungleich Null ist.
Auf der Suche nach Funktionen, die diesem Wert zugeordnet sind, habe ich Folgendes gefunden:
zurumode_init
zurumode_callback
zurumode_update
zurumode_cleanup
Auf den ersten Blick ist ihr Zweck mysteriös, sie jonglieren mit Bits in den Offsets einer Variablen namens
osAppNMIBuffer
.
So sah die Arbeit dieser Funktionen auf den ersten Blick aus:
zurumode_init
- Setzt
zurumode_flag
auf 0 - Überprüft mehrere Bits in
osAppNMIBuffer
- Speichert einen Zeiger auf die Funktion
zurumode_callback
in der padmgr
Struktur zurumode_update
zurumode_update
- Überprüft mehrere Bits in
osAppNMIBuffer
- Abhängig vom Wert dieser Bits wird
zurumode_flag
aktualisiert - Druckt eine Formatzeichenfolge in die Betriebssystemkonsole.
Dies ist normalerweise nützlich, um dem Code einen Kontext zu geben, aber die Zeile enthielt viele nicht druckbare Zeichen. Der einzige erkennbare Text war "zurumode_flag" und "% d".
Angenommen, es könnte sich um japanischen Text mit Multibyte-Zeichencodierung handeln, habe ich die Zeichenfolge durch das Codierungserkennungswerkzeug geleitet und festgestellt, dass die Zeichenfolge mit Shift-JIS codiert wurde. In der Übersetzung bedeutete die Zeile einfach "Der Wert von zurumode_flag hat sich von% d auf% d geändert." Dies gibt uns nicht viele neue Informationen, aber jetzt wissen wir, dass Shift-JIS verwendet wird: In Binärdateien und Zeilentabellen gibt es viel mehr Zeilen in dieser Codierung.
zurumode_callback
zerumode_check_keycheck
- Überprüft mehrere Bits in
osAppNMIBuffer
- Der
zurumode_flag
Wert wird zurumode_flag
zurumode_update
zerumode_check_keycheck
bis wir uns wegen einer anderen Schreibweise getroffen haben ... was ist das?
Eine riesige komplexe Funktion, die viel mehr an Bits mit unbenannten Werten arbeitet.
Zu diesem Zeitpunkt habe ich mich entschlossen, einen Schritt zurückzutreten und andere Debugging-Funktionen und -Variablen zu untersuchen, da ich mir nicht sicher war, wie wichtig der Zuru-Modus ist. Außerdem habe ich nicht verstanden, was "Schlüsselüberprüfung" hier bedeutet. Ist es möglich, dass dies ein kryptografischer Schlüssel ist?
Zurück zum Debuggen
Ungefähr zu dieser Zeit bemerkte ich ein Problem beim Laden von Debugging-Symbolen in die IDA. Die Datei
foresta.map
auf der Festplatte des Spiels enthält viele Adressen und Namen von Funktionen und Variablen. Zuerst habe ich nicht gesehen, dass die Adressen für jeden Abschnitt von vorne beginnen, deshalb habe ich ein einfaches Skript geschrieben, das einen Namenseintrag für jede Zeile der Datei hinzufügt.
Ich habe neue IDA-Skripte geschrieben, um das Laden von Symboltabellen für verschiedene Abschnitte des Programms zu
.text
:
.text
,
.rodata
,
.data
und
.bss
. Der
.text
Abschnitt enthält alle Funktionen, daher habe ich ihn so gestaltet, dass das Skript diesmal beim
.text
des Namens die Funktionen an jeder Adresse automatisch erkennt.
In den
m_debug.o
erstellte er nun ein Segment für jedes
m_debug.o
(z. B.
m_debug.o
, das als Code für etwas namens
m_debug
kompiliert werden
m_debug
) und legte den Platz und die Namen für jedes Datenelement fest.
Dies gab mir viel mehr Informationen, aber ich musste den Datentyp für jedes Datenelement manuell festlegen, da ich jedes Datenobjekt als einfaches Byte-Array definierte. (Rückblickend verstehe ich, dass es besser wäre anzunehmen, dass Fragmente von 4 Bytes 32-Bit-Ganzzahlen enthielten, da es viele und viele Adressen von Funktionen und Daten enthielt, die für die Erstellung von Querverweisen wichtig sind.)
Beim Studium des neuen
.bss
Segments für
m_debug_mode.o
ich mehrere Variablen der Form
quest_draw_status
und
event_status
. Dies ist interessant, weil ich wollte, dass nützliche Informationen, nicht nur ein Leistungsdiagramm, im Debug-Modus angezeigt werden. Glücklicherweise gab es aus diesen Datensätzen Querverweise auf einen riesigen Code, der
debug_print_flg
überprüfte.
Mit einem Debugger im Dolphin-Emulator habe ich einen Haltepunkt an der Stelle der Funktion festgelegt, an der
debug_print_flg
überprüft wurde (bei
8039816C
), um zu verstehen, wie diese Überprüfung funktioniert. Das Programm wurde jedoch nie an diesen Haltepunkt übergeben.
game_debug_draw_last
wir
game_debug_draw_last
warum dies passiert: Diese Funktion wird von
game_debug_draw_last
. Ratet mal, welcher Wert vor seinem bedingten Aufruf überprüft wird?
zurumode_flag
! Was zur Hölle ist los?
Ich habe einen Haltepunkt für diese Prüfung festgelegt (
80404E18
) und es hat sofort funktioniert. Der Wert von
zurumode_flag
war Null, sodass das Programm bei normaler Ausführung den Aufruf dieser Funktion verpasst hätte. Ich habe stattdessen eine NOP-Verzweigungsanweisung eingefügt (durch eine Anweisung ersetzt, die nichts bewirkt), um zu überprüfen, was passiert, wenn die Funktion aufgerufen wird.
Im Dolphin-Debugger können Sie dies tun, indem Sie das Spiel anhalten, mit der rechten Maustaste auf die Anweisungen klicken und "Nop einfügen" auswählen:
Nichts ist passiert. Dann überprüfte ich, was in der Funktion vor sich ging, und entdeckte ein anderes Verzweigungskonstrukt, das alles Interessante umging, was bei
803981a8
passierte. Stattdessen habe ich auch NOP eingefügt, und der Buchstabe „D“ wurde in der oberen rechten Ecke des Bildschirms angezeigt.
In dieser Funktion bei
8039816C
(ich habe sie
zzz_DebugDrawPrint
) gibt es immer noch eine Menge interessanten Codes, der jedoch nicht aufgerufen wird. Wenn Sie diese Funktion in Form eines Diagramms betrachten, sehen Sie, dass es eine Reihe von Verzweigungsoperatoren gibt, die Codeblöcke in der gesamten Funktion überspringen:
Nachdem ich NOP anstelle mehrerer anderer Verzweigungskonstruktionen eingefügt hatte, sah ich verschiedene interessante Dinge auf dem Bildschirm:
Die nächste Frage war, wie diese Debugging-Funktion aktiviert werden kann, ohne den Code zu ändern.
Außerdem tritt in einigen Verzweigungskonstrukten
zurumode_flag
in dieser Debug-
zurumode_flag
erneut auf. Ich habe einen weiteren Patch hinzugefügt,
zurumode_update
Flag in
zurumode_update
immer der Wert 2 zugewiesen wird, da es, wenn es nicht mit 0 verglichen wird, speziell mit dem Wert 2 verglichen wird.
Nach dem Neustart des Spiels sah ich in der oberen rechten Ecke des Bildschirms eine solche Meldung "msg. nein. "
Die Nummer 687 ist die Datensatzkennung der zuletzt angezeigten Nachricht. Ich habe es mit dem Table Viewer-Programm überprüft, das ich zu Beginn der Analyse geschrieben habe. Sie können es aber auch mit dem
String-Tabellen-Editor mit einer vollständigen GUI überprüfen, die ich zum Hacken von ROMs geschrieben habe. So sieht der Beitrag im Editor aus:
Zu diesem Zeitpunkt wurde klar, dass das Studium des Zuru-Modus nicht mehr weggenommen wurde - es steht in direktem Zusammenhang mit den Debugging-Funktionen des Spiels.
Zurück zum Zuru-Modus
zurumode_init
initialisiert verschiedene Dinge:
0xC(padmgr_class)
der Wert der zurumode_callback
Adresse zurumode_callback
0x10(padmgr_class)
der Adresswert von padmgr_class
selbst padmgr_class
0x4(zuruKeyCheck)
der Wert des letzten Bits in dem von 0x3C(osAppNMIBuffer)
geladenen Wort 0x3C(osAppNMIBuffer)
.
Ich habe herausgefunden, was
padmgr
, eine Abkürzung für "Gamepad Manager". Dies bedeutet, dass möglicherweise eine spezielle Kombination von Tasten (Schaltflächen) auf dem Gamepad eingegeben werden kann, um den Zuru-Modus zu aktivieren, oder eine Art Debugging-Gerät oder eine Funktion der Entwicklerkonsole, mit der ein Signal zum Aktivieren gesendet werden kann.
zurumode_init
wird nur beim ersten Start des Spiels ausgeführt (wenn die Reset-Taste gedrückt wird, funktioniert es nicht).
Nachdem wir an der Adresse
8040efa4
einen Haltepunkt
8040efa4
, an dem der Wert
0x4(zuruKeyCheck)
zugewiesen ist
0x4(zuruKeyCheck)
, können wir sehen, dass beim Laden ohne Drücken von Tasten der Wert auf 0 gesetzt wird. Wenn Sie ihn durch 1 ersetzen, passiert etwas Interessantes:
Der Buchstabe „D“ wird erneut in der oberen rechten Ecke angezeigt (diesmal grün, nicht gelb), und einige Montageinformationen werden ebenfalls angezeigt:
[CopyDate: 02/08/01 00:16:48 ]
[Date: 02-07-31 12:52:00]
[Creator:SRD@SRD036J]
Ein Patch, der zu Beginn immer
0x4(zuruKeyCheck)
auf 1 setzt, sieht folgendermaßen aus:
8040ef9c 38c00001
Dies scheint der richtige Weg zu sein, um den Zuru-Modus zu initialisieren. Danach können verschiedene Aktionen erforderlich sein, um die Anzeige bestimmter Debugging-Informationen zu erreichen. Wenn Sie das Spiel starten, einen Spaziergang machen und mit einem Dorfbewohner sprechen, sehen wir keine der oben genannten Nachrichten (außer dem Buchstaben "D" in der Ecke).
Die wahrscheinlichsten Verdächtigen sind
zurumode_update
und
zurumode_callback
.
zurumode_update
zurumode_update
zuerst in
zurumode_init
und dann ständig von
zurumode_callback
.
Es überprüft erneut das letzte Bit
0x3C(osAppNMIBuffer)
und aktualisiert dann basierend auf diesem Wert
zurumode_flag
.
Wenn das Bit Null ist, wird das Flag auf Null gesetzt.
Wenn nicht, wird die folgende Anweisung ausgeführt, wobei der vollständige Wert
0x3c(osAppNMIBuffer)
r5
:
extrwi r3, r5, 1, 28
Es extrahiert das 28. Bit aus
r5
und speichert es in
r3
.
Dann wird 1 zum Ergebnis hinzugefügt, dh das Endergebnis ist immer 1 oder 2.
Dann wird
zurumode_flag
mit dem vorherigen Ergebnis verglichen, je nachdem, wie viele der 28. und letzten Bits auf
0x3c(osAppNMIBuffer)
: 0, 1 oder 2.
Dieser Wert wird in
zurumode_flag
. Wenn sich nichts ändert, wird die Funktion beendet und der aktuelle Flag-Wert zurückgegeben. Wenn sich der Wert ändert, wird eine viel komplexere Kette von Codeblöcken ausgeführt.
Auf Japanisch wird eine Meldung angezeigt: Der gleiche „zurumode_flag-Wert wurde von% d in% d geändert“, über den wir oben gesprochen haben.
Dann wird eine Reihe von Funktionen mit unterschiedlichen Argumenten aufgerufen, je nachdem, ob das Flag gleich Null geworden ist oder nicht. Der Assembler-Code dieses Teils ist eintönig, daher werde ich seinen Pseudocode zeigen:
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) }
Beachten Sie, dass wenn das Flag Null ist, das Argument 0 an JC_JUTDbPrint_setVisible übergeben wird.
Wenn das Flag ungleich Null ist und Bit 25 oder Bit 31 auf
0x3C(osAppNMIBuffer)
, wird
setVisible
an Argument 1 übergeben.
Dies ist der erste Schlüssel zum Aktivieren des Zuru-Modus: Das letzte Bit
0x3C(osAppNMIBuffer)
muss auf 1 gesetzt werden, um Debugging-Informationen anzuzeigen, und
zurumode_flag
Wert ungleich Null setzen.
zurumode_callback
zurumode_callback
befindet sich bei
8040ee74
und wird wahrscheinlich von einer Funktion aufgerufen, die sich auf das Gamepad bezieht. Nach dem Einfügen eines Haltepunkts in den Dolphin-Debugger zeigt der Aufrufstapel an, dass er tatsächlich von
padmgr_HandleRetraceMsg
.
Eine ihrer ersten Aktionen war die Ausführung von
zerucheck_key_check
. Diese Funktion ist komplex, scheint jedoch im Allgemeinen darauf ausgelegt zu sein, den Wert von
zuruKeyCheck
zu lesen und zu aktualisieren. Bevor ich zur Keycheck-Funktion überging, habe ich mich entschlossen zu überprüfen, wie dieser Wert im Rest der Rückruffunktion verwendet wird.
Dann wird erneut nach einigen Bits in
0x3c(osAppNMIBuffer)
. Wenn Bit 26 gesetzt ist oder wenn Bit 25 gesetzt ist und
padmgr_isConnectedController(1)
einen Wert ungleich Null zurückgibt, wird das letzte Bit in
0x3c(osAppNMIBuffer)
auf 1 gesetzt!
Wenn keines dieser Bits gesetzt ist oder Bit 25 gesetzt ist, aber
padmgr_isConnectedController(1)
0 zurückgibt, prüft die Funktion, ob das Byte an der Adresse
0x4(zuruKeyCheck)
gleich Null ist. Wenn gleich, wird das letzte Bit im ursprünglichen Wert zurückgesetzt und in
0x3c(osAppNMIBuffer)
. Wenn nicht, wird das letzte Bit immer noch auf 1 gesetzt.
Im Pseudocode sieht es so aus:
x = osAppNMIBuffer[0x3c] if (BIT(x, 26) || (BIT(x, 25) && isConnectedController(1)) || zuruKeyCheck[4] != 0) { osAppNMIBuffer[0x3c] = x | 1
Wenn danach Bit 26 nicht gesetzt ist, ruft die Funktion
zurumode_update
und wird beendet.
Wenn das Bit gesetzt ist und
0x4(zuruKeyCheck)
ungleich Null ist, wird eine
0x4(zuruKeyCheck)
, in der Folgendes angezeigt wird: „ZURU% d /% d“.
Um die Zwischensumme zusammenzufassen
Folgendes passiert:
padmgr_HandleRetraceMsg
ruft
zurumode_callback
. Ich gehe davon aus, dass diese "Handle Retrace-Nachricht" bedeutet, dass einfach die Tastenanschläge des Controllers gescannt werden. Bei jedem Scan kann es zu einer Reihe unterschiedlicher Rückrufe kommen.
Wenn
zurumode_callback
ausgeführt wird
zurumode_callback
werden die aktuellen Tastenanschläge (Schaltflächen) überprüft. Es sieht so aus, als würde sie eine bestimmte Schaltfläche oder eine Kombination von Schaltflächen überprüfen.
Das letzte Bit im NMI-Puffer wird abhängig von den spezifischen Bits in seinem aktuellen Wert sowie vom Wert eines der
zuruKeyCheck
Bytes (
0x4(zuruKeyCheck)
)
0x4(zuruKeyCheck)
.
Dann wird
zurumode_update
und prüft dieses Bit. Wenn es 0 ist, wird das Zuru-Modus-Flag auf 0 gesetzt. Wenn es 1 ist, ändert sich das Modus-Flag auf 1 oder 2, je nachdem, ob Bit 28 gesetzt ist.
Es gibt drei Möglichkeiten, den Zuru-Modus zu aktivieren:- Bit 26 wird auf
0x3C(osAppNMIBuffer)
- Bit 25 ist auf
0x3C(osAppNMIBuffer)
und der Controller ist mit Port 2 verbunden 0x4(zuruKeyCheck)
nicht Null
osAppNMIBuffer
Interessiert an der Bedeutung von
osAppNMIBuffer
suchte ich nach „NMI“ und fand im Nintendo-Kontext Links zu „nicht maskierbaren Interrupts“. Es stellt sich heraus, dass der Name dieser Variablen in der Entwicklerdokumentation für Nintendo 64 vollständig erwähnt wird:
osAppNMIBuffer ist ein 64-Byte-Puffer, der bei einem Kaltstart gelöscht wird. Wenn das System aufgrund von NMI neu gestartet wird, ändert sich der Status dieses Puffers nicht.
Tatsächlich ist dies ein kleines Stück Speicher, das während eines „sanften“ Neustarts (mit der Reset-Taste) gespeichert wird. Das Spiel kann diesen Puffer verwenden, um alle Daten zu speichern, während sich die Konsole im Netzwerk befindet. Das ursprüngliche Animal Crossing wurde auf Nintendo 64 veröffentlicht, daher ist es logisch, dass im Code etwas Ähnliches hätte erscheinen müssen.
Wenn wir zur binären Datei
boot.dol
gehen (alles, was oben gezeigt wurde, war in
foresta.rel
), dann hat seine
main
viele Links zu
osAppNMIBuffer
. Ein kurzer Blick zeigt, dass es eine Reihe von Überprüfungen gibt, die dazu führen können,
0x3c(osAppNMIBuffer)
Werte verschiedener Bits
0x3c(osAppNMIBuffer)
mithilfe von ODER-Operationen festgelegt werden.
Die folgenden ODER-Operandenwerte können interessant sein:
- Bit 31: 0x01
- Bit 30: 0x02
- Bit 29: 0x04
- Bit 28: 0x08
- Bit 27: 0x10
- Bit 26: 0x20
Wir erinnern uns, dass die Bits 25, 26 und 28 besonders interessant sind: 25 und 26 bestimmen, ob der Zuru-Modus aktiviert ist, und Bit 28 bestimmt den Flag-Pegel (1 oder 2).
Bit 31 ist ebenfalls interessant, aber es sieht so aus, als würde es sich abhängig von den Werten der anderen ändern.
Bit 26
Zunächst einmal: An der Adresse
800062e0
befindet sich ein Befehl
ori r0, r0, 0x20
mit einem Pufferwert von
0x3c
. Es setzt Bit 26, das den Zuru-Modus immer einschaltet.
Damit das Bit gesetzt werden kann, muss das von
DVDGetCurrentDiskID
zurückgegebene achte Byte
DVDGetCurrentDiskID
sein. Diese Kennung befindet sich ganz am Anfang des Disk-Images des Spiels und wird unter
80000000
in den Speicher
80000000
. In einer regulären Einzelhandelsversion des Spiels sieht die ID folgendermaßen aus:
47 41 46 45 30 31 00 00 GAFE01..
Wenn Sie das letzte Byte des Bezeichners durch einen
0x99
für
0x99
, erhalten Sie zu Beginn des Spiels das folgende Bild:
In der Betriebssystemkonsole wird Folgendes angezeigt:
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
Alle anderen Patches können entfernt werden. Danach wird der Buchstabe D erneut in der oberen rechten Ecke des Bildschirms angezeigt, es werden jedoch keine Debug-Meldungen mehr aktiviert.
Bit 25
Bit 25 wird in Verbindung mit der Überprüfung von Controller-Port 2 verwendet. Was bewirkt, dass es eingeschaltet wird?
Es stellt sich heraus, dass er dieselbe Prüfung wie für Bit 28 verwenden sollte: Die Version muss größer oder gleich
0x90
. Wenn Bit 26 gesetzt ist (ID ist
0x99
), werden auch diese beiden Bits gesetzt und der Zuru-Modus wird weiterhin aktiviert.
Wenn sich die Version jedoch im Bereich von
0x90
bis
0x98
, wird der Zuru-Modus nicht sofort
0x98
. Erinnern Sie sich an die in
zurumode_callback
durchgeführte
zurumode_callback
- der Modus wird nur aktiviert, wenn Bit 25 gesetzt ist
und padmgr_isConnectedController(1)
einen Wert ungleich Null zurückgibt.
Nachdem der Controller mit Port 2 verbunden ist (das Argument
isConnectedController
hat keine Indizierung), wird der Zuru-Modus aktiviert. Der Buchstabe D und Informationen zur Baugruppe werden auf dem Startbildschirm angezeigt, und wir ... können die Anzeige des Debuggens mit den Tasten des zweiten Controllers steuern!
Einige Schaltflächen führen Aktionen aus, die nicht nur die Anzeige ändern, sondern beispielsweise auch die Geschwindigkeit des Spiels erhöhen.
zerucheck_key_check
Das letzte Rätsel bleibt
0x4(zuruKeyCheck)
. Es stellt sich heraus, dass dieser Wert mit einer riesigen komplexen Funktion aktualisiert wird, die ich oben gezeigt habe:
Mit dem Dolphin-Emulator-Debugger konnte ich feststellen, dass der von dieser Funktion überprüfte Wert ein Satz von Bits ist, die den Tastendrücken auf dem zweiten Controller entsprechen.
Die Verfolgung von
0x2(zuruKeyCheck)
wird in einem 16-Bit-Wert bei
0x2(zuruKeyCheck)
. Wenn die Steuerung nicht angeschlossen ist, lautet der Wert
0x7638
.
2 Bytes, die Flags der Controller-Tastendrücke enthalten, werden heruntergeladen und dann am Anfang von
zerucheck_key_check
aktualisiert. Der neue Wert wird mit dem Register
r4
Funktion
padmgr_HandleRetraceMsg
, wenn die Rückruffunktion
padmgr_HandleRetraceMsg
.
Gegen Ende von
zerucheck_key_check
gibt es einen weiteren Ort, an dem
0x4(zuruKeyCheck)
aktualisiert wird
0x4(zuruKeyCheck)
. ,
r3
,
r3
, .
8040ed88
r4
0x4(zuruKeyCheck)
. XOR- 1. — ( — ) 0 1. ( 0,
XOR 1 1. 1, 0. . XOR.)
Früher, als ich die Werte im Speicher studierte, habe ich dieses Verhalten nicht bemerkt, aber ich werde versuchen, diese Anweisung im Debugger zu unterbrechen, um zu verstehen, was passiert. Der ursprüngliche Wert wird um geladen 8040ed7c
.Ohne Berühren der Controller-Tasten komme ich auf dem Startbildschirm nicht zu diesem Haltepunkt. Um in diesen Codeblock zu gelangen, r5
muss der Wert 0xb
vor dem Verzweigungsbefehl vor dem Haltepunkt ( 8040ed74
) gleich werden. Von den vielen verschiedenen Pfaden, die zu diesem Block führen, weist nur einer an der Adresse einen r5
Wert 0xb
davor zu 8040ed68
.Beachten Sie, dass der Wert unmittelbar davor gleich sein muss , um den Block zu erreichen, der den r5
Wert zuweist . Wenn Sie den Blöcken entlang der Kette bis zum Beginn der Funktion folgen, sehen Sie alle Einschränkungen, die erforderlich sind, um diesen Block zu erreichen:0xB
r0
0x1000
- 8040ed74: Wert
r5
muss gleich sein0xB
- 8040ed60: Wert
r0
muss gleich sein0x1000
- 8040ebe8: Wert
r5
muss gleich sein0xA
- 8040ebe4: Wert
r5
muss kleiner sein0x5B
- 8040eba4: Wert
r5
muss größer sein0x7
- 8040eb94: Wert
r6
muss 1 sein - 8040eb5c: Wert
r0
darf nicht 0 sein - 8040eb74: Die Werte der Port 2-Tasten sollten sich ändern

Hier haben wir den Punkt erreicht, an dem die alten Schaltflächenwerte geladen und die neuen Werte gespeichert werden. Dann kommen einige Operationen, die auf die neuen und alten Werte angewendet werden: Die XOR-Operation markiert alle Bits, die sich zwischen den beiden Werten geändert haben. Dann maskiert die UND-Operation den neuen Eingang, um alle Bits zu setzen, die derzeit nicht auf den Zustand 0 gesetzt sind. Das Ergebnis ist ein Satz neuer Bits (Tastendruck) im neuen Wert. Wenn es nicht leer ist, sind wir auf dem richtigen Weg. Um einen Unterschied zu machen , muss sich die vierte der 16-Bit-Tracking-Tasten ändern. Nachdem ich nach der XOR / AND-Operation einen Haltepunkt eingefügt hatte, stellte ich fest, dass die START-Taste diesen Zustand auslöst. Die nächste Frage ist, wie man es anfänglich gleich machtold_vals = old_vals XOR new_vals
old_vals = old_vals AND new_vals
r0
r0
0x1000
r5
0xA
. r5
und r6
werden vom 0x0(zuruKeyCheck)
Anfang der Schlüsseltestfunktion geladen und näher am Ende aktualisiert, wenn wir nicht in den Codeblock gelangen, der enthält 0x4(zuruKeyCheck)
.Davor gibt es mehrere Stellen, an denen der r5
Wert zugewiesen wird 0xA
:8040ed38
8040ed34
: Der Wert r0
muss gleich sein 0x4000
(Taste B wird gedrückt)8040ebe0
: Wert r5
muss gleich sein0x5b
8040eba4
: Wert r5
muss größer sein0x7
- dann geht alles wie bisher ...
r5
sollte mit beginnen 0x5b
8040ed00
8040ecfc
: Wert r0
muss gleich sein 0xC000
(A und B werden gedrückt)8040ebf8
: Wert r5
muss> = 9 sein8040ebf0
: Wert r5
muss kleiner als 10 sein8040ebe4
: Wert r5
muss kleiner sein0x5b
8040eba4
: r5
sollte mehr sein0x7
- dann geht alles wie bisher ...
r5
sollte um 9 beginnen8040ed50
8040ed4c
: Wert r0
muss gleich sein 0x8000
(Taste A gedrückt)8040ec04
: Wert r5
muss kleiner sein0x5d
8040ebe4
: Wert r5
muss größer sein0x5b
8040eba4
: Wert r5
muss größer sein0x7
- dann geht alles wie bisher ...
r5
sollte beginnen mit 0x5c
Es scheint, dass es zwischen den Tastenanschlägen eine Art Status gibt, nach dem Sie eine bestimmte Folge von Combos über die Tasten eingeben müssen, die mit dem Drücken von START enden. Es sieht so aus, als ob A und / oder B direkt vor dem Start gehen sollten.Wenn Sie den Pfad des Codes verfolgen, der den r5
Wert auf 9 setzt, entsteht ein Muster: r5
- Es ist ein zunehmender Wert, der entweder zunehmen kann, wenn ein r0
geeigneter Wert gefunden wird, oder Null. Die seltsamsten Fälle, in denen dies kein Wert im Bereich von 0x0
bis ist0xB
treten auf, wenn Schritte mit mehreren Tasten verarbeitet werden, z. B. wenn A und B gleichzeitig gedrückt werden. Eine Person, die versucht, diese Kombination einzugeben, kann normalerweise nicht beide Tasten gleichzeitig drücken, während sie das Gamepad verfolgt. Sie müssen also die gedrückte Taste verarbeiten erster.Wir untersuchen weiterhin verschiedene Codepfade:r5
nimmt den Wert 9 an, wenn an der Adresse RECHTS gedrückt wird 8040ece8
.r5
nimmt den Wert 8 an, wenn die rechte Taste C an der Adresse gedrückt wird 8040eccc
.r5
nimmt den Wert 7 an, wenn die linke Taste C an der Adresse gedrückt wird 8040ecb0
.r5
nimmt den Wert 6 an, wenn LEFT an der Adresse gedrückt wird 8040ec98
.r5
nimmt den Wert 5 an (und r6 nimmt den Wert 1 an), wenn an der Adresse DOWN gedrückt wird 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, .
-
:
- Halten Sie die Stoßstangen L + R gedrückt und drücken Sie Z.
- D-up
- C-DOWN
- C-up
- D-down
- D-links
- C-links
- C-RECHTS
- D-RECHTS
- A + B.
- STARTEN
Es funktioniert! Schließen Sie den Controller an den zweiten Port an und geben Sie den Code ein. Danach werden die Debug-Informationen angezeigt. Danach können Sie die Tasten am zweiten (oder sogar dritten) Controller drücken, um verschiedene Aktionen auszuführen.Diese Kombination funktioniert, ohne die Versionsnummer des Spiels zu patchen. Es kann sogar in einer regulären Einzelhandelsversion des Spiels ohne Cheat-Tools oder Konsolen-Mods verwendet werden. Durch erneutes Eingeben von Combos wird der Zuru-Modus deaktiviert.«ZURU %d/%d»
zurumode_callback
, , ID
0x99
(, -). — ,
r5
. 1, , ,
r6
1.
, , , . , — . , - , .
, Z — , , , .
fault_callback_scroll
, . , NOP. , :
Nachdem ich dies alles getan hatte, stellte ich fest, dass das Aufrufen des Debug-Modus durch Patchen derVersions- ID 0x99
bereits anderen Personen bekannt ist: https://tcrf.net/Animal_Crossing#Debug_Mode . (Außerdem enthält der Link gute Hinweise, die auf verschiedene Meldungen hinweisen und über andere Dinge sprechen, die mit dem Controller in Port 3 ausgeführt werden können.) Soweit ich weiß, hat jedoch noch niemand die Cheat-Kombination veröffentlicht.Das ist alles
Es gibt andere Entwicklerfunktionen, die ich untersuchen möchte, wie den Debug-Bildschirm der Karte und den NES-Emulator-Auswahlbildschirm und wie man sie ohne Verwendung von Patches aktiviert.Darüber hinaus werde ich Artikel zum Reverse Engineering von Dialogsystemen, Ereignissen und Quests veröffentlichen, um Mods zu erstellen.