Useful RAM values & percentage count
Guide
/
Useful RAM values & percentage count
Aggiornato 7 months ago di toca

This guide details how/where key game items are stored in RAM and how to change them — at the time of writing this I am playing the PAL version of Spyro Fusion on Bizhawk 2.8 (mGBA core). As a result of this RAM search I was also able to decipher this game's percentage system, cf. the end of this guide.

Also, abbreviations that will be used throughout this guide are: HEX (a number in hexadecimal), DEC (a number in the standard decimal system), and BIN (a number in binary).

RAM

First, to access and change the RAM values mentioned down below open the Bizhawk hex editor

and change the memory domain to Combined WRAM:

Aside from the current RNG value which — like in Crash Fusion — is located at 0x000000 in Combined WRAM, all values mentioned down below can be found at 0x006AF· through 0x006C6·, as well as or 0x00CA··. For more on how the RNG works in this game refer to the RNG knowledge base.

Findings

Everything I was able to identify in these memory regions is documented in this spreadsheet. The rest of this section will contain explanations and further information complementing the spreadsheet in question.

Gems & Sparx

The gem count is a plain 4-bit value in RAM, located at 0x00608:

While this can be changed at will (and the game will display any value you enter), if you give yourself more than 999 gems, changing the gems count in-game in any way will set it back to the game's maximum value 000003E7 (=999 in DEC).

Also, your current amount of Sparx health (0, 1, 2, 3) is stored in 0x006AFC. While freezing the value does not keep Sparx health stable throughout taking damage, setting the value to 80000000 (2147483648 in DEC) and freezing essentially amounts to invulnerability.

Level & boss completion

The way the game keeps track of how often levels have been completed is that each level corresponds to a 4-byte region in RAM. The corresponding value encodes one of five possible states the level is in:

  • If the value is 00000000, then the level has not been unlocked yet
  • If the value is 00000001, then the level has been unlocked but not yet completed
  • If the value is 00000002, the level has been completed once
  • If the value is 00000004, then the level has been completed three times.
  • If the value is anything but the ones previously listed, then the level has been completed twice. In this case the game will set the value to 00000003, although any value other than the listed ones seems to have the same effect

The value of the boss level bytes is set to 00000001 as soon as all levels of a world have been beaten. However, setting this value to 1 manually does not activate the world portal. Also, once a boss is beaten—which sets the value to 2—portal rush, gem rush, and the next world's tab (if applicable) are set to 1 meaning they are also accessible through the party tab.

Finally, the five 4-byte values between "tab unlocked in party mode" and the first level seem to not correspond to anything that is saved; at least changing the values, saving, and loading again does not lead to a permanent change.

Gem Hop

Aside from the bytes at 0x006C24 which controls whether Gem Hop is unlocked, there are three other 4-byte values relevant to this minigame. They all count the amount of gems collected in Gem Hop since last starting the minigame, but they have different functions:

  • 0x01AE58: If this count exceeds 50, then only half-size platforms are spawned on the left side of the screen
  • 0x01814C: If this count exceeds 50, then only half-size platforms are spawned on the right side of the screen
  • 0x019334: Ends the minigame if the value stores here equals (or exceeds) 100

Cutscenes & unique gems

The bytes 0x006CA4 through 0x006CB4 track which cutscenes have played already; the corresponding byte is 0 if the cutscene has not played yet, and 1 if it has.

The unique gems in each world are kept track of at 0x00CA14 through 0x00CA27. Each half of each world corresponds to a 2-byte region where collection of non-red gems is tracked.

Changing RAM: binary & hexadecimal

Let us take a quick detour to understand how we can change these RAM values to our liking by means of an example.

Let's say we already collected all unique gems in world 2-1, meaning byte 0x00CA18 consists of the bits 0 0 1 1 1 1 1 1. When checking the RAM (and the data size in the options is set to 1-byte), the value of byte 0x00CA18 reads 3F, because 00111111 in binary is 3F in hexadecimal (as verified, e.g., via a binary-hex-conversion website). Now assume we want to spawn in the top left blue gem again; we have to set the third bit of that byte to 0. As written in the top row, this Bit 3 has HEX value 20, so we subtract 20 from the current value (3F) which yields 1F. Inserting this new value into byte 0x00CA18 lets the game know that the card corresponding to the third bit has not been collected yet. Alternatively, we could of course turn a 1 from the initial bitstring into a 0 and do the BIN-HEX-conversion again. NB: We still have to make the game adopt this change to the save file in order for the gem to actually re-spawn. This will be detailed down below.

