Reverse Engineering Engineering Animal Crossing Mode

Using the code on a real gamecube

Last summer, I started reverse engineering of the Animal Crossing game for GameCube. I wanted to explore the possibility of creating mods for this game. In addition, I wanted to document the process to create tutorials for people interested in ROM hacking and reverse engineering. In this post, I’ll talk about the developer’s debugging features that were left in the game, and I’ll also share how I found a cheat combo that can be unlocked.

new_Debug_mode


Studying the remaining debugging symbols, I noticed the names of the functions and variables containing the word “debug”, and decided that it would be interesting to see if some debugging functionality remained in the game. If I manage to activate the debugging or development functions, it will help me in the process of creating mods.

The first function I noticed was new_Debug_mode . It is called by the entry function, which starts immediately after the completion of the screen with the Nintendo logo. All that it does is that it places the byte structure 0x1C94 and stores a pointer to it.

After its call to the entry in the placed structure at offset 0xD4 immediately before the call, mainproc is set to 0.


To see what happens when the value is not zero, I patched the li r0, 0 instruction at 80407C8C , replacing it with li r0, 1 . The raw bytes of the li r0, 0 instruction are 38 00 00 00 , where the assigned value is at the end of the instruction, so I could just replace the bytes with 38 00 00 01 and get li r0, 1 . As a more reliable way of assembling instructions, you can use something like kstool :

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


In the Dolphin emulator, this patch can be applied by going to the “Patches” tab in the game properties and entering it as follows:


After assigning a value of 1, an interesting graph appeared at the bottom of the screen:



It looked like an indicator of performance: the small bars at the bottom of the screen increased and diminished. (Later, when I looked at the names of the functions that render this graph, I found that they actually display the CPU and memory usage metrics.)

It was great, but not very helpful. After assigning a value of 1, my city stopped loading, so nothing could be done here.

Zuru mode


I again began to look for other references to debugging functions, and several times I came across something called “zuru mode”. The branches of code blocks with debugging functionality often checked the zurumode_flag variable.

game_move_first function

zzz_LotsOfDebug (the name I came up with myself) in the above function game_move_first is called only when zurumode_flag not zero.

Looking for functions related to this value, I found these:


At first glance, their purpose is mysterious; they juggle bits in the offset of a variable called osAppNMIBuffer .

Here is how the functions of these functions looked at first glance:

zurumode_init



zurumode_update



This is usually useful for giving context to the code, but there were a lot of non-printing characters in the string. The only recognizable text was "zurumode_flag" and "% d".

zuru mode format string

Assuming that it could be Japanese text with multibyte character encoding, I skipped the string through the encoding recognition tool and found out that the string is Shift-JIS encoded. In translation, the line simply meant "The value of zurumode_flag changed from% d to% d." This does not give us a lot of new information, but now we know that Shift-JIS is used: in binary files and tables of rows there are much more lines in this encoding.

zurumode_callback



zerumode_check_keycheck until we met because of a different writing ... what is it?

zerumode_check_keycheck

A huge complex function that does much more work on bits with values ​​without names.

At this point, I decided to take a step back and explore other debugging functions and variables, because I was not sure about the importance of zuru mode. In addition, I did not understand what “key check” means here. Is it possible that this is a cryptographic key?

Back to debug


Around this time, I noticed a problem with my way of loading debug symbols in IDA. The foresta.map file on the game disk contains many addresses and names of functions and variables. At first, I did not see that the addresses for each section start anew from scratch, so I wrote a simple script that adds a name entry for each line of the file.

I wrote new IDA scripts to fix the loading of symbol tables for different sections of the program: .text , .rodata , .data and .bss . In the .text section there are all functions, so I made sure that this time, when specifying the name, the script automatically recognized the functions at each address.

In the data sections, he now created a segment for each binary object (for example, m_debug.o , which was supposed to be compiled code for something called m_debug ), and specified the space and names for each piece of data.

This gave me a lot more information, but I had to manually set the data type for each data fragment, because I set each data object as a simple byte array. (Looking back, I understand that it would be better to assume that the multiple 4 byte fragments contained 32-bit integers, because there were a lot of them, and many contained addresses of functions and data important for building cross-references.)

Studying the new .bss segment for the presence of m_debug_mode.o , I discovered several variables of the form quest_draw_status and event_status . This is interesting because I wanted to display useful information in debug mode, not just a performance graph. Fortunately, of these data records, there were cross-references to a huge piece of code that checks debug_print_flg .

Using the debugger in the Dolphin emulator, I set a breakpoint at the function where debug_print_flg checked (at 8039816C ) in order to understand how this check works. But the program never went to this breakpoint.

Let's game_debug_draw_last why this happens: this function is called game_debug_draw_last . Guess what value is checked before its conditional call? zurumode_flag ! What the hell is going on?

