Subsystems
How Legaia's engine actually works under the hood. Each card is a self-contained page on one subsystem - what it does, where its code lives, and how it connects to the rest. Start with Boot path if you're new to the project.
The shape of the engine
Legaia is a fairly typical late-90s PSX RPG engine, but with a twist: most of the gameplay logic doesn't live in the main executable. SCUS_942.54 contains the boot path, low-level I/O, the asset dispatcher, the move-table VM, the motion VM, and the audio / renderer libraries - about half the runtime. The other half lives in RAM overlays that the disc loads on demand into one shared window. There are fewer of them than the subsystem list suggests: the slot-A family (field/town 0897, battle 0898, menu 0899, cutscene/STR 0970, and the minigames) all load at the same base and are mutually exclusive, while the world map, save, shop, level-up, and options are subsystems inside the field and menu overlays, not separate ones. Two of the runtime VMs (the field/event VM and the effect VM) live in these overlays.
0x801CE818): battle loads on top of town's slot, never coexisting. Several “subsystems” are not separate overlays - the world map is a mode of the field overlay (0897), and save / shop / level-up / options all live inside the menu overlay (0899). The randomizer injects a whole new vendor screen into 0899's dead space - see the overlay dead-region write-up for a zoom into that overlay.The list below groups subsystems by what layer of the engine they sit at.
Bootstrap and asset plumbing
The five runtime VMs
Legaia's runtime is driven by five independent VMs that all talk to one shared actor model. Four are clean bytecode dispatchers (actor / move-table / motion / field-event); the fifth (effect) is a per-slot state machine - different shape, same architectural role. They serve different layers of the engine and were almost certainly written by different members of the dev team.
FUN_8003774C. Drives NPC pathing + camera follow + "face the speaker" dialog poses. Fully ported.Per-domain runtime
FUN_801E76D4 handles the debug top-view toggle, top-view camera scroll/azimuth/zoom, and normal-walk per-frame update. Entity tick SM (FUN_801DA51C, 5 states) drives encounter and location-entry sequences.FUN_801DC6B4: 9-state machine, entry-context pointer table at 0x801E4F40, slot-select via actor VM, save-block existence scan at DAT_80084140.ShopSession tracks pending item, quantity, and buy/sell direction. Sell price = half buy price (min 1). Per-scene item tables pending overlay trace.InnSession { cost } gates the stay on affordability, then restores all active party members to full HP/MP. Per-scene costs pending overlay trace.DAT_80076AF4 + formula, and per-character stat growth = DAT_800769CC / DAT_80076918, both applied by the overlay level-up function FUN_801E9504. LevelUpBanner renders for 180 frames via level_up_draws_for(). The engine extracts the real XP curve from the user’s SCUS at boot; the stat-growth tables are parsed but not yet applied.StrInit / StrMode). MDEC decoder: VLC → IDCT → BT.601 YCbCr→RGBA. Clean-room port in crates/mdec.FUN_801E295C - the layer between “player picked Attack” and “HP has been deducted.” Two-level dispatch: action category (party byte) plus execution phase (ctx byte).FUN_800402F4; spirit damage is hard-coded; MP cost is ability-bit modified; engine-vm port lives in battle_formulas.rs.DAT_801C9360[char][0x0C]+0x74 escalates over base for an off-class weapon (favored 0x1E / off-class 0x2A / Astral 0x36) - not a flat double.The clean-room port
The Rust port lives in its own page - phase plan, crate layering, architectural principles, and the legal posture.