Steal-item table Confirmed
What can you steal from each enemy, and how often does it work? With the Evil God Icon accessory equipped, the answer is a static two-byte-per-monster lookup baked into SCUS_942.54, the game's main executable on the disc: steal chance (percent) and stolen item id. The surprise is where it lives - in the executable, not in the monster archive alongside the rest of a monster's battle data. The monster record's reward block carries only gold / exp / drop, so every attempt to find the steal item by scanning monster records was structurally doomed. Second trap: the two bytes are stored chance-first, the reverse of the drop pair in the monster record.
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.)
| Field | Value |
|---|---|
| Base address | DAT_80077828 (file offset 0x68028 in SCUS_942.54) |
| Index form | DAT_80077828 + monster_id*2 (1-based monster id, same id space as the monster archive) |
| Stride | 0x2 bytes |
| Id range | monster ids 1.. (entry 0 is a reserved sentinel) |
| Offset | Type | Field |
|---|---|---|
+0 | u8 | steal_chance_pct - steal success chance, percent |
+1 | u8 | steal_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.