Per-character save record Confirmed
The runtime per-character record is 0x414 bytes. Four records sit contiguously at 0x80084708 + slot * 0x414 (Vahn / Noa / Gala / guest). Field offsets are pinned by a fusion of decompiled function dumps, captured save-state observations, and the GameShark cheat database under data/cheats/.
Confidence
Confirmed for every offset in the table below. Each is cross-referenced against at least two of: a Ghidra-dumped function, a captured save-state observation, or a GameShark cheat with a labelled effect.
Per-character bases
| Slot | Base | Owner |
|---|---|---|
| 0 | 0x80084708 | Vahn |
| 1 | 0x80084B1C | Noa |
| 2 | 0x80084F30 | Gala |
| 3 | 0x80085344 | guest / future-party-member |
Layout
+0x000 u32 LE xp_low_word_alt ; "Max Exp" cheat target.
+0x004 u16 LE xp_cumulative_u16 ; captured XP cell - level-up logic
; reads here (Noa 4-jump 102 -> 336).
+0x0F4 u8[16] ability_bits ; OR'd into 0x80074358 each frame
+0x104 u16 LE hp_curr_live ; "Infinite HP" / "Max HP"
+0x106 u16 LE hp_max_live
+0x108 u16 LE mp_curr_live ; "Max MP"
+0x10A u16 LE mp_max_live
+0x10C u16 LE sp_curr_live
+0x10E u16 LE sp_max_live ; "100 AP"
+0x110 u16 LE agl_live ; "Max AGL" (live copy)
+0x112 u16 LE atk_live ; "Max ATK"
+0x114 u16 LE udf_live ; "Max UDF"
+0x116 u16 LE ldf_live ; "Max LDF"
+0x118 u16 LE spd_live ; "Max SPD"
+0x11A u16 LE int_live ; "Max INT" (legacy stat_cap target)
+0x11C u16 LE hp_max_record
+0x11E u16 LE mp_max_record
+0x120 u16 LE stat_cap_constant ; always 100 in captured saves
+0x122 u16 LE agl_record
+0x124 u16 LE atk_record
+0x126 u16 LE udf_record
+0x128 u16 LE ldf_record
+0x12A u16 LE spd_record
+0x12C u16 LE int_record
+0x130 u8 magic_rank ; "Level 99" cheat target. See note.
+0x13C u8 magic_slot_activator ; cheat sets to 0x24 to enable
+0x13D u8[12] magic_group_0 ; "Magic Modifier 1..12" hits
+0x149 u8[12] magic_group_1
+0x155 u8[12] magic_group_2
+0x161 u8[16] summon_levels ; "All Summons Level 9"
+0x185 u8 displayed_skill_count
+0x186 u8[16] displayed_skill_ids ; "Has all Arts" target
+0x196 u8 armor_id ; "Armor Modifier"
+0x197 u8 head_gear_id ; "Head Gear Modifier"
+0x198 u8 weapon_id ; "Weapon Modifier"
+0x199 u8 accessory_or_seru_lock ; "Activate Meta/Terra/Ozma at Lv9"
+0x19A u8 leg_gear_id ; "Leg Gear Modifier"
+0x19B..0x19D accessory_1..3_id ; "Accessory N Modifier"
+0x2B0..0x37F active_spell_slots[14] ; 14 x 0x14-byte runtime slots
Reconciled contradictions
+0x130 is Magic Rank, not character level
The captured Noa + Gala 4-level jumps show +0x130 ticking +1 per level-up event regardless of the levels gained. The Level 99 GameShark cheat sets the same byte to 0x63 (99). Both readings reconcile if +0x130 is the Magic Rank: setting it to 99 unlocks every magic / summon at maximum power without affecting the character's combat level (which is derived from cumulative XP via legaia_save::level_for_cumulative_xp).
+0x161..+0x178 is the summon-level array, not spell levels
The prior SpellList::levels field claimed +0x161..+0x184 was a parallel "spell level" array. The All Summons Level 9 cheat stamps 0x09 into 16 bytes at +0x161..+0x178 - one byte per summon ID, not per spell. The corrected accessor in legaia_save::CharacterRecord is summon_levels().
The +0x120 cap constant
Across every captured save, +0x120 reads as 100. The level-up overlay writes this constant on every level-up event regardless of character or stat. Interpreted as the hard ceiling the record-side stats clamp against; the runtime then re-projects through FUN_80042558's 0x3E7 clamp on the live copy, so equipment / buffs can push past the record-side cap up to 999 in battle.
See also
- Cheat databases - the GameShark / Mednafen cheat parser, full citation table, and runtime applier.
- RAM map - newly-pinned globals around the per-character records.
- Level-up subsystem - the multi-frame write split during a level-up event.