Hacking FF7: Data Maps

Hacking FF7: Data Maps

This series of posts is going to look at hacking into the memory of a much-beloved game from the 90's: Final Fantasy VII. Specifically, we're looking at the PC release (whether it be the original, or the Steam re-release, it doesn't make much difference).

To begin with, we're going to start with 2 prime locations for data we can use. Data save map, and the battle map. These will hold the majority of the useful data for us.

Save Map

The save map is a series of binary data that gets saved to the memory card or save file when the game is saved. It is also updated whenever changes occur within the Field (i.e. not in battle) as we play the game. Having a good idea of at least some of how this is formatted will help us utilise this information.

In the English translations of the game, the save map begins at offset 0xDBFD38, and has a length of 0x10F4 bytes. This is the ff7.exe in the original release, and ff7_en.exe in the steam release.

All offsets are slightly different in other official languages for the game. However, mod-translations based on the English version retain the same offsets as the English version.

OffsetLengthDescription
0x40x26Save Preview (information shown on the Load Game screen)
0x480xBWindow Colour
0x540x80 [Character]Cloud
0xD80x80 [Character]Barret
0x15C0x80 [Character]Tifa
0x1E00x80 [Character]Aeris
0x2640x80 [Character]Red XIII
0x2E80x80 [Character]Yuffie
0x36C0x80 [Character]Cait Sith / Young Cloud
0x3F00x80 [Character]Vincent / Sephiroth
0x4740x80 [Character]Cid
0x4F80x1Party Member 1
0x4F90x1Party Member 2
0x4FA0x1Party Member 3
0x4FC0x280Items Inventory
0x77C0x320Materia Inventory
0xA9C0xC0Stolen Materia
0xB7C0x3Gil
0xB800x4Number of Seconds Played

Obviously, there's more to the save map than just this, but this gives us a good start without getting too long. For a full list of what I've found so far, please see my SaveMapOffsets file on GitHub

You may have also noticed that within some of the records, I've denoted a [Character] type for the data. This is a common structure used for each character, and resembles the following:

OffsetDescription
0x0Character Id
0x1Level
0x2Strength
0x3Vitality
0x4Magic
0x5Spirit
0x6Dexterity
0x7Luck
0x8Strength Bonus
0x9Vitality Bonus
0xAMagic Bonus
0xBSpirit Bonus
0xCDexterity Bonus
0xDLuck Bonus
0xELimit Level
0xFLimit Gauge
0x10Name
0x1CEquipped Weapon
0x1DEquipped Armor
0x1EEquipped Accessory
0x1FStatus Flags
0x20Row
0x21Level Progress
0x22Limit Breaks
0x24Number of kills
0x26Limit Level 1 Uses
0x28Limit Level 2 Uses
0x2ALimit Level 3 Uses
0x2CCurrent HP
0x2EBase HP
0x30Current MP
0x32Base MP
0x38Max HP
0x3AMax MP
0x3CCurrent EXP
0x40Weapon Materia
0x60Armor Materia
0x80EXP to next level

As you can see, we've got some quite rich information about our characters right now. Unfortunately, this is only half of the story when it comes to our party. You might remember that I mentioned the save map is updated on the field. What this means is that during battle, this data isn't updated until the battle ends. To compliment this data, lets look at the Battle Map next.

Battle Map

The battle map is a shorter set of memory used when a battle is active. This can be found at offset 0x9AB0DC, and has a length of 0x750 bytes. It's comprised of a series of actors, each holding some basic information about the combatants:

OffsetLengthDescription
0x00x68 [BattleActor]Party Member 1
0x680x68 [BattleActor]Party Member 2
0xD00x68 [BattleActor]Party Member 3
0x1A00x68 [BattleActor]Enemy 1
0x2080x68 [BattleActor]Enemy 2
0x2700x68 [BattleActor]Enemy 3
0x2D80x68 [BattleActor]Enemy 4
0x3400x68 [BattleActor]Enemy 5
0x3A80x68 [BattleActor]Enemy 6

Like with [Character] above, we also have [BattleActor] here. These represent both allied and enemy characters, and hold basic information required for battle. The basics of which look like this:

OffsetDescription
0x0Status
0x4Row
0x9Level
0x28Current MP
0x2AMax MP
0x2CCurrent HP
0x30Max HP

Obviously, you'll notice this isn't complete, but again, it's enough to get us started. Using both the save and battle maps above lets us access the core stats of our party and enemies whatever state the game is in.

However, to ensure that we know exactly which one to use at any given time, we have one more location to look at: Offset 0x9A8AF8 holds a flag indicating whether or not we're currently in an active battle.

We'll look more in-depth in a number of these in a future post, but for now, we have enough to watch our party both in and out of battle.