Item-effect descriptor table Confirmed
What does a Healing Leaf actually do, and why can't you use a Door of Light in
battle? This table answers both: it is a static table in SCUS_942.54 (the game's main executable
on the disc) saying what kind of effect each consumable has - heal HP, restore MP, cure
status, revive, raise a stat, escape a dungeon - plus its target shape (single ally vs. whole party) and where
it can be used (field menu vs. battle). Notably it does not hold the literal restore amounts
- those live in two separate small tables (below) - and "key items are unusable"
is not a special case at all: a key item simply resolves to a descriptor with neither usability bit set, which
is how the menu greys it out. It is the sibling of the item-name table
(DAT_80074368) and the spell table: the three are contiguous
static data, and this table ends exactly where the spell table begins.
Table base + record layout
(DAT_800752C0 is a Ghidra label for the fixed RAM address the table occupies once the executable is loaded; static data, so the address maps directly to a file offset in SCUS_942.54 on the disc. Each record is 4 bytes - the "stride".)
| Field | Value |
|---|---|
| Record base | DAT_800752C0 (file offset 0x65AC0 in SCUS_942.54) |
| Stride | 0x4 bytes |
| Record count | 130 (subtypes 0x00..=0x81); ends at the spell table 0x800754C8 |
| Offset | Type | Field |
|---|---|---|
+0 | u8 | effect class (action-validator arm) |
+1 | u8 | tier / sub-case (per-class selector; e.g. heal-HP 0/1/2 = 200/800/max) |
+2 | u8 | flags: 0x80 populated, 0x20 whole-party target, 0x04 battle-usable, 0x02 field-usable |
+3 | u8 | passive-effect index (0x00..=0x3F) for accessory / quest-item rows; 0x41 = no-passive sentinel on consumable rows (see the accessory-passive table; ids resolve through the item-name table) |
Healers carry 0x04 | 0x02 (field + battle); permanent stat-ups and field-utility items carry 0x02 only; status-cures and revive carry 0x04 only. Key items resolve to a descriptor with neither usability bit set, which is how the item menu greys them out. The class byte is meaningful only together with the usability flags - many key items funnel to class 0 with no usability bit, so "class 0" alone is not "an HP potion".
Indexing (double-indirected)
The lookup goes item id → subtype → descriptor: an item's record in the name table carries a subtype byte, and the subtype picks the descriptor here. From FUN_8003043C (FUN_80xxxxxx names are Ghidra-traced functions, labelled by their RAM address):
subtype = item_name_table[id].byte(+1) ; DAT_80074369[id*0xC]
descriptor = (&DAT_800752C0)[subtype * 4] ; stride-4 record
arm = descriptor[+0] ; effect class / validator arm
tier = descriptor[+1]
flags = descriptor[+2]
The & 0x20 all-party test is read in FUN_8003043C itself; the 0x02/0x04 field-vs-battle bits are read by the field item-menu list builder FUN_80030628; the battle item path reads class + tier straight into the actor's action context (battle overlay 0x801E3BA4).
Class byte (+0)
Class labels are validated against the on-disc item description strings (item record +8 pointer):
| Class | Effect | Example item |
|---|---|---|
0 / 1 | heal HP, one ally / whole party | Healing Leaf / Healing Bloom |
2 | restore MP | Magic Leaf |
3 / 8 | cure all status / cure single status | Medicine / Antidote |
4 | revive | Phoenix |
5 | extend action gauge (one battle) | Fury Boost |
6 | permanent stat-up (tier = which stat) | Miracle Water, the Water line |
7 | temporary stat buff (one battle, ×6/5) | Power Elixir |
11/12/13 | arts book (Fire/Wind/Thunder; tier = book level) | Fire Book I |
126/127 | summon flute | Lippian Flute |
128 / 129 | field escape (dungeon) / field warp (city) | Door of Light / Door of Wind |
130 | reduce encounter rate | Incense |
Heal-amount table (0x8007655C)
This table holds class + tier + flags, not the literal restore amounts - those live in a separate, also-static pair of u16[4] sub-tables the apply handler FUN_800402F4 reads, indexed by the descriptor tier (base + tier*2):
| VA | Sub-table | Tier 0 | Tier 1 | Tier 2 |
|---|---|---|---|---|
0x8007655C (file 0x66D5C) | HP restore cap (classes 0/1) | 200 | 800 | 9999 |
0x80076564 | MP restore cap (class 2) | 50 | 200 | 20 |
Each restore is deficit-clamped (applied = min(max - current, table[tier])), so tier 2's 9999 is an effective full restore. Tier 3+ HP heals are character-relative (scaled off the per-character Seru-heal tables), and revive (class 4) is max_hp×0.4 + rand()%(max_hp/8) at tier 0, full at higher tiers. The class 5/6/7 stat magnitudes are inline immediates in the handler's switch, pinned per (class, tier): the Water line adds a flat +16 HP / +8 MP / +4 stat to the character record, the Elixirs multiply battle-actor stats by 6/5 for one battle.
Parser + provenance
legaia_asset::item_effect::ItemEffectTable::from_scus resolves both tables from a SCUS_942.54 image (PSX-EXE t_addr → file-offset map, the same resolver as the item-name table). heal_amounts() exposes the two u16 arrays, restore_amount(id) resolves an item id through its (class, tier) to Hp/Mp/CharRelative, and stat_effect(id) decodes the class 5/6/7 items. Apply handler FUN_800402F4 is reached through a 132-entry jump table at 0x80014FA0 indexed by the class byte; it handles both the field item menu and the battle item path (branching on game_mode == 0x15). The disc-gated item_effect_real test pins the descriptor bytes and cross-checks each against its on-disc description; the engine consumes the flags via ItemCatalog::apply_effect_flags and the amounts via the shared stat-raise / buff / gauge paths.
Superseded readings
An earlier reading held that the restore amounts were "a switch of immediates inside an overlay-resident apply handler, not in the dumped corpus". The HP / MP amounts are in fact on the disc in the static tier-indexed tables at 0x8007655C / 0x80076564, read by the static FUN_800402F4; only the class 5/6/7 stat magnitudes are inline immediates.