zurumode_flag check

I set a breakpoint on this check ( 80404E18 ) and it immediately worked. The value of zurumode_flag was zero, so in normal execution the program would have missed calling this function. I inserted NOP instead of the branch instruction (replaced it with an instruction that does nothing) to check what happens when the function is called.

In the Dolphin debugger, this can be done by pausing the game, right-clicking on the instructions and selecting “Insert nop”:

Dolphin debugger NOPping

Nothing has happened. Then I checked what was going on inside the function and found another branching construct that went around all the interesting things happening at 803981a8 . I also inserted a NOP instead, and the letter “D” appeared in the upper right corner of the screen.

Debug mode letter D

In this function at 8039816C (I called it zzz_DebugDrawPrint ), there is a bunch of interesting code, but it is not called. If you look at this function in the form of a graph, you can see that there is a series of branch operators that pass code blocks throughout the entire function:

Branches in zzz_DebugDrawPrint

Having inserted NOP instead of several other branching constructions, I began to see various interesting things on the screen:

More debug stuff getting printed

The next question was how to activate this debugging functionality without changing the code.

In addition, in some branch constructions, zurumode_flag is again found in this debug drawing function. I added one more patch so that the zurumode_update flag zurumode_flag always zurumode_update to zurumode_flag value 2, because when it is not compared with 0, it is compared specifically with value 2.

After restarting the game, I saw the following message “msg. no.

message number display

The number 687 is the record ID of the most recently displayed message. I checked it with a table viewer, which I wrote at the very beginning of the analysis, but you can also check it with the help of a string table editor with a full GUI , which I wrote for hacking ROMs. This is what the message looks like in the editor:

Message 687 in the string table editor

At that moment, it became clear that the study of zuru mode could no longer get away - it is directly related to the debugging functions of the game.

Back to Zuru mode again.


zurumode_init initializes several things:


I figured out what padmgr , a short for gamepad manager. This means that there may be a special key combination (buttons) that you can enter on the gamepad to activate zuru mode, or some kind of debugging device or developer console function, which you can use to send a signal to activate it.

zurumode_init is executed only when the game is first loaded (when the reset button is pressed, it does not work).

By setting a breakpoint at 8040efa4 , at which the value 0x4(zuruKeyCheck) is assigned 0x4(zuruKeyCheck) , we can see that when loaded without pressing a key, the value 0 is assigned. If you replace it with 1, an interesting thing happens:

Title screen with zuru mode

The letter “D” appears again in the upper right corner (this time green, not yellow), and also some assembly information is displayed:

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


A patch that always sets a value of 1 at the beginning for 0x4(zuruKeyCheck) looks like this:

8040ef9c 38c00001

It seems that this is the right way to initialize zuru mode. After that, you may need various actions to achieve the display of certain debugging information. Having started the game, having walked along it and having spoken with a villager, we will not see any messages mentioned above (with the exception of the letter “D” in the corner).

The most likely suspects are zurumode_update and zurumode_callback .

zurumode_update


zurumode_update first called in zurumode_init , and then constantly called by the zurumode_callback function.

It again checks the last bit 0x3C(osAppNMIBuffer) and then updates the zurumode_flag based on this value.

If the bit is zero, then the flag is assigned the value zero.

If not, the following instruction is executed, with the full value of 0x3c(osAppNMIBuffer) being r5 :

extrwi r3, r5, 1, 28

It extracts the 28th bit from r5 and saves it to r3 .

Then 1 is added to the result, that is, the final result is always 1 or 2.

Then the zurumode_flag compared with the previous result, depending on how many of the 28 and last bits are specified in 0x3c(osAppNMIBuffer) : 0, 1, or 2.

This value is written zurumode_flag . If it changes nothing, then the function terminates and returns the current value of the flag. If it changes the value, then a much more complex chain of code blocks is executed.

The message is displayed in Japanese: that same “The value of zurumode_flag changed from% d to% d”, which we talked about above.

Then a series of functions is called with different arguments depending on whether the flag has become zero or not. The assembly code of this part is monotonous, so I will show its pseudocode:

 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) } 

Note that if the flag is zero, then JC_JUTDbPrint_setVisible number 0 is passed to JC_JUTDbPrint_setVisible.

If the flag is not zero and bit 25 or bit 31 is set to 0x3C(osAppNMIBuffer) , then argument 1 is passed to the setVisible function.

This is the first key to activate zuru mode: the last bit 0x3C(osAppNMIBuffer) must be set to 1 in order to display debug information and set the zurumode_flag non-zero value.

zurumode_callback


zurumode_callback is located at 8040ee74 and is probably called by a function associated with a gamepad. After inserting a breakpoint in the Dolphin debugger, the call stack shows us that it is actually called from padmgr_HandleRetraceMsg .

