Table base + record layout

(DAT_80077828 is a Ghidra label - the disassembler's name for the fixed RAM address the table occupies. Static data: the address maps directly to a file offset in SCUS_942.54 on the disc.)

FieldValue
Base addressDAT_80077828 (file offset 0x68028 in SCUS_942.54)
Index formDAT_80077828 + monster_id*2 (1-based monster id, same id space as the monster archive)
Stride0x2 bytes
Id rangemonster ids 1.. (entry 0 is a reserved sentinel)
OffsetTypeField
+0u8steal_chance_pct - steal success chance, percent
+1u8steal_item_id - stolen item id in the item-name table's id space; 0 = none

Field order is chance-then-item - the reverse of the drop fields in the monster record (+0x48 item / +0x49 chance). Reading the table with the record's item-first order shifts every chance by one monster. A chance of 0 (or item 0) means the enemy can't be stolen from; a raw steal id resolves to a name the same way a drop id does (0x8A → Incense).

Provenance

Pinned from a live player-steal RAM capture (Evil God Icon equipped): the Skeleton (monster id 13) entry at 0x80077842 is 1e 8a = 30% Incense, exactly the steal the in-battle banner shows. Base, stride, and the [chance, item] order are confirmed byte-exact against the complete published steal table - item and chance columns - across every resolvable monster id, zero mismatches. The table is static rodata, resident in RAM identical to the file bytes.

Why the monster record has no steal field

An exhaustive offset scan of the decoded monster record (extraction entry 0867 / raw TOC index 0x365 - a numbered entry of PROT.DAT, the disc's single big archive - the battle_data monster archive), correlated against ground-truth steal data, finds no steal field there: the reward block at +0x44..+0x49 holds only gold / exp / drop. Every record-only search for the steal item was structurally doomed - the data was never in the archive.

Parser

legaia_asset::steal_table::StealTable::from_scus resolves the table from a SCUS_942.54 image (PSX-EXE t_addr → file-offset map, identical to the item-name table resolver). entry(monster_id) returns the [chance, item] pair; steal_item(monster_id) returns the item only when the entry is stealable. The disc-gated steal_table_real test pins the Skeleton→Incense anchor plus a span of ids against the real executable. CLI: asset steal-table <SCUS> [--all] [--json], with the item id joined to its name.

The randomizer (legaia_patcher::steal) edits this table on a user-supplied disc to reassign steal items.

See also