Cards

Trading cards are being kept track of twice: once for whether the player currently owns this cards, and once for whether a card has been owned at some point and then, e.g., been traded away (in this case the card is greyed out). The corresponding memory regions are 0x00F0AC - 0x00F0C4 and 0x00F0D8 - 0x00F0F0, respectively:

The requirement to get the card animation in the main menu which signifies that you got all 200 cards is that none of these card bit has a 0 in both these memory regions at the same time. In other words, if you have or at some point had every card that's enough.

card screen is triggered only once the second memory region (0x00CA68 - 0x00CA80 is complete, i.e., is equal to FF FF FF FF ...)

Missing stuff

While this concludes everything I managed to find and identify, there are some things that have eluded my grasp:

  • Checkpoint: Spyro's current checkpoint somehow kept track in 0x006AF0 onwards. 0x006AF0 somehow keeps track of which area/level the player spawns in upon a map change. I was able to warp to different levels by changing and freezing the value but I couldn't figure out how this whole memory region works exactly. Also, changing 0x006AF4 to 0 and changing worlds spawns Spyro into the start of world, value 1 corresponds to the boss portal, etc. Actually there are values which seem to not correspond to any level, those seem to send the player to coordinates (0,0), resulting in a softlock. But that's all I was able to find out so far.
  • Coordinates: I didn't even look for Spyro's x- and y-coordinates in RAM. If it's anything like in Crash Fusion, the coordinates won't be stored in a fixed RAM location anyway which makes manipulating them a lot harder.

Now there are two things we still have to address:

How to make RAM changes permanent

If we change something in RAM, how do we force the game to make these changes permanent, i.e., transfer these changes to our save file? Luckily, this game saves quite often so all we have to do is force the game into this state, without it loading the current save into RAM before that. With this in mind, the game saves whenever you:

  • exit (not enter!) a level, shop, or boss fight, either by completing it or simply by quitting out via the menu
  • change the overworld you're currently in, either by changing which half of a world you're in, or which world you're in altogether
  • collect an overworld gem
  • actually, exiting from the overworld to the main menu seems to also save the game.

Percentage count

Finally, throughout this little project I was able to figure out how percentage works in this game:

  • Beating a level for the first, second, and third time gives 1% each, so 25 levels completed three times amounts to 75% completion
  • Defeating a boss gives 5%, that is, 5 bosses give 25% combined.

So far we tracked 100% of completion, so the final 13% have to come from trading cards, more specifically, from 13 very specific cards (owning them gives 1% each). These cards are:

  • Firefly (Blue, from W5 Vase Shuffle)
  • Castle Talisman (Green, from W1-1 all unique gems)
  • Arctic Talisman (Green, from W2-1 all unique gems)
  • Fire Talisman (Green, from W3-1 all unique gems)
  • Jungle Talisman (Green, from W4-1 all unique gems)
  • Tech Talisman (Green, from W5 all unique gems. Unlocks the level "Gem Hop")
  • S Sheep (Orange, from W2 Sheep)
  • P Sheep (Orange, from W1 Sheep)
  • Y Sheep (Orange, from W3 Sheep)
  • R Sheep (Orange, from W4 Sheep)
  • Spit (Orange, from W1-2 all unique gems)
  • Agent 9 (Red, from W4-2 all unique gems)
  • Moneybags (Red, from W3-2 all unique gems)

In total this yields the well-known max percentage of 113%. Interestingly, this means that one could theoretically have a 0% save file with 187 cards.

The last thing I want to share is a "minimal 100% RAM string", that is, the smallest possible string which, when inserted at 0x006B28 — just copy-paste works in HEX editor — yields a save with 100% completion & all party mode tabs unlocked:

0400000004000000040000000400000004000000000000000000000002000000000000000000000000000000010000000000000000000000000000000000000000000000010000000400000004000000040000000400000004000000000000000000000002000000000000000000000000000000010000000000000000000000000000000000000000000000010000000400000004000000040000000400000004000000000000000000000002000000000000000000000000000000010000000000000000000000000000000000000000000000010000000400000004000000040000000400000004000000000000000000000002000000000000000000000000000000010000000000000000000000000000000000000000000000020000000400000004000000040000000400000004000000000000000000000002000000

So if you start a new game and paste this string into RAM you will have a 100% file where absolutely nothing else is done: all barriers are intact and all cutscenes & non-red overworld gems are where they're supposed to be. Analogously, if you don't want any cutscenes or info textboxes, insert the following string at 0x006CA4:

0101010001010000010101010101010101000000