Inventory - one bag, one active window
Legaia keeps the whole party’s items in a single 256-slot bag. Every read and write of that bag is fenced by an active window that shrinks to one 128-slot half whenever a character travels alone. That one rule explains the “divided” inventory players notice during solo segments - and why the folklore “72-slot” limit is a display page, not a bound.
At a glance
- Where
- Main RAM
0x80085958; save block offset+0x1818(rides verbatim into every memory-card save) - Size
- 256 slots × 2 bytes:
[item id][count]; id0= free; stacks cap at 99 - Window
- Three halfwords
gp[+0x2D2]start,gp[+0x2D4]end,gp[+0x2D6]span - one writer - Accessors
- Five
SCUS_942.54helpers (add / consume / capacity / reserve / normalize); nothing touches the array raw - Engine
engine-coreinventory kernel (apply_battle_loot,buy_from_shop, field-VMGIVE_ITEM)- Confidence
- Confirmed - disassembly of every helper plus a live three-member window read
- Used by
- Field menu, Shop, Field script VM, Battle item use
What players see
Open the menu and the bag looks like a handful of tabbed pages. Play a segment where one character is alone and the bag seems cut in half: items the party had are gone, and what the loner picks up never shows up once everyone regroups. Both impressions come from one array and one window. The pages are views; the halves are access rules; nothing is ever physically moved.
start and end. A lone character gets one half; a party gets the whole array.Storage and access
Nothing indexes the array directly. The pause menu, chests and scripts (the field VM’s GIVE_ITEM op), battle rewards, shops and the equip swap-back all go through the same small helper family, passing item ids or helper-returned slot numbers. There is no raw-index sort or swap primitive anywhere in retail.
| Helper | What it does |
|---|---|
| add | find a matching id, else the first free slot in the window |
| consume | decrement by id; zeroes an emptied id in place, never compacts |
| capacity / reserve | paired before adds in the equip swap-back path |
| normalize | re-installs the window, merges duplicate stacks (cap 99), pulls occupied slots down into holes |
| window setup | the sole writer of the three window halfwords |
A new game seeds exactly one slot - five Healing Leaves - after pre-zeroing the whole range. Occupancy keys on the id alone, so a live id with a zero count survives compaction.
The active window
The helpers never see “256 slots”; they see [start, end). Window setup picks the bounds from the party roster - member count and the first roster id - every time normalize runs.
| Party members | Story flag 20 | First roster member | Window installed |
|---|---|---|---|
| 0 | - | - | none - the previous window stays |
| 1 | set | - | [0, 256) |
| 1 | clear | Vahn | [0, 128) |
| 1 | clear | anyone else | [128, 256) |
| 2 or more | not tested | not tested | [0, 256) |
- Consequence. A solo character has an isolated pocket of the bag: they can neither see nor spend the party’s items, and what they pick up alone never reaches the party inventory.
- Design intent. Vahn owns the lower half because the game opens with Vahn alone - the early-game bag simply is
[0, 128)until the party forms. Once two members are present the full 256 opens. - Bounds.
endis only ever 128 or 256. Slots outside the window exist in RAM but are invisible to add, consume, capacity and normalize alike.
Three “divisions” that are not the window
| Division | Where it lives | What it is |
|---|---|---|
| The window halves | the three gp halfwords | Runtime bounds over one shared array, keyed on party composition. Storage is never partitioned - only access is. |
| Per-character gear | each character record, equip bytes +0x196..+0x19D | Worn weapon, armor and Goods are not in the bag. Equipping refunds the displaced item through the add helper. |
| Menu pages | consumables 0x77..0x8E, equipment below, books and key items above | The pause menu’s tabs filter the one array by item-id band. The “72-slot” figure is the general-items page size. |
The add helper’s off-by-one
The add helper stores the item id before it bound-checks. On a completely full window the scan exits at i == end and the id byte lands one slot past the window; only the count store is guarded. This is the core of the open arbitrary-code-execution reachability thread - see How we know for the exact addresses.
Details: reaching the leak, and the probe trap
The stray id lands at 0x80085958 + end·2: 0x80085A58 when end = 128, 0x80085B58 when end = 256. Demonstrating it needs a bag genuinely filled to end (256 slots with a multi-member party) with the hit shown to land past the guard. An execution probe at pc = 0x800422BC fires on every successful add, before the guard, so a hit there is not out-of-bounds evidence by itself. Status: open RE threads.
History: the 72-slot ACE ceiling
The earlier ceiling “0x800859E8 = first key-item slot” assumed the 72-slot display page was the window. The accessors bound on end, which is only ever 128 or 256, so every out-of-bounds figure derived from a 72-slot bag is void. See do-not-re-walk.
How we know
| Function | Address | What it proves |
|---|---|---|
| window setup | FUN_8004313C | sole SCUS writer of gp[+0x2D2 / +0x2D4 / +0x2D6]; 11 callers; branches on party count (0x80084594), story flag 20 (FUN_8003CE64(0x14)), first roster byte (0x80084598) |
| add | FUN_800421D4 | id store precedes the bound check; count store guarded |
| consume | FUN_80042310 | zeroes the emptied id in place |
| capacity / reserve | FUN_80042EE0 / FUN_80043048 | the pair the equip swap-back calls before adding |
| normalize | FUN_800423E0 | calls window setup first; occupancy = id ≠ 0 |
| new-game seed | FUN_80034A6C | writes slot 0 = (0x77 Healing Leaf, ×5) |
| battle rewards | FUN_8004E568 | loot grants go through the add helper |
| menu overlay sweep | PROT 0899, all 129 functions | zero direct array writes; 17 inventory operations, all via the helpers |
| live read | three-member mid-game state | window (0, 256, 256), 160 contiguous occupied slots - more than any 72-slot model allows |
Cheat-device names (Have 99 Items, Item Modifier) are third-party anchors on the same region; the Have 99 Items range covers 72 slots because that is the page it targeted. Per-cell detail: memory map.