Spyro: Year of the Dragon
By looking up some of the gameshark codes for this game, we can determine the following memory addresses:
0x8007582cis the number of lives.
0x80078bbcis the health of Spyro.
0x80075860is the number of unspent jewels available to the player.
0x80075750is the number of dragons Spyro released so far.
With this, we can build a small UI to visualize and manipulate these values in real time:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55
You can see this code in action in this demo video.
Using exactly the same as above, we can repeat the same sort of cheats for Crash Bandicoot. Note that when the CPU is being emulated, the
DrawImguiFrame function will be called at least when the emulation is issuing a vsync event. This means that cheat codes that regularly write to memory during vsync can be applied naturally.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Crash Bandicoot - Using Conditional BreakPoints
This example will showcase using the BreakPoints and Assembly UI, as well as using the Lua console to manipulate breakpoints.
Crash Bandicoot 1 has several modes of execution. These modes tell the game what to do, such as which level to load into, or to load back into the map. These modes are passed to the main game loop routine as an argument. Due to this, manually manipulating memory at the right time with the correct value to can be tricky to ensure the desired result.
The game modes are listed here.
In Crash 1, there is a level that was included in the game but cut from the final level selection due to difficulty, 'Stormy Ascent'. This level can be accessed only by manipulating the game mode value that is passed to the main game routine. There is a gameshark code that points us to the memory location and value that needs to be written in order to set the game mode to the Story Ascent level.
30011DB0 0022- This is telling us to write the value 0x0022 at memory location
0x8001db00x0022 is the value of the Stormy Ascent level we want to play.
The issue is that GameShark uses a hook to achieve setting this value at the correct time. We will set up a breakpoint to see where the main game routine is.
Setting the breakpoint can be done through the Breakpoint UI or in the Lua console. There is a link to a video at the bottom of the article showing the entire procedure.
Breakpoints can alternatively be set through the Lua console. In PCSX-Redux top menu, click Debug → Show Lua Console
We are going to add a breakpoint to pause execution when memory address 0x8001db0 is read. This will show where the main game loop is located in memory.
In the Lua console, paste the following hit enter.
You should see where the breakpoint was added in the Lua console, as well as in the Breakpoints UI. Note that we need to assign the result of the function to a variable to avoid garbage collection.
Now open Debug → Show Assembly
Start the emulator with Crash Bandicoot 1 SCUS94900
Right before the BIOS screen ends, the emulator should pause. In the assembly window we can see a yellow arrow pointing to
0x80042068. We can see this is a
lw instruction that is reading a value from
0x8001db0. This is the main game loop reading the game mode value from memory!
Now that we know where the main game loop is located in memory, we can set a conditional breakpoint to properly set the game mode value when the main game routine is executed.
This breakpoint will be triggered when the main game loop at
0x80042068 is executed, and ensure the value at
0x80011db0 is set to
In the Lua console, paste the following and hit enter.
1 2 3
We can now disable/remove our Read breakpoint using the Breakpoints UI, and restart the game. Emulation → Hard Reset
If the Emulator status shows Idle, click Emulation → Start
Once the game starts, instead of loading into the main menu, you should load directly into the Stormy Ascent level.
You can see this in action in this demo video.