jamchambs blogg
Förra sommaren började jag reverse engineering av Animal Crossing för GameCube för att utforska möjligheten att skapa moddar för spelet. Jag ville också dokumentera processen för att skapa handledningar för personer som är intresserade av ROM-hacking och reverse engineering.I det här inlägget utforskar jag de felsökningsfunktioner för utvecklare som fortfarande finns kvar i spelet, och hur jag upptäckte en fuskkombination som kan användas för att låsa upp dem.
new_Debug_mode
När jag tittade runt på några överblivna debugsymboler såg jag funktioner och variabelnamn som innehöll ordet ”debug”, och tänkte att det skulle vara intressant att se vilka debugfunktioner som kan finnas kvar i spelet. Om det fanns några felsöknings- eller utvecklarfunktioner som jag kunde aktivera skulle det också kunna vara till hjälp vid skapandet av mods.
Den första funktionen jag tittade på var new_Debug_mode
.Den anropas av entry
-funktionen, som körs direkt efter att Nintendotrademark-skärmen är klar. Allt den gör är att allokera en 0x1C94
byte struktur och spara dess pekare.
När den anropas i entry
sätts värdet 0 vid offset 0xD4
i den allokerade strukturen,precis innan mainproc
anropas.
För att se vad som händer när värdet inte är noll, lappade jag li r0, 0
-instruktionen vid 80407C8C
till li r0, 1
. De råa bytena för instruktionen li r0, 0
är 38 00 00 00
,där det tilldelade värdet finns i slutet av instruktionen, så du kan bara ändra detta till 38 00 00 01
för att få li r0, 1
. För ett mer tillförlitligt sätt att sätta ihop instruktioner kan du använda något som kstool
:
Du kan tillämpa den här patchen i Dolphin-emulatorn genom att gå till fliken ”Patches” i spelets egenskaper och skriva in den så här:
Som du sätter detta värde till 1 visas en intressant graf längst ner på skärmen:
Det såg ut som en prestandamätare, med de små staplarna längst ner på skärmen som växte och krympte. (När jag senare slog upp namnen på de funktioner som ritar grafen fann jag att de faktiskt visar mätvärden för CPU- och minnesanvändning.) Det var snyggt, men inte särskilt användbart. Att sätta värdet över 1 stoppade faktiskt mytown från att laddas, så det verkade inte som om det fanns mycket mer att göra med detta.
Zuru mode
Jag började titta runt på andra referenser till felsökningsrelaterade saker, och såg något som kallades ”zuru mode” dyka upp ett par gånger. Förgreningar till kodblock som hade felsökningsfunktionalitet kontrollerade ofta en variabel som hette zurumode_flag
.
I game_move_first
-funktionen på bilden ovan kallas zzz_LotsOfDebug
(ett namn som jag hittade på)endast om zurumode_flag
inte är noll.
Looking for functions related to this value yields the following:
zurumode_init
zurumode_callback
zurumode_update
zurumode_cleanup
Om man ser det vid första anblicken så är de alla lite obskyra, de vrider runt olika bitar i offsets i en variabel som heter osAppNMIBuffer
.Här är en första titt på vad dessa funktioner gör:
zurumode_init
- Sätt
zurumode_flag
till 0 - Kontrollera några bitar i
osAppNMIBuffer
- Lagra en pekare till
zurumode_callback
-funktionen ipadmgr
-strukturen - Kalla
zurumode_update
zurumode_update
- Kontrollera några bitar i
osAppNMIBuffer
- Anslutningsvis uppdatera
zurumode_flag
baserat på dessa bitar - Skriv ut en formatsträng till OS-konsolen.
Den här typen av saker är vanligtvis användbara för att ge ett sammanhang till koden, men det fanns ett gäng oskrivbara tecken i strängen. Den enda igenkännbara texten var ”zurumode_flag” och ”%d”.
I och med tanke på att det kunde vara japansk text som använde en teckenkodning med flera byte, körde jag strängen genom ett verktyg för teckenkodningsdetektering och fick reda på att den var Shift-JIS-kodad. Den översatta strängen betyder bara ”zurumode_flag hasbeen changed from %d to %d”. Det ger inte mycket ny information, men att veta om användningen av Shift-JIS gör det, eftersom det finns många fler strängar i binärfilerna och strängtabellerna som använder denna kodning.
zurumode_callback
- Kallar
zerumode_check_keycheck
- Kontrollerar några bitar i
osAppNMIBuffer
- Prycker värdet av
zurumode_flag
någonstans - Kallar
zurumode_update
zerumode_check_keycheck
syntes inte tidigare på grund av den annorlunda stavningen.. vad är det?
En enorm komplex funktion som gör mycket mer bit-twiddling på värden utan namn.Vid det här laget bestämde jag mig för att backa tillbaka och leta efter andra felsökningsrelaterade funktioner och variabler, eftersom jag inte ens var säker på vad zuru-läget hade för betydelse. Jag var också osäker på vad ”key check” betydde här. Kunde det vara en kryptografisk nyckel?
Tillbaka till debug
Omkring den här tiden märkte jag att det fanns ett problem med hur jag laddade debugsymbolerna i IDA. Filen foresta.map
på spelskivan innehåller en massa adresser och namn för funktioner och variabler. Jag hade inte lagt märke till att adresserna för varje sektion började vid noll, så jag satte bara upp ett enkelt skript för att lägga till en namnpost för varje rad i filen.
Jag satte upp nya några IDA-skript för att fixa symbolmappens laddning för de olika sektionerna i programmet: .text
, .rodata
, .data
, och .bss
. I avsnittet .text
finns alla funktioner,så jag ställde in skriptet så att det automatiskt upptäcker funktioner på varje adress när jag ställer in ett namn den här gången.
För dataavsnitten ställde jag in det så att det skapar ett segment för varje binärt objekt (t.ex. m_debug.o
,som skulle vara kompilerad kod för något som heter m_debug
), och ställer in utrymme och namn för varje datastycke.Detta ger mig mycket mer information än vad jag hade tidigare, även om jag nu var tvungen att manuellt definiera datatypen för varje data, eftersom jag ställde in varje dataobjekt som en enkel byte array. (I efterhand hade det varit bättre att åtminstone anta att alla data med en storlek som är en multipel av 4 bytes innehöll 32-bitarsintegraler, eftersom det finns så många av dem och många innehåller adresser till funktioner och data som är viktiga för att bygga upp korshänvisningar.)
När jag tittade igenom det nya .bss
-segmentet för m_debug_mode.o
, såg jag några variabler som quest_draw_status
ochevent_status
. Dessa är intressanta eftersom jag vill få felsökningsläget att visa mer användbara saker än prestandadiagrammet. Lyckligtvis fanns det korshänvisningar från dessa dataposter till ett stort stycke kod som kontrollerar debug_print_flg
.
Med hjälp av Dolphin debugger satte jag en brytpunkt på den funktion där debug_print_flg
kontrolleras (vid 8039816C
) för att se hur kontrollen fungerar. Brytpunkten träffade aldrig.
Låt oss kontrollera varför: denna funktion anropas av game_debug_draw_last
. Gissa vilket värde som kontrolleras innan den villkorligt anropas? zurumode_flag
. Vad fan är det?
Jag satte en brytpunkt på den kontrollen (80404E18
) och den bröts omedelbart. Värdet för zurumode_flag
var noll, så den skulle normalt hoppa över att anropa denna funktion. Jag NOPped ut greninstruktionen (ersatte den med en instruktion som inte gör någonting) för att se vad som skulle hända när funktionen anropas.
I Dolphin debugger kan du göra detta genom att pausa spelet, högerklicka på en instruktion och sedan klicka på ”Insert nop”:
Inget hände. Sedan kontrollerade jag vad som hände inne i funktionen och hittade en annan greninstruktion som kunde förkorta förbi alla intressanta saker vid 803981a8
. Jag NOPpade även det och bokstaven ”D” dök upp i övre högra hörnet av skärmen.
Det fanns en hel del mer intressant kod i den här funktionen vid 8039816C
(jag kallade den zzz_DebugDrawPrint
), men inget av det blev anropat. Om du tittar på den grafiska vyn för den här funktionen kan du se att det finns en rad förgreningssatser som hoppar över block genom hela funktionen:
Genom att NOPa ut fler av dessa grenförklaringar började jag se olika saker skrivas ut på skärmen:
Nästa fråga är hur man kan aktivera dessa felsökningsfunktioner utan att modifiera koden.Dessutom dyker zurumode_flag
upp igen för vissa grenförklaringar som gjorts i denna debug draw-funktion. jag lade till en annan patch så att zurumode_flag
alltid sätts till 2 i zurumode_update
, eftersom det vanligtvis jämförs specifikt med 2 när det inte jämförs med 0.Efter att ha startat om spelet såg jag det här meddelandet ”msg. no” visas uppe till höger på skärmen.
Talet 687 är entry ID för det senast visade meddelandet. Jag kontrollerade detta med hjälp av en simpletabellvisare som jag gjorde tidigt, men du kan också kontrollera det med en fullständig GUI-redigerare för strängtabeller som jag gjorde för ROM-hackning. Så här ser meddelandet ut i editorn:
Det stod nu klart att det inte längre gick att undvika att ta reda på zuru-läget; det stack direkt in i spelets felsökningsfunktioner.
Zuru mode revisited
Vid återgång till zurumode_init
initialiseras några saker:
-
0xC(padmgr_class)
sätts till adressen förzurumode_callback
-
0x10(padmgr_class)
sätts till adressen förpadmgr_class
själv -
0x4(zuruKeyCheck)
sätts till sista biten i ett ord som laddas från0x3C(osAppNMIBuffer)
.
Jag undersökte vad padmgr
betyder och det är en förkortning för ”gamepad manager”. Detta tyder på att det kan finnas en särskild tangentkombination (knappkombination) som man måste ange på gamepaden för att aktivera zuru-läget, eller möjligen någon särskild felsökningsenhet eller utvecklingskonsolfunktion som kan användas för att skicka en signal för att aktivera det.
zurumode_init
körs endast första gången spelet laddas (att trycka på återställningsknappen utlöser den inte).
Sätt en brytpunkt vid 8040efa4
, där 0x4(zuruKeyCheck)
är inställd, så kan vi se att under uppstartenutan att hålla nere några tangenter kommer den att sättas till 0. Att ersätta detta med 1 gör att en intressant sak händer:
Bokstaven ”D” dyker upp i det övre högra hörnet igen (grönt istället för gult den här gången),och det finns också lite build-information:
En patch för att 0x4(zuruKeyCheck)
alltid ska sättas till 1 vid start:
8040ef9c 38c00001
Detta verkar vara det korrekta sättet att få zuru-läge initialiserat. Efter det kan det finnas olika åtgärder som vi måste vidta för att få viss felsökningsinformation att visas. Att starta spelet och gå runt och prata med en bybo visade ingen av de visningar som nämndes tidigare (förutom bokstaven ”D” i hörnet).
De troliga misstänkta är zurumode_update
och zurumode_callback
.
zurumode_update
zurumode_update
anropas först av zurumode_init
och blir sedan upprepade gånger anropad avzurumode_callback
.
Den kontrollerar den sista biten i 0x3C(osAppNMIBuffer)
igen och uppdaterar sedan zurumode_flag
utifrån dess värde.
Om biten är noll, sätts flaggan till noll.
Om inte, körs följande instruktion med r5
som fullt värde för 0x3c(osAppNMIBuffer)
:
Detta extraherar den 28:e biten från r5
och sparar den i r3
.Sedan adderas 1 till resultatet, så att slutresultatet alltid är 1 eller 2.
zurumode_flag
jämförs sedan med det föregående resultatet, beroende på hur mångaav den 28:e och sista biten som är inställda i 0x3c(osAppNMIBuffer)
: 0, 1 eller 2.
Detta värde skrivs till zurumode_flag
. Om det inte ändrade något avslutas funktionen och returnerar flaggans aktuella värde. Om den ändrar värdet utförs en mycket mer komplex kedja av kodblock.
Ett meddelande på japanska skrivs ut: detta är meddelandet ”zurumode_flag har ändrats från %d till %d ”som nämndes tidigare.
Därefter anropas en rad funktioner med olika argument beroende på om flaggan ändrats till noll eller inte. Monteringen för den här delen är tråkig, så pseudokoden ser ut så här:
Observera att om flaggan är noll får JC_JUTDbPrint_setVisible
argumentet 0. Om flaggan inte är noll OCH bit 25 eller bit 31 är inställda i 0x3C(osAppNMIBuffer)
fårsetVisible
-funktionen argumentet 1.
Detta är den första nyckeln till att aktivera zuru-läget: den sista biten i 0x3C(osAppNMIBuffer)
måste sättas till 1 för att göra felsökningsskärmarna synliga och sätta zurumode_flag
till ett värde som inte är noll.
zurumode_callback
zurumode_callback
ligger på 8040ee74
och anropas troligen av en funktion som är relaterad till gamepaden. När man sätter en brytpunkt på den i Dolphin debugger visar callstack att den verkligen anropas från padmgr_HandleRetraceMsg
.
En av de första sakerna den gör är att köra zerucheck_key_check
. Det är komplicerat, men överlag verkar det som om den läser och sedan uppdaterar värdet på zuruKeyCheck
. Jag bestämde mig för att se hur det värdet används i resten av callback-funktionen innan jag går vidare till nyckelkontrollfunktionen.
Nästan kontrollerar den några bitar i 0x3c(osAppNMIBuffer)
igen. Om bit 26 är inställd, eller om bit 25 är inställd och padmgr_isConnectedController(1)
returnerar icke-noll, så sätts den sista biten i 0x3c(osAppNMIBuffer)
till 1!
Om ingen av dessa bitar är inställda, eller om bit 25 åtminstone är inställd men padmgr_isConnectedController(1)
returnerar 0, så kontrollerar den om byte vid 0x4(zuruKeyCheck)
är 0. Om den är det, så nollställer den den sista biten i det ursprungliga värdet och skriver tillbaka det till 0x3c(osAppNMIBuffer)
.Om inte, så sätter den fortfarande den sista biten till 1.
I pseudokod ser detta ut som:
Efter det, om bit 26 inte är satt, så kortar den till att kalla zurumode_update
och avslutar sedan.
Om det är satt, så om 0x4(zuruKeyCheck)
inte är noll, så laddar den upp en formatsträng där det verkar som att den ska skriva ut: ”ZURU %d/%d”.
Recap
Här är vad som händer:
padmgr_HandleRetraceMsg
anropar zurumode_callback
.Min gissning är att ”handle retrace message” betyder att den just har skannat av tangenttryckningar på styrenheten. Varje gång den skannar kan den anropa en rad olika callbacks.
När zurumode_callback
körs kontrollerar den de aktuella tangenttrycken (knapptryckningarna).Det verkar som om den kontrollerar en specifik knapp eller en kombination av knappar.
Den sista biten i NMI-bufferten uppdateras baserat på specifika bitar i dess currentvalue, samt värdet av en av zuruKeyCheck
-bytena (0x4(zuruKeyCheck)
).
Därefter körs zurumode_update
och kontrollerar den biten. Om den är 0 kommer zuru-lägesflaggan att beset till 0. Om den är 1 uppdateras lägesflaggan till 1 eller 2 beroende på om bit 28 är inställd.
De tre sätten att aktivera zuru-läget är:
- Bit 26 är satt i
0x3C(osAppNMIBuffer)
- Bit 25 är satt i
0x3C(osAppNMIBuffer)
, och en styrenhet är ansluten till port 2 -
0x4(zuruKeyCheck)
är inte noll
osAppNMIBuffer
Vad var osAppNMIBuffer
, Jag började med att söka efter ”NMI” och hittade hänvisningar till ”non-maskable interrupt” i samband med Nintendo. Det visade sig att hela variabelnamnet också förekommer i utvecklardokumentationen för Nintendo 64:
osAppNMIBuffer är en 64-byte buffert som rensas vid en kall återställning. Om systemet startas om på grund av en NMI är den här bufferten oförändrad.
Det här är i princip en liten del av minnet som finns kvar vid mjuka omstarter. Ett spel kan använda den här bufferten för att lagra vad det vill så länge konsolen är påslagen.Det ursprungliga Animal Crossing-spelet släpptes faktiskt på Nintendo 64, så det är logiskt att något sådant här skulle dyka upp i koden.
Om man går över till den binära boot.dol
-versionen (allt ovan är från foresta.rel
) finns det många referenser till osAppNMIBuffer
i main
-funktionen. En snabb titt visar att det finns en rad kontroller som kan resultera i att olika bitar i 0x3c(osAppNMIBuffer)
sätts med OR-operationer.
Interessanta OR-operandvärden att hålla utkik efter skulle vara:
- Bit 31: 0x01
- Bit 30: 0x02
- Bit 29: 0x04
- Bit 28: 0x08
- Bit 27: Kom ihåg att bitarna 25, 26 och 28 är särskilt intressanta: 25 och 26 bestämmer om zuru-läget är aktiverat och bit 28 bestämmer flaggans nivå (1 eller 2).Bit 31 är också intressant, men verkar i första hand uppdateras baserat på värdena för de andra.
Bit 26
Först upp: vid
800062e0
finns enori r0, r0, 0x20
-instruktion på buffertvärdet vid0x3c
. Detta skulle sätta bit 26, vilket alltid resulterar i att zuru-läget är aktiverat.För att biten ska sättas måste den åttonde byte som returneras från
DVDGetCurrentDiskID
vara0x99
.Detta ID finns i början av spelskivans avbildning och laddas upp vid80000000
i minnet. För en vanlig detaljhandelsutgåva av spelet ser ID:47 41 46 45 30 31 00 00 GAFE01..
Patchning av den sista byte av ID:t till
0x99
gör att följande händer när spelet startas:Och i OS-konsolen skrivs följande ut:
06:43:404 HW\EXI_DeviceIPL.cpp:339 N: ZURUMODE2 ENABLE08:00:288 HW\EXI_DeviceIPL.cpp:339 N: osAppNMIBuffer=0x00000078
Alla andra patchar kan tas bort, och bokstaven D visas också i det övre högra hörnet av skärmen igen, men ingen av de andra felsökningsskärmarna är aktiverade.
Bit 25
Bit 25 används i samband med att kontrollen av styrenheten för port 2 utförs. Vad gör att den aktiveras?
Detta visar sig ha samma kontroll som används för bit 28: versionen måste vara större än eller lika med
0x90
. Om bit 26 var inställd (ID är0x99
), kommer båda dessa bitar också att vara inställda, och zuru-läget kommer att aktiveras i alla fall.Om versionen är mellan
0x90
och0x98
aktiveras dock inte zuru-läget omedelbart.Om vi minns kontrollen som gjordes izurumode_callback
, kommer det att aktiveras endast om bit 25 är inställd ochpadmgr_isConnectedController(1)
returnerar icke-noll.Så snart en styrenhet är inkopplad i port 2 (argumentet tillisConnectedController
är noll-indexerat), aktiveras zuru-läget. Bokstaven D och byggnadsinformationen visas på titelskärmen, och… genom att trycka på knapparna på den andra handkontrollen styrs felsökningsskärmarna!Vissa knappar gör också andra saker än att ändra skärmen, t.ex. ökar spelets hastighet.
zerucheck_key_check
Det sista mysteriet är
0x4(zuruKeyCheck)
. Det visar sig att detta värde uppdateras av den jättelika komplexa funktion som visades tidigare:Med hjälp av Dolphins felsökare kunde jag konstatera att det värde som kontrolleras av denna funktion är en uppsättning bitar som motsvarar knapptryckningar på den andra kontrollenheten. Spårningen av knapptryckningar lagras i ett 16-bitarsvärde på
0x2(zuruKeyCheck)
. När ingen styrenhet är inkopplad är värdet0x7638
.De 2 bytes som innehåller flaggor för knapptryckningar på styrenhet 2 laddas och uppdateras sedan i början av
zerucheck_key_check
. Det nya värdet skickas in medregisterr4
avpadmgr_HandleRetraceMsg
när den anropar callback-funktionen.Närmast i slutet av
zerucheck_key_check
finns det faktiskt ett annat ställe där0x4(zuruKeyCheck)
uppdateras. Det fanns inte med i listan över korshänvisningar eftersom det använderr3
som basadress, och vi kan bara ta reda på vadr3
är genom att titta på vad den sätts till varje gång denna funktion ska anropas.Vid
8040ed88
skrivs värdet avr4
till0x4(zuruKeyCheck)
. Det laddas från samma plats och sedan XORd med 1 strax före det. Vad detta bör göra är att växla bytevärdet (egentligen bara den sista biten) mellan 0 och 1. (Om det är 0 blir resultatet avXORing av det med 1 1.) Om den är 1 blir resultatet 0. Slå upp sanningstabellen för XOR för att se detta.)Jag märkte inte det här beteendet när jag tittade på minnesvärdena tidigare, men jag ska försöka bryta den här instruktionen i felsökaren för att se vad som händer. Det ursprungliga värdet laddas vid
8040ed7c
.Och utan att röra några knappar på styrenheten träffar jag inte denna brytpunkt under titelskärmen. För att nå det här kodblocket måste värdet för
r5
vara0xb
före den greninstruktion som kommer före den (8040ed74
). Av de många olika vägar som leder fram till det blocket finns det en som sätterr5
till0xb
före det, vid8040ed68
.Bemärk att för att nå blocket som sätter
r5
till0xB
måster0
ha varit lika med0x1000
strax innan. Genom att följa blocken uppåt i kedjan till funktionens början kan vi se de begränsningar som krävs för att nå detta block:- 8040ed74:
r5
måste vara0xB
- 8040ed60:
r0
måste vara0x1000
- 8040ebe8:
r5
måste vara0xA
- 8040ebe4:
r5
måste vara mindre än0x5B
- 8040eba4:
r5
måste vara större än0x7
- 8040eb94:
r6
måste vara 1 - 8040eb5c:
r0
får inte vara 0 - 8040eb74: Port 2 button values must have changed
Här når vi den punkt där de gamla knappvärdena laddas och de nya värdena lagras. Därefter tillämpas ett par operationer på de nya och gamla värdena:
old_vals = old_vals XOR new_valsold_vals = old_vals AND new_vals
XOR-operationen markerar alla bitar som har ändrats mellan de två värdena. AND-operationen maskerar sedan den nya ingången för att ta bort alla bitar som för närvarande inte är inställda. Resultatet i
r0
är uppsättningen nya bitar (knapptryckningar) i det nya värdet. Om det inte är tomt är vi på rätt väg.För att
r0
ska bli0x1000
måste den fjärde av de 16 knappspårningsbitarna just ha ändrats.Genom att sätta en brytpunkt efter XOR/AND-operationen kan jag räkna ut vilken knapptryckning som orsakar detta: det är START-knappen.Nästkommande fråga är hur jag ska få
r5
att börja som0xA
.r5
ochr6
laddas från0x0(zuruKeyCheck)
i början av tangentkontrollfunktionen och uppdateras mot slutet när vi inte använder kodblocket som växlar0x4(zuruKeyCheck)
.Det finns några ställen strax innan där
r5
sätts till0xA
:8040ed50
8040ed00
8040ed38
8040ed38
-
8040ed34
:r0
måste vara0x4000
(B-knappen trycktes ned) -
8040ebe0
:r5
måste vara0x5b
-
8040eba4
:r5
måste vara större än0x7
- Samma som tidigare hädanefter…
r5
måste börja på0x5b
8040ed00
-
8040ecfc
:r0
måste vara0xC000
(A och B tryckta) -
8040ebf8
:r5
måste vara >= 9 -
8040ebf0
:r5
måste vara mindre än 10 -
8040ebe4
:r5
måste vara mindre än0x5b
-
8040eba4
:r5
måste vara större än0x7
- Samma som tidigare hädanefter…
r5
måste börja vid 98040ed50
-
8040ed4c
:r0
måste vara0x8000
(A trycktes in) -
8040ec04
:r5
måste vara mindre än0x5d
-
8040ebe4
:r5
måste vara större än0x5b
-
8040eba4
:r5
måste vara större än0x7
- Samma som tidigare från och med nu…
r5
måste börja vid0x5c
Det verkar som om det finns något slags tillstånd mellan knapptryckningar, och sedan måste en viss sekvens av knappkombinationer anges, som slutar med START. Det verkar som om A och/eller B kommer strax före START.
Följer man kodvägen som sätter
r5
till 9, framträder ett mönster:r5
är ett stigande värde som antingen kan ökas när det korrekta värdet för knapptryckning hittas ir0
, eller återställas till 0. De konstigare fallen där det inte är ett värde mellan0x0
och0xB
inträffar när man hanterar steg med flera knappar, t.ex. när man trycker på A och B samtidigt. En person som försöker mata in denna kombination kommer vanligtvis inte att trycka på båda knapparna vid exakt samma tidpunkt som pad trace uppstår, så den måste hantera att en av knapparna trycks in före den andra.Fortsätter med de olika kodvägarna:
-
r5
sätts till 9 när RIGHT trycks in vid8040ece8
. -
r5
sätts till 8 när C-stick höger trycks på8040eccc
. -
r5
sätts till 7 när C-stick vänster trycks på8040ecb0
. -
r5
sätts till 6 när LEFT trycks på8040ec98
. -
r5
sätts till 5 (och r6 till 1) när DOWN trycks på8040ec7c
. -
r5
sätts till 4 när C-stick upp trycks på8040ec64
. -
r5
sätts till 3 när C-stick ned trycks på8040ec48
. -
r5
sätts till 2 när UP trycks på8040ec30
. -
r5
sätts till 1 (ochr6
till 1) när Z trycks på8040ec1c
.
Den aktuella sekvensen är:
Z, UP, C-DOWN, C-UP, DOWN, LEFT, C-LEFT, C-RIGHT, RIGHT, A+B, START
Ett ytterligare villkor kontrolleras före Z-kontrollen: samtidigt som den nyss intryckta knappen måste vara Z, måste de aktuella flaggorna vara
0x2030
: de vänstra och högra stötfångarna måste också vara intryckta (de har värdena0x10
och0x20
). Dessutom är UP/DOWN/LEFT/RIGHT knapparna påD-pad, inte analoga pinnar.Fuskkoden
Den fullständiga kombinationen är:
- Håll L+R-bumpers och tryck på Z
- D-UP
- C-DOWN
- C-UP
- D-DOWN
- D-.LEFT
- C-LEFT
- C-RIGHT
- D-RIGHT
- A+B
- START
Det fungerar! Anslut en styrenhet till den andra porten och ange koden, och felsökningsskärmarna kommer att visas. Därefter kan du börja trycka på knappar på den andra (eller till och med tredje) kontrollenheten för att börja göra saker.
Den här kombinationen fungerar utan att du behöver patcha spelets versionsnummer.Du kan till och med använda den här på en vanlig detaljhandelskopia av spelet utan några fuskverktyg eller konsolmods. Om du anger kombinationen en andra gång stängs zuru-läget av igen.
Meddelandet ”ZURU %d/%d” i
zurumode_callback
används för att skriva ut statusen för den här kombinationen om du anger den när disk-ID:t redan är0x99
(förmodligen för att felsöka fuskkoden i sig själv). Det första numret är din nuvarande position i sekvensen, som matcharr5
. Det andra talet sätts till 1 när vissa knappar i sekvensen hålls nedtryckta, dessa kan motsvara närr6
sätts till 1.De flesta av displayerna förklarar inte vad de är på skärmen, så för att ta reda på vad de gör måste du hitta de funktioner som hanterar dem. Till exempel är den långa raden av blå och röda rutor som visas högst upp på skärmen platshållare för att visa statusen för olika uppdrag. När ett quest är aktivt kommer några siffror att visas där, vilket indikerar questens status.
Den svarta skärmen som visas när du trycker på Z är en konsol för att skriva ut felsökningsmeddelanden, men specifikt för saker på låg nivå som minnesallokering och heapfel eller andra dåliga undantag. Beteendet hos
fault_callback_scroll
tyder på att den kan vara till för att visa dessa fel innan systemet startas om. Jag utlöste inget av dessa fel, men jag kunde få den att skriva ut ett par skräptecken med några NOPs. Jag tror att detta skulle vara väldigt användbart för att skriva ut egna felsökningsmeddelanden senare:Efter att ha gjort allt detta, upptäckte jag att det redan är känt att man får felsökningsläget genom att patcha versions-ID till
0x99
: https://tcrf.net/Animal_Crossing#Debug_Mode. (De har också några bra anteckningar om vad de olika displayerna är, och fler saker du kan göra med hjälp av en kontrollant i port 3.) Såvitt jag kan se har dock inte fuskkombinationen publicerats ännu.Det var allt för det här inlägget. Det finns fortfarande några fler utvecklarfunktioner som jag skulle vilja utforska, till exempel debug map screen och NES emulator select screen, och hur man aktiverar dem utan att använda patchar.
Jag kommer också att publicera skrivelser om hur man vänder på dialog-, event- och quest-systemen i syfte att göra moddar.
Uppdatering: Slidorna för det föredrag jag höll om detta finns här.
- 8040ed74: