By myst6re.
The save format
PC steam version: Offsets start from at 384 (0x0180). File is compressed with LZS.
| Offset | Size | Data |
|---|---|---|
| 0x0000 | 2 bytes | Checksum |
| 0x0002 | 2 bytes | Always 0x08FF |
| 0x0004 | 2 bytes | Preview: Location ID |
| 0x0006 | 2 bytes | Preview: 1st character’s current HP |
| 0x0008 | 2 bytes | Preview: 1st character’s max HP |
| 0x000A | 2 bytes | Preview: save count |
| 0x000C | 4 bytes | Preview: Amount of Gil |
| 0x0010 | 4 bytes | Preview: Total number of seconds played |
| 0x0014 | 1 byte | Preview: 1st character’s level |
| 0x0015 | 1 byte | Preview: 1st character’s portrait |
| 0x0016 | 1 byte | Preview: 2nd character’s portrait |
| 0x0017 | 1 byte | Preview: 3rd character’s portrait |
| 0x0018 | 12 bytes | Preview: Squall’s name (0x00 terminated) |
| 0x0024 | 12 bytes | Preview: Rinoa’s name (0x00 terminated) |
| 0x0030 | 12 bytes | Preview: Angelo’s name (0x00 terminated) |
| 0x003C | 12 bytes | Preview: Boko’s name (0x00 terminated) |
| 0x0048 | 4 bytes | Preview: Current Disk (0 based) |
| 0x004C | 4 bytes | Preview: Current save (last saved game) |
| 0x0050 | 68 bytes | Guardian Forces: Quetzalcoatl |
| 0x00B4 | 68 bytes | Guardian Forces: Shiva |
| 0x00D8 | 68 bytes | Guardian Forces: Ifrit |
| 0x011C | 68 bytes | Guardian Forces: Siren |
| 0x0160 | 68 bytes | Guardian Forces: Brothers |
| 0x01A4 | 68 bytes | Guardian Forces: Diablos |
| 0x01E8 | 68 bytes | Guardian Forces: Carbuncle |
| 0x022C | 68 bytes | Guardian Forces: Leviathan |
| 0x0270 | 68 bytes | Guardian Forces: Pandemonia |
| 0x02B4 | 68 bytes | Guardian Forces: Cerberus |
| 0x02F8 | 68 bytes | Guardian Forces: Alexander |
| 0x033C | 68 bytes | Guardian Forces: Doomtrain |
| 0x0380 | 68 bytes | Guardian Forces: Bahamut |
| 0x03C4 | 68 bytes | Guardian Forces: Cactuar |
| 0x0408 | 68 bytes | Guardian Forces: Tonberry |
| 0x044C | 68 bytes | Guardian Forces: Eden |
| 0x0490 | 152 bytes | Characters: Squall |
| 0x0528 | 152 bytes | Characters: Zell |
| 0x05C0 | 152 bytes | Characters: Irvine |
| 0x0658 | 152 bytes | Characters: Quistis |
| 0x06F0 | 152 bytes | Characters: Rinoa |
| 0x0788 | 152 bytes | Characters: Selphie |
| 0x0820 | 152 bytes | Characters: Seifer |
| 0x08B8 | 152 bytes | Characters: Edea |
| 0x0950 | 400 bytes | Shops |
| 0x0AE0 | 20 bytes | Configuration |
| 0x0AF4 | 4 bytes | Party (0xFF terminated) |
| 0x0AF8 | 4 bytes | Known weapons |
| 0x0AFC | 12 bytes | Griever name (FF8 text format) |
| 0x0B08 | 1 bytes | Weapon ID Laguna |
| 0x0B09 | 1 bytes | Weapon ID Kiros |
| 0x0B0A | 1 bytes | Weapon ID Ward |
| 0x0B0B | 1 bytes | Unknown |
| 0x0B0C | 4 bytes | Amount of Gil |
| 0x0B10 | 4 bytes | Amount of Gil (Laguna) |
| 0x0B14 | 2 bytes | Limit Break Quistis |
| 0x0B16 | 2 bytes | Limit Break Zell |
| 0x0B18 | 1 byte | Limit Break Irvine |
| 0x0B19 | 1 byte | Limit Break Selphie |
| 0x0B1A | 1 byte | Limit Break Angelo completed |
| 0x0B1B | 1 byte | Limit Break Angelo known |
| 0x0B1C | 8 bytes | Limit Break Angelo points |
| 0x0B24 | 32 bytes | Items battle order |
| 0x0B44 | 396 bytes | Items 198 items (Item ID and Quantity) |
| 0x0CD0 | 4 bytes | Game time |
| 0x0CD4 | 4 bytes | Countdown |
| 0x0CD8 | 4 bytes | Unknown |
| 0x0CDC | 4 bytes | Battle: victory count |
| 0x0CE0 | 2 bytes | Battle: battles lost (defeat or time out) |
| 0x0CE2 | 2 bytes | Battle: battle escaped |
| 0x0CE4 | 4 bytes | Unknown |
| 0x0CE8 | 4 bytes | Battle: Tonberry killed count |
| 0x0CEC | 4 bytes | Battle: Tonberry Sr killed (yeah, this is a boolean) |
| 0x0CF0 | 4 bytes | Unknown |
| 0x0CF4 | 4 bytes | Battle: First “Bug” battle (R1 tip) |
| 0x0CF8 | 4 bytes | Battle: First “Bomb” battle (Elemental tip) |
| 0x0CFC | 4 bytes | Battle: First “T-Rex” battle (Mental tip) |
| 0x0D00 | 4 bytes | Battle: First “Irvine” battle (Irvine’s limit break tip) |
| 0x0D04 | 8 bytes | Battle: Magic drawn once |
| 0x0D0C | 20 bytes | Battle: Ennemy scanned once |
| 0x0D20 | 1 byte | Battle: Renzokuken auto |
| 0x0D21 | 1 byte | Battle: Renzokuken indicator |
| 0x0D22 | 1 byte | Battle: dream/Odin/Phoenix/Gilgamesh/Angelo disabled/Angel Wing enabled/???/??? |
| 0x0D23 | 16 bytes | Tutorial infos |
| 0x0D33 | 1 byte | SeeD test level |
| 0x0D34 | 4 bytes | Unknown |
| 0x0D38 | 4 bytes | Party (last byte always = 255) |
| 0x0D3C | 4 bytes | Unknown |
| 0x0D40 | 2 bytes | Module: 1 field, 2 world map. Saving writes no other value; loading reads 0 as 1 |
| 0x0D42 | 2 bytes | Current field |
| 0x0D44 | 2 bytes | Previous field |
| 0x0D46 | 3*2 bytes (signed) | Coord X (party1, party2, party3) |
| 0x0D4C | 3*2 bytes (signed) | Coord Y (party1, party2, party3) |
| 0x0D52 | 3*2 bytes | Triangle ID (party1, party2, party3) |
| 0x0D58 | 3*1 bytes | Direction (party1, party2, party3) |
| 0x0D5B | 1 byte | Padding |
| 0x0D5C | 4 bytes | Unknown |
| 0x0D60 | 256 + 1024 bytes | Field vars |
| 0x1260 | 128 bytes | Worldmap (TODO) |
| 0x12E0 | 128 bytes | Triple Triad (TODO) |
| 0x1360 | 64 bytes | Chocobo World (TODO) |
After the savemap
The decompressed image is 8192 bytes. The game copies 5028 bytes of savemap to offset 384, so bytes 5412 to 8191 are zero, outside the checksum and never read back. FFNx keeps its own blocks there, each with its own checksum, and ignores a block that doesn’t validate:
| Image offset | Size | Data |
|---|---|---|
| 0x1600 | 522 bytes | Items: FXIM block with all 255 slots, when kernel.bin adds items |
| 0x1810 | 524 bytes | Abilities: FXAB block with what each GF learned past id 127 |
Guardian Forces
The checksum calculation starts here.
There are 16 G-F: Quetzalcoatl, Shiva, Ifrit, Siren, Brothers, Diablos, Carbuncle, Leviathan, Pandemonia, Cerberus, Alexander, Doomtrain, Bahamut, Cactuar, Tonberry, Eden.
For each G-F:
| Offset | Size | Data |
|---|---|---|
| 0x00 | 12 bytes | Name (0x00 terminated) |
| 0x0C | 4 bytes | Experience |
| 0x10 | 1 byte | Unknown |
| 0x11 | 1 byte | Exists |
| 0x12 | 2 bytes | HP |
| 0x14 | 16 bytes | Complete abilities (1 bit = 1 ability completed, 9 bits unused) |
| 0x24 | 24 bytes | APs (1 byte = 1 ability of the GF, 2 bytes unused) |
| 0x3C | 2 bytes | Number of kills |
| 0x3E | 2 bytes | Number of KOs |
| 0x41 | 1 byte | Learning ability |
| 0x42 | 3 bytes | Forgotten abilities (1 bit = 1 ability of the GF forgotten, 2 bits unused) |
Characters
Squall, Zell, Irvine, Quistis, Rinoa, Selphie, Seifer, Edea. For each character:
| Offset | Size | Data |
|---|---|---|
| 0x00 | 2 bytes | Current HPs |
| 0x02 | 2 bytes | Max HPs |
| 0x04 | 4 bytes | Experience |
| 0x08 | 1 byte | Model ID |
| 0x09 | 1 byte | Weapon ID |
| 0x0A | 1 byte | STR |
| 0x0B | 1 byte | VIT |
| 0x0C | 1 byte | MAG |
| 0x0D | 1 byte | SPR |
| 0x0E | 1 byte | SPD |
| 0x0F | 1 byte | LCK |
| 0x10 | 2*32 bytes | Magics |
| 0x50 | 3 bytes | Commands |
| 0x53 | 1 byte | Padding or unused command |
| 0x54 | 4 bytes | Abilities |
| 0x58 | 2 bytes | Junctionned GFs |
| 0x5A | 1 byte | Unknown |
| 0x5B | 1 byte | Alternative model (Normal, SeeD, Soldier…) |
| 0x5C | 1 byte | Junction HP |
| 0x5D | 1 byte | Junction STR |
| 0x5E | 1 byte | Junction VIT |
| 0x5F | 1 byte | Junction MAG |
| 0x60 | 1 byte | Junction SPR |
| 0x61 | 1 byte | Junction SPD |
| 0x62 | 1 byte | Junction EVA |
| 0x63 | 1 byte | Junction HIT |
| 0x64 | 1 byte | Junction LCK |
| 0x65 | 1 byte | Junction elemental attack |
| 0x66 | 1 byte | Junction mental attack |
| 0x67 | 4 bytes | Junction elemental defense |
| 0x6B | 4 bytes | Junction mental defense |
| 0x6F | 1 byte | Unknown (padding?) |
| 0x70 | 16 * 2 bytes | Compatibility with GFs |
| 0x90 | 2 bytes | Number of kills |
| 0x92 | 2 bytes | Number of KOs |
| 0x94 | 1 byte | Exists |
| 0x95 | 1 byte | Unknown |
| 0x96 | 1 byte | Mental Status tr(“Mort”) « tr(“Poison”) « tr(“Fossile”) « tr(“Darkness”) « tr(“Aphasie”) « tr(“Trouble”) « tr(“Zombie”); |
| 0x97 | 1 byte | Unknown |
Worldmap
| Offset | Size | Data |
|---|---|---|
| 0x00 | 12 | party_pos Party position (see below) |
| 0x0C | 12 | party_pos2 Party position, second set. Selected by a flag that FFWorldInit clears and nothing sets, so never used |
| 0x18 | 12 | ragnarok_pos Ragnarok position |
| 0x24 | 12 | bgu_pos Balamb Garden position |
| 0x30 | 12 | car_pos Rental car position |
| 0x3C | 12 | car_pos2 Rental car position, second set: never used, as above |
| 0x48 | 24 | Unused: no code reads it |
| 0x60 | 2 | camera_angle Where the world map camera faces |
| 0x62 | 1 | car_rent World model of the car left on the map, at car_pos: 73-75 or 81-87 (vehicles 32-40). 0xFF: none |
| 0x63 | 1 | car_rent2 Rent status of the second set: never used |
| 0x64 | 5 | u1 Unknown |
| 0x69 | 1 | vehicle Vehicle in use when the world map was left |
| 0x6A | 2 | Set to 0xFFFF when the world map is left |
| 0x6C | 1 | camera_view Camera view mode |
| 0x6D | 1 | world_flag Bit 0 is what script condition FF21 tests |
| 0x6E | 1 | disp_map_config (0: none, 1: minisphere, 2: minimap) |
| 0x6F | 1 | u4 (Unknown) |
| 0x70 | 4 | Unused: no code reads it |
| 0x74 | 8 | script_bits 64 bits the wmset scripts test and set (FF27, FF28), see below |
| 0x7C | 2 | script_vars 2 bytes the wmset scripts test and set (FF2D, FF2E, FF30, FF31) |
| 0x7E | 2 | u6[2] (Unknown) |
A position is { s32 x, s32 y, s16 height, u16 angle }, a full turn of the angle being 4096 (World_SaveWorldmapStateToSavemap). On the map, x runs from -131072 to 131071 west to east and y from -98304 to 98303 north to south. Height is negative upward: the party stands at minus the ground height of wmx.obj, the game keeps cars and the Garden relative to the ground, and only the Ragnarok flies freely, up to -3584 (World_ComputeVehicleAltitude).
Bits 0-7 of script_bits hold messages shown once: 0 car controls, 1 unused, 2 Garden controls, 3 chocobo controls, 4 Ragnarok controls, 5 Selphie’s reminder of the Missile Base, 6 “This place looks…familiar” at Edea’s house, 7 the Garden gauge going berserk. Bits 8-15 hold the PuPu quest: 8 and 9 unused, 10 Mandy Beach, 11 Winhill, 12 Trabia, 13 Kashkabald Desert, 14 UFO defeated, 15 PuPu defeated. In the rest, bits 17-19, 25, 27-29, 35-39, 49, 51-54 and 56-59 are unused.
Script variable 0 is the Obel Lake quest’s progress, compared and set as a whole value: 0 lake not found, 1 lake found, 2 asked to find Mr. Monkey, 4 Mr. Monkey found, then 121 to 124 for the kind of Liar Rock met (black, white, red or blue-faced), drawn at random. Script variable 1 is the step of the conversation under way, and is cleared when a field is entered.
Obel quest flag
[0] => avoir fredonné twice|???|Unused|Unused|n ricochets|infini ricochets|Vu ryo|Vu ryo² (“100x + de ricochets que toi !”) [1] => Ryo a donné tablette|Unused|Indices ombre pour trouver l’idiot|Unused|Unused|Unused|Indice ombre pour Eldbeak|Eldbeak trouvé [2] => Trésor île Minde|Trésor Plaine de Mordor|Unused|Unused|Unused|Unused|Unused|Unused [3] => ???|Pierre Balamb|Pierre Ryo|Pierre Timber|Pierre Galbadia|Toutes les pierres|Indice Ombre pour Balamb|??? [4] => ??? (mordor var?) [5] => ???|???|???|???|Block access Lunatic Pandora|???|Block access Lunatic Pandora|??? [6] => avoir parlé à l’ombre|Accepter de chercher l’idiot|Avoir vu l’idiot|… [7] => ??? (temp var)
Triple Triad
| Offset | Size | Data |
|---|---|---|
| 0x00 | 77 | Normal cards (IDs 0-76), 1 byte each: bit 7 = owned at least once, bits 0-6 = copies owned (max 100) |
| 0x4D | 33 | Rare cards (IDs 77-109): current location of each card (240 = player, otherwise an NPC’s Deck ID) |
| 0x6E | 5 | Rare cards obtained at least once (1 bit per rare card, bit N = card 77+N) |
| 0x73 | 1 | Flags: bit 0 = collection complete (every card obtained once) |
| 0x74 | 2 | Triple Triad victory count |
| 0x76 | 2 | Triple Triad defeat count |
| 0x78 | 2 | Triple Triad draw count |
| 0x7A | 2 | Unknown |
| 0x7C | 4 | Unknown (set to 1 on a new game) |
On a new game everything is cleared and rare card N is placed at location N + 123 (200-232), the Deck ID of its starting NPC owner. See the Card List and the CARDGAME opcode for how locations work.
Chocobo World
| Offset | Size | Data |
|---|---|---|
| 0x00 | 1 byte (bit field) | Enabled / In world / MiniMog found / Demon King defeated / Koko kidnapped / Hurry! / Koko met / Event Wait off |
| 0x01 | 1 byte | Level |
| 0x02 | 1 byte | Current HP |
| 0x03 | 1 byte | Max HP |
| 0x04 | 2 bytes | Weapon (4 bit = 1 weapon) |
| 0x06 | 1 byte | Rank |
| 0x07 | 1 byte | Move |
| 0x08 | 4 bytes | Save count |
| 0x0C | 2 bytes | id related |
| 0x0E | 6 bytes | Unknown |
| 0x14 | 1 byte | Item Class A count |
| 0x15 | 1 byte | Item Class B count |
| 0x16 | 1 byte | Item Class C count |
| 0x17 | 1 byte | Item Class D count |
| 0x18 | 16 bytes | Unknown |
| 0x28 | 4 bytes | Associated save ID |
| 0x2C | 1 byte | Unknown |
| 0x2D | 1 byte | Boko Attack (star count: 0 = ChocoFire, 1 = ChocoFlare, 2 = ChocoMeteor, 3 = ChocoBocle) |
| 0x2E | 1 byte | Unknown |
| 0x2F | 1 byte | Home walking |
| 0x30 | 16 bytes | Unknown (unused?) |
Configuration
| Offset | Size | Data |
|---|---|---|
| 0x00 | 1 | vts_combat (VTS combat setting) |
| 0x01 | 1 | vts_msg_combat (VTS message combat setting) |
| 0x02 | 1 | vts_msg (VTS message setting) |
| 0x03 | 1 | analog_volume (Analog volume setting) |
| 0x04 | 1 | divers (Miscellaneous settings) joystick,vibration,manette,???,???,pointeur,son,ATB |
| 0x05 | 1 | scan (Scan setting) |
| 0x06 | 1 | camera (Camera setting) |
| 0x07 | 1 | MAP SEAL |
| 0x08 | 1 | L2 (L2 button mapping) |
| 0x09 | 1 | R2 (R2 button mapping) |
| 0x0A | 1 | L1 (L1 button mapping) |
| 0x0B | 1 | R1 (R1 button mapping) |
| 0x0C | 1 | TRIANGLE (Triangle button mapping) |
| 0x0D | 1 | ROND (Circle button mapping) |
| 0x0E | 1 | CROIX (Cross button mapping) |
| 0x0F | 1 | CARRE (Square button mapping) |
| 0x10 | 1 | SELECT (Select button mapping) |
| 0x11 | 1 | u4 (Unknown) |
| 0x12 | 1 | u5 (Unknown) |
| 0x13 | 1 | START (Start button mapping) |