One of its first actions is executing zerucheck_key_check . This function is complex, but it seems that in general it is designed to read and update the value of zuruKeyCheck . Before moving on to the keycheck function, I decided to check how this value is used in the rest of the callback function.

Then it checks again some bits in 0x3c(osAppNMIBuffer) . If bit 26 is set, or if bit 25 is set and padmgr_isConnectedController(1) returns a non-zero value, then the last bit in 0x3c(osAppNMIBuffer) assigned the value 1!

If none of these bits are set, or bit 25 is set, but padmgr_isConnectedController(1) returns 0, the function checks whether the byte at 0x4(zuruKeyCheck) is equal to zero. If equal, then it resets the last bit in the original value and writes it back to 0x3c(osAppNMIBuffer) . If not, it still assigns the value 1 to the last bit.

In pseudocode, it looks like this:

 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 } 

After that, if bit 26 is not set, the function proceeds to the zurumode_update call, and then exits.

If the bit is set, then if 0x4(zuruKeyCheck) not zero, then it loads a format string in which it displays the following: “ZURU% d /% d”.

Let's summarize


This is what happens:

padmgr_HandleRetraceMsg calls zurumode_callback . I assume that this “handle retrace message” means that it simply scans the keystrokes of the controller. With each scan, it can trigger a series of different callbacks.

When performing zurumode_callback it checks the current keystrokes (buttons). It looks like she is testing a specific button or combination of buttons.

The last bit in the NMI Buffer is updated depending on the specific bits in its current value, as well as on the value of one of the zuruKeyCheck bytes ( 0x4(zuruKeyCheck) ).

Then zurumode_update is zurumode_update and checks this bit. If it is 0, the value 0 is assigned to the zuru mode flag. If it is 1, the mode flag is changed to 1 or 2, depending on whether bit 28 is set.

There are three ways to activate zuru mode:

  1. Bit 26 is set to 0x3C(osAppNMIBuffer)
  2. Bit 25 is set to 0x3C(osAppNMIBuffer) and the controller is connected to port 2
  3. 0x4(zuruKeyCheck) not zero

osAppNMIBuffer


Interested in what osAppNMIBuffer means, I began to search for “NMI” and found in the context of Nintendo references to “non-maskable interrupt”. It turns out that the name of this variable is entirely mentioned in the developer documentation for Nintendo 64:

osAppNMIBuffer is a 64-byte buffer, cleared during a cold restart. If the system restarts due to NMI, the status of this buffer does not change.

In fact, this is a small fragment of memory that is saved during a “soft” restart (with the reset button). The game can use this buffer to store any data while the console is online. The original Animal Crossing was released on Nintendo 64, so it’s logical that something like this should appear in the code.

If we go to the binary boot.dol file (everything shown above was in foresta.rel ), then in its main function there are many links to osAppNMIBuffer . A quick 0x3c(osAppNMIBuffer) that there is a series of checks that can lead to the 0x3c(osAppNMIBuffer) different 0x3c(osAppNMIBuffer) bit 0x3c(osAppNMIBuffer) using OR operations.

The following OR operand values ​​can be interesting:


We remember that bits 25, 26 and 28 are particularly interesting: 25 and 26 determine whether zuru mode is on, and bit 28 determines the flag level (1 or 2).
Bit 31 is also interesting, but it seems that it changes depending on the values ​​of the others.

Bit 26

First of all: at the address 800062e0 there is an ori r0, r0, 0x20 instruction with a buffer value of 0x3c . It sets bit 26, which always leads to the inclusion of zuru mode.

Setting bit 26

For the bit to be set, the eighth byte returned from the DVDGetCurrentDiskID must be 0x99 . This identifier is located at the very beginning of the game disk image, and is loaded into memory at the address 80000000 . In a regular retail game release, the ID looks like this:

47 41 46 45 30 31 00 00 GAFE01..

Replacing with the patch the last byte of the identifier to 0x99 , we get the following picture when starting the game:

Game version ID 0x99

And in the OS console, the following is displayed:

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


All other patches can be removed, after which the letter D will reappear in the upper right corner of the screen, but no debugging messages will no longer be activated.

Bit 25

Bit 25 is used in conjunction with checking the port of controller 2. What causes it to turn on?

Bit 25 and 28

It turns out that he should use the same check as for bit 28: the version must be greater than or equal to 0x90 . If bit 26 is set (ID is 0x99 ), then both of these bits will also be set, and zuru mode is still activated.

However, if the version is in the range from 0x90 to 0x98 , then zuru mode does not turn on instantly. Recall the test performed in zurumode_callback — the mode will be enabled only if bit 25 is set and padmgr_isConnectedController(1) returns a non-zero value.

After connecting the controller to port 2 (the isConnectedController argument is zero indexed), zuru mode is activated. The letter D and the build information appear on the initial screen, and we ... can control the debug display using the buttons of the second controller!

