Help:Contents/Finding Content/Finding menus and areas
Grab yourself a RAM editor and dive right in! That's pretty much all you need.
- 1 Finding menus (Basic)
- 2 Finding Menus (Intermediate - Advanced)
- 3 Finding areas
- 4 Misc
There are two ways to enter unused/hidden menus. Either force the game to choose an out-of-bounds "option" that will open it, or tell the game to open the pretended menu instead of the correct one. In theory, during development, the game had the entrances to these menus available, and when it was time to remove them, the options were merely left invisible and non-selectable. If you can force the cursor to choose said options, you may be able to open the menus once again.
Start by highlighting the first option on the main menu. Then, search the RAM for an unsigned byte with the value 0. Then, highlight the second option (if they're not organized linearly, try different options), and check for 1. Keep doing the same with other menus until you'll eventually find the address that stores what option you have highlighted. Now comes the easy part, just change that value to something out-of-bounds and you may just see the cursor pointing to nowhere. Now, cross your fingers, press A, or Start or whatever, and hope that a debug menu pops up in front of you. If you couldn't make it, try it again with different values. Maybe the first option is actually 1, not 0, maybe the second/third/... option isn't the one you thought it was, or, of course, maybe the menu isn't there to begin with.
- Examples of games with debug menus accessed this way: Light Crusader, Pac-Man 2: The New Adventures (SNES)
If that fails, you could try to force the game to open a different menu. If you have several sub-menus, enter the main menu. Search for an unsigned byte, valued with 0. Enter a sub-menu, the one you think is the first one, and search for 1. Keep doing the same, trying to guess which menu has what value, and you should run into an address that stores what (sub-)menu you're in. Keep changing that value, and attempting to reload the menu, and you could enter the debug menu. Again, keep trying with different values in different places. If you can't have it work with menus and sub-menus, try entering the options screen and search for an unknown value. Then try the main menu and search for a different one. Then the password, etc.
- Examples of games with debug screens accessed this way: Super Smash Bros.
Finding Menus (Intermediate - Advanced)
This guide aims to help people with little to no knowledge at disassembly. It will be easier to digest if you already know a bit about reverse engineering. While this guide solely covers consoles that use MIPS (N64, PS1, PS2, and PSP), these methods would be able to work for other systems as well with the right disassembly tools and reverse lookup methods. A few things we will need:
- ps2dis https://www.romhacking.net/utilities/692/
- Project 64 https://www.pj64-emu.com/nightly-builds
- This byte flipper tool (converts Little Endian to Big Endian and vice versa)
Lets take a look at our first game, Bomberman Hero for the Nintendo 64.
Bomberman Hero Example (Basic - Intermediate Level)
First lets take a memory dump while in-game. We will need to enable the debugger in Project 64 if we haven't already (Options > Settings > Advanced > Enable Debugger). Under Debugger, choose Memory > Dump... This will save a memory dump in big endian format. ps2dis only reads Little Endian byte order. Lets open up our byte flipper tool and load the big endian file. This will now output a little endian file as well in the same directory with a double file extension attached to it. What we will be doing is taking both memory dumps and loading them into ps2dis.
Finding Debug Text in ps2dis
Open ps2dis and drag and drop the big endian memory dump first into the main window. You will be prompted with a Load From and Address From box. Since we don't need to do anything special with the boxes, simply click OK. Now load the little endian dump into the main window.
Now, lets click Edit > Jump to Labeled. Immediately we can see text that is quite interesting.
Lets click on " G BUTTON DEBUG = %d". This will take us to the memory location of that text which is 001343a4 (In actually N64 memory, the first zero will be an 8 which translates to 801343a4). At this location, lets press Spacebar to mark it then press F3 to see if there is any simple game functions that point to this text. After which it will prompt with with another text box. Click OK. This does automatic scans for function calls and memory references.
There is indeed one reference to this text at location 000fea94 which is inside of a function. Lets scroll up a bit with the Up arrow key until we see a FNC_xxxxxxxx text which indicates the starting of a function. Upon scrolling, we will also see several more interesting text. Looks like we are on the right path.
The top of this function is 000fe9bc. You can also generally spot a function with the op code addiu sp, sp, $xxxx. xxxx in this case will be the reserved stack space for the function. Sometimes function calls are also labeled as __xxxxxxxx, where the x's are what would be the starting function memory location. What differs here is that these are generally referenced later in some other function and loaded at a later point rather than immediately.
Running the Debug Menu
Lets mark the top of the function (000fe9bc) with Spacebar and then press F3. We will be taken to memory location 000ff83c which takes us to a JAL which calls function 000fe9bc (our group of debug text we just found) then will render the debug menu text if the game ran this function.
- JAL (Jump And Link) jumps to a new function call and increments the current address that the JAL was at by +8 and stores that address as a return address in the RA register. Once the call is finished and hits the op code jr ra (Jump Register, Return Address) it will return to the location held in register RA.
Open Project 64, enter the game and then open the breakpoint window by clicking on Debugger > Breakpoint. Lets put a breakpoint on 000ff83c by double clicking that spot. Now resume the game by clicking on the main window (remember to replace the first zero with an 8 for actual N64 memory). Obviously, the game isn't running to this memory location or the game would have paused again. Lets scroll up to the top of the function which is location 800ff7b4 and put a breakpoint on it.
Clicking on the main game window again and we will be thrown back to the breakpoint window. This means the game is running this function to a degree. Good, we are on the right path. Lets undo our latest breakpoint at 800ff7b4 and scroll down and put another breakpoint on 800ff7d4 and click Go. The game will now highlight this memory location. This is loading a signed byte from memory location in S0 +E3EC which translates to 8016e3ec. If we click Step we can see S0 be filled with zero. Lets undo our latest breakpoint at 800FF7D4 and click Go. Now lets go to that interesting memory location at 8016e3ec by clicking Debugger > Memory > View. In the address box, paste 8016E3EC. Lets try poking this location with a value of 1.
Interesting. Now we have some debug text displayed... but not what we want. Lets just put zero back at that memory location for now. Lets go back to our breakpoint window at 800FF7D4. If we scroll down by one address we come to 800FF7D8 which has the op code addiu at, r0, $0001. This loads the value 00000001 into register AT. The next op code is BEQ S0, AT, 0x800FF81C which will branch to location 800FF81C if S0 and AT registers are equal. This jumped to that location previously when we saw that debug text render on-screen. But what branches to our debug menu text we just found? It's surely part of this function. Try and see if you can spot the branch that would take us to 800FF83C (hint: look at memory locations 800FF7F0, and 800FF7F4). If you guessed value 64 in S0 (8016e3ec), you are correct.
Value 64 does indeed display our debug menu... however we cannot control the menu when it is executed like this as the function that contains the debug menu text only renders the text and does not control player inputs.
Back in ps2dis, lets go to memory location 800ff7d4 and press the right arrow key. This will take us back to our memory location that controlled what menu or debug text to display (8016e3ec). You can also go back to your previous entry with the left arrow key. Now that we are at 8016e3ec again, lets mark it with Spacebar and press F3 to see what references this spot. What we are looking for is something that stores the value 64 to this memory location. As we previously learned, this memory location is handled as a single byte. After pressing F3 three times, we come to the location 80024484.
We can see sb t1, $e3ec(at). Lets find what the value t1 holds by scrolling up a bit. Location 8002447c indeed loads t1 with value 64. Excellent. This is what we want. Above that, we can see some branches that look to be skipping this part of code. We can assume so because value 64 is not currently at that address. Lets scroll up a bit more until we see the op code lhu t6, $e384(t6) at 80024438. If we press the right arrow key over this address, we come to 8016E384. If we observe this memory location while the game is running, we can see it change values when the player presses buttons. Back in the breakpoint window, lets put another break on 80024438 which loads an unsigned half word (16 bit value) into the register t6. Nothing. Weird. We seemed to be really close. What happens when we pause the game? Oh, we got a hit! We can assume while the player is in the pause menu, the game expects a certain button press. To cut things short, it expects the L button to be pressed. If L is pressed, it will resume and not take the branch at 80024444. Undo our last breakpoint and place one at 80024450 which is a few addresses below our previous one. Lets press L again in the pause screen, and click Step once the game pauses from hitting the break. Click Step until we reach 80024450. This loads a byte into t8 (memory location 8016e424) and the branch below would skip the portion of code responsible for storing our value of 64 if it was zero (hint: it is always zero) which would display the debug menu.
Finally, we can once again go to the memory location that t8 loads be pressing right arrow key over 80024450 in ps2dis. This will take us to 8016e424. Lets now place a value of 1 here and now press L in the pause menu.
Success. We now have our debug menu and can fully control it. We can make this into a Gameshark code by pasting this into the cheat region of an emulator or cheat device: 8016E424 0001.
It's mostly the same thing, really. If you can notably tell what separates an area from another (for instance, in Metroid, that'd be every time you enter a room), you might be able to force the game to load a different area. Just use the same method, and try all types of values until you hit an area that doesn't exist in the game.
If you can alter the game's files, a pretty obvious and immediate way to access unused areas is by replacing the file of a normal area with the file of an unused one. This isn't guaranteed, though, as some internal aspects in the game might react wrongly when the wrong files are used.
Using Djinn Tile Mapper
Maybe a screenshot of a game in which this works well?
With Djinn Tile Mapper, you can find areas mapped with tiles. Choose a page from the right window, and navigate the ROM using the left window. It'll fill every byte found in the ROM with the corresponding tile from the page. It's hard to find the right page, in the right place, and then find the right spot on the ROM with the map, and that is all implying the tiles and maps aren't compressed. But if you're really lucky, you might just be able to find something.
- Walk-through wall codes
- Out-of game Level editors
- "Forward", "Play", and "Back" cheats (Adobe Flash)