Some buttons perform actions that not only change the display, but also, for example, increase the speed of the game.

zerucheck_key_check


The last mystery remains 0x4(zuruKeyCheck) . It turns out that this value is updated by a huge complex function, which I showed above:

zerumode_check_keycheck

Using the Dolphin emulator debugger, I managed to determine that the value checked by this function is a set of bits corresponding to the button presses on the second controller.

Button tracking is stored in a 16-bit value in 0x2(zuruKeyCheck) . When the controller is not connected, the value is 0x7638 .

The 2 bytes containing the controller keystroke flags are loaded and then updated at the beginning of the zerucheck_key_check . The new value is transferred with the r4 register by the function padmgr_HandleRetraceMsg when it calls the callback function.

key check end

Toward the end of zerucheck_key_check there is another place where 0x4(zuruKeyCheck) updated 0x4(zuruKeyCheck) . It did not appear in the list of cross references because it uses r3 as the base address, and we can find out the value of r3 only by looking at what value is assigned to it before calling this function.

At the address 8040ed88value is r4written to 0x4(zuruKeyCheck). Right before that, but is recorded from the same place and then XOR-one from 1. The task of this operation is to switch the byte value (and in fact - the last bit) between 0 and 1. (If the value is 0, then the
XOR result from 1 will be 1 If the value is 1, then the result will be 0. See the truth table for XOR.)

key check end

, , , , , . 8040ed7c .

, . , r5 0xb , ( 8040ed74 ). , , r5 0xb , 8040ed68 .

Setting r5 to 0xb

Note that in order to reach the block that assigns the r5value 0xB, the value just before it r0must be equal 0x1000. Following the blocks up the chain before the beginning of the function, we can see all the restrictions necessary to achieve this block:


Tracing the code path

, . , :

old_vals = old_vals XOR new_vals
old_vals = old_vals AND new_vals


XOR , . AND , 0 , . r0 ( ) . , .

r0 0x1000 , 16 . XOR/AND, , START.

, r5 , 0xA . r5 r6 0x0(zuruKeyCheck) , , 0x4(zuruKeyCheck) .

, r5 0xA :


8040ed38


r5 0x5b

8040ed00


r5 9

8040ed50


r5 0x5c

, - , , START. , A / B START.

, r5 9, : r5 — , , r0 , . , 0x0 0xB, occur when processing steps with several buttons, for example, by simultaneously pressing A and B. A person trying to enter this combo usually cannot press both buttons at exactly the same time when tracking down the gamepad, so you have to handle the button that is pressed first.

We continue to explore different code paths:


The current sequence is:

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

Before checking Z, one more condition is checked: although the new pressed button must be Z, the current flags must be equal 0x2030: the left and right bumpers must also be pressed (they have the values 0x10and 0x20). In addition, UP / DOWN / LEFT / RIGHT are D-pad buttons, not analog stick.

Cheat code


A complete combo looks like this:

  1. Hold the bumpers L + R and press Z
  2. D-UP
  3. C-DOWN
  4. C-UP
  5. D-DOWN
  6. D-LEFT
  7. C-LEFT
  8. C-RIGHT
  9. D-RIGHT
  10. A + B
  11. START

Works!Connect the controller to the second port and enter the code, after which the debug information will appear. After that, you can start pressing the buttons on the second (or even third) controller to perform different actions.

This combo will work without patching the version number of the game. It can even be used in a regular retail copy of the game without any cheat tools or console mods. Re-entering the combo disables zuru mode.

Using the code on a real GameCube

The message “ZURU% d /% d” is zurumode_callbackused to display the state of this combination if you enter it when the disk ID is already equal 0x99(probably to debug the cheat code itself). The first number is your current position in the sequence, the corresponding r5. The second takes the value 1, when certain buttons of a sequence are held, they can correspond to when r6a value of 1 is assigned.

Most of the messages do not explain what they are doing on the screen, so in order to understand their purpose, it is necessary to find the functions that process them. For example, a long line of blue and red stars at the top of the screen are placeholders for displaying the status of various quests. When the quest is active, there appear some numbers indicating the status of the quest.

, Z — , , , . fault_callback_scroll , . , NOP. , :

JUTConsole garbage characters

Having done all this, I found out that getting into the debugging mode by patching the ID of the
version is 0x99already known to other people: https://tcrf.net/Animal_Crossing#Debug_Mode . (The link also contains good notes on what the various messages mean, and tells about other things that can be done with the controller in port 3.) However, as far as I know, no one has yet published the cheat combination.

That's all. There are other developer features that I would like to explore, such as the debugging screen of the card and the NES emulator selection screen, and ways to activate them without using patches.

Map select screen


In addition, I will publish articles on reverse engineering systems of dialogues, events and quests to create mods.

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


All Articles