Key functions
While Legaia runs, a few thousand small routines do the work: load a scene off the disc, step the NPC scripts, draw the party, resolve a hit. This is the who's-who of the ones the reverse-engineering has mapped, grouped by subsystem with a one-line job each. It is the orientation; the full per-address directory lives in the repo's per-subsystem pages, which you grep for an address.
At a glance
- Naming
FUN_8003E4E8= the function whose first instruction sits at RAM0x8003E4E8;DAT_/PTR_mark data and pointer tables the same way. "JT" is a jump table.- Resident code
0x80010000..0x8007B800- the executableSCUS_942.54, always loaded- Overlay code
0x801C0000+- one game-mode overlay paged in at a time (field, battle, menu, title, cutscene)- Dumps
ghidra/scripts/funcs/<addr>.txt, overlays asoverlay_<label>_<addr>.txt(gitignored: Sony-derived; regenerable via Ghidra tooling)- Full directory
- Eleven Markdown pages under
docs/reference/functions/- list below - Status per function
- Port catalog (dumped / documented / ported)
How to read an entry
The game shipped with no symbol names, so the address is the name and it is stable across every doc and probe in this project. Four things catch people out:
| Trap | What to do |
|---|---|
Overlay addresses collide. Several overlays share 0x801C0000+. | If an entry does not match the code you are reading, check which overlay you have first - overlay capture, overlay window. |
| "No static caller in SCUS" does not mean dead. Most game logic lives in overlays. | Zero callers in the executable means "needs an overlay sweep", not "unused". |
| Some printed addresses are phantoms. A dump can show a real body beside a VA no runtime image ever used. | Before treating an unfamiliar 0x801E… / 0x8020… as new, check overlay VA aliases. |
Decompiled C is a rendering. Ghidra promotes intra-function labels to fake FUN_ calls, drops register arguments, reorders stores. | Read the disassembly; the artifact catalogue is on the Ghidra page. |
Not finding an address here does not mean it is unknown - this page covers the functions that anchor an explanation somewhere on the site.
Highlights by subsystem
Boot + asset loader
How the disc's single big archive (PROT.DAT) gets found, indexed and streamed into RAM.
| Function | Role |
|---|---|
| FUN_8003E4E8 | Boot-time TOC loader: first 3 sectors of PROT.DAT into 0x801C70F0 |
| FUN_8003E8A8 | LBA resolver against the in-RAM TOC |
| FUN_8003E6BC | Path-based file opener (data\battle\efect.dat etc.) |
| FUN_8003EBE4 / FUN_8003EC70 | Overlay loaders A / B; two overlays resident at once. B pages in the per-summon Seru-magic overlays |
| FUN_8001F05C | Asset-type dispatcher (type byte → handler) |
| FUN_80020224 | Descriptor-pair walker |
| FUN_8002541C | Streaming-asset driver |
| FUN_80021934 | Scene-transition streaming actor: streams the destination scene bundle, then hands off to MAIN INIT |
Deep dive: overlay loader indexing and the scene-transition actor
- Loader argument
param + 0x381is a raw TOC index = extraction entryparam + 0x37F(the resolver indexes the raw in-RAM PROT head, two above extraction numbering). A player Seru-magic cast (spell id0x81..0x8B) callsFUN_8003EC70(id - 0x79)→ extraction PROT 903..913; the summon's models live in those overlays plus the extraction-0871etmd.datlibrary, not inbefect_data. FUN_80021934is a 5-state machine overactor+0x1A(jump table0x80010760); its real entry is 3 instructions before the0x80021940prologue. Spawned byFUN_8001FD44viaFUN_80020DE0from the descriptor at0x80070734(the phase-misaligned family0x800705FC..0x80070763, not a mode-table row). StreamsDATA_FIELD\<scene>.LZS(rawscene_base+3) into_DAT_8007B85C- by PROT index in dev (state 2), by filename in retail (state 4) - then sets_DAT_8007B83C = 2. Dumpfuncs/80021940.txt.
Game VMs
The bytecode interpreters that run scenes, menus, moves and actor motion - most of the game's logic is script, not native code.
| Function | Role |
|---|---|
| FUN_801DE840 | Field / event script VM (43 ops, switch at 0x801E00F4) |
| FUN_801E0C3C | Field-VM op 0x4C second-stage dispatcher (16-entry JT 0x801CEE60) |
| FUN_801E3040 | Op 0x4C nibble-0xE sub-dispatcher (15-entry JT 0x801CF008) |
| FUN_801E30E4 | Op 0x4C 0xE2 FMV trigger: FMV index → _DAT_8007BA78, mode 0x1A → _DAT_8007B83C |
| FUN_8003CE9C | Field-VM context resolver (sign-extended u16 read) |
| FUN_80023070 | Move-table VM (71 ops, JT 0x80010778) |
| FUN_801D362C | Move-VM extension (61 sub-ops via op 0x2F); one copy, in the field overlay - the world-map, dialog and cutscene modes run on that overlay, not on copies of their own. Its one outward call is the scanline strip emitter FUN_801D31B0 |
| FUN_801D6628 | Window-widget ("actor") VM of the menu overlay (13 ops, JT 0x801CED70) |
| FUN_80038158 | Per-actor motion / bytecode VM; ops 7 / 8 set / clear story flags in DAT_80085758 |
| FUN_8002519C | Per-frame actor-list tick iterator (5× per frame from FUN_80016444) |
| FUN_80021DF4 | Per-frame actor tick |
| FUN_800204F8 | Move-buffer setup (Tactical Arts) |
| FUN_8003A1E4 | Field-NPC placement installer; its tail pre-runs the script prologue, which is where a never-walked NPC's initial facing comes from |
Deep dive: motion-VM flag writes and NPC initial facing
FUN_80038158is dispatched byFUN_8003BC08when actor+0x10 & 0x80. Its bytecode is MAN tail-section 1 (installerFUN_8003A9D4, parserlegaia_asset::man_motion). The chapter-spine gates0x142/0x482/0x1BEand the town01 one-shot549appear in no motion stream: the first three are field-VM script bytes,549is a direct code path.FUN_8003A1E4reads[model, anim, tile_x, tile_z]- no facing byte. Its tail (0x8003A474..0x8003A4F8) executes a script's leading ops throughFUN_801DE840when the script opens with op0x24/0x25, until a0x21NOP or a sub-0x20byte; ops0x4C 0x51sub-1 and0x38writeactor+0x26from the 8-direction heading LUT at0x80073F04. Portman_field_scripts::placement_initial_facing+World::seed_field_npc_facings; see field locomotion.
Cutscene, FMV and camera
The movie player, the CD-XA voice streamer, and the field camera the story scenes drive.
| Function | Role |
|---|---|
| FUN_801CEA3C | STR / MDEC master dispatch (cutscene overlay 0970, modes 26 / 27): decoder select, letterbox, play loop, return-scene hand-off |
| FUN_801CF098 | FMV main play loop; pad-abort honoured only for fmv_id 0 |
| FUN_8005F024 | STR frame-demux state machine (St library; ring API FUN_8005BBF8 / FUN_8005EF40 / FUN_8005EE4C) |
| FUN_8003D764 | XA-clip drive sequencer / channel selector, armed off the 34-slot XA1..XA34 table at 0x801C6ED8 |
| FUN_8003BDE0 | Partition-record → VM-context dispatcher: named record + 3 story-flag gates, then script entry |
| FUN_80037174 | Narration crawl roller (the [CC F8 80 N] op), spawned as a child context so the timeline keeps running |
| FUN_8003C764 | On-screen text-balloon spawner (op 4C E1); centred, Y=180, 120-frame pages - not the opening crawl |
| FUN_8003C83C | Field-VM cross-context resolver (0xF8 player / camera anchor, 0xFB system) |
| FUN_801DB8EC | Camera focus + projection setter (ops 0x4C 0x39 / 0x3E) - focus and FOV, not eye position |
| FUN_801DBC20 | Camera-zone config loader: splits the 18-byte camera-region record into the camera globals |
| 0x801D0A6C (data) | FMV dispatch table, 23 × 32 B (legaia_asset::fmv_dispatch) |
| 0x801CAE08 (data) | libcd CdlFILE directory cache |
Deep dive: gate math and the camera cluster
FUN_8003BDE0(x, z, record, gate): gate C1 blocks if any flag set inDAT_80085758, C2 requires all; script entry at1 + name_len*2 + (1+C0) + (1+C1*2) + (1+C2*2). Callers: field-VM op0x44(ra0x801DF098) and the walk-on triggerFUN_801D1EC4→FUN_801D5630(ra0x801D218C).FUN_8003D764sequence:CdlSeekL→ Setmode0xC8→CdlSetloc→CdlSetfilter {file 1, chan}→CdlReadS→CdlGetlocPpoll; armed byFUN_8003D53C/FUN_8003EAE4.FUN_801DB8ECcallsFUN_801DAB90(transform, 0x801F3580), sets GTEHfrom_DAT_8007B6F4, and writes focus globals_DAT_80089118/_DAT_80089120: tile-derived when camera-mode nibbleDAT_8007B607>>4 == 5, else anchor-follow. Eye-back depth lives in the op-0x45translation trio's slot 5 (_DAT_800840C0).FUN_801DBC20keys on mode nibblebyte[5] >> 4(3 look-at-anchor, 5 fixed-shot, 6 no-op, else position-sweep) into globals0x8007B607..0x8007B627; consumersFUN_801DAB90+FUN_801DB510, defaults inFUN_801DBE9C. Byte split: camera-region table.FUN_801CEA3C's return hand-off: label table0x801CE8AC→ scene-name global0x80084548+ spawn word0x80084540.
Renderer + GTE
From an actor's mesh chain to GPU primitives: the per-actor render dispatcher, the TMD emit leaves and the animated-mesh poser.
| Function | Role |
|---|---|
| FUN_8001ADA4 | Per-actor render dispatcher (switch on actor[+0x56]); case 5 walks the mesh chain, case 0xB is the ocean CLUT-walk emitter |
| FUN_8001D140 | Stack-swap wrapper around the dispatcher; 6× per frame from FUN_80016444 (render pass) |
| FUN_80043390 | Per-prim emit selector: SCUS table 0x8007657C or the world-map overlay's fog-enabled table 0x801F8968 |
| FUN_8002735C | The 60-GTE-op table-driven TMD renderer; gated on actor +0x42, and unentered in every sampled mode |
| FUN_8001B964 | Animated-mesh renderer: poses TMD object i with ANM bone i (R·v + T); skips unless object count = bone count |
| FUN_8001BE80 | ANM per-(bone, frame) entry decoder (port player_anm::BoneTransform) |
| FUN_80024D78 | Actor object-table fill from the pool TMD |
| FUN_80024E80 | Screen-fade primitive spawn (port engine-core::fade::spawn_fade) |
| FUN_80026B4C / FUN_800268DC | TMD validate + register into DAT_8007C018; group-descriptor fixup |
| FUN_8002C69C | HUD / menu / dialog sprite-batch POLY_FT4 emitter |
| FUN_801F69D8 | World-map top-view tile-visibility dispatcher (overlay) |
| FUN_801D8280 | DAT_8007C018 table walker (overlay) |
| 0x8007C018 (data) | Global TMD pointer table; counters 0x8007B774 / 0x8007BB38 (world-map overlay) |
| DAT_8007326C (data) | Per-mode primitive descriptor table |
| 0x800EB654 (data) | Battle context struct (via _DAT_8007BD24) |
Deep dive: dispatch details
- Case 5 of
FUN_8001ADA4dispatches each TMD toFUN_8002735Cwhen actor+0x42is non-zero,FUN_80029888(env-mapped) when only+0x7Ais, elseFUN_80043390(textured) - the measured leaf. Case0xBbanksacc += DAT_1F800393and fires a 16×1MoveImageper kingdom slot-5 table entry whenacc >= hold. FUN_80043390picks its table on_DAT_1F800394 & 1; the overlay variant routes every prim through the fog leaves at0x801F7644..0x801F8690- the bulk continent emit path.FUN_80024E80(fade_template, mode)allocates from pool&DAT_80070674viaFUN_80020DE0, storesmodeatactor[+0x18], loads the ramp viaFUN_80020B00. The battle-action SM uses it for the summon backdrop fade (state0x33) and the escape white-out (state0x66).FUN_80026B4Cstores atDAT_8007C018[DAT_8007B774++]; reached fromFUN_8001F05Ccases 2 and 9.FUN_800268DCbuilds the+0xCdescriptor array (0x1C stride).FUN_801F69D8lives inoverlay_world_map_top_ext.bin; in warp-transition captures it is the live caller (ra0x801F725C).FUN_801D8280iterates0..DAT_8007BB38callingFUN_801D5E20per sub-record.
Battle
The battle overlay's action state machine, HUD, loaders and the resident stat kernels.
| Function | Role |
|---|---|
| FUN_801E295C | Battle action state machine (16 KB) |
| FUN_801D0748 | Battle / level-up main tick; sub-states 0x1E / 0x32 / 0x6E / 0xFE |
| FUN_801D388C | Battle animation dispatcher (switch on animation type) |
| FUN_801D5854 | Per-pose camera / presentation driver (the anim system is FUN_80047430 → FUN_8004AD80 → FUN_8004998C) |
| FUN_801D8DE8 | HUD element renderer, 80-entry element table; shared with Muscle Dome |
| FUN_801DA6B4 | Actor display-state controller (opacity, pose flags) |
| FUN_801DB81C | Next-valid-target scan (actor[+0x14C] != 0, no death / stone) |
| FUN_801ED710 | Records / stats screen renderer |
| FUN_800520F0 | Battle scene loader: the befect_data cluster + effect-model library into DAT_8007C018[3..32] |
| FUN_80020050 | Flame / effect-texture atlas loader (PROT 870 into VRAM) - separate from the bundle loader |
| FUN_8004E2F0 | Range / line-of-sight |
| FUN_80054CB0 | Monster init (applies the battle-load stat boost) |
| FUN_80042558 | Per-frame stat aggregator |
| FUN_8003FB10 / FUN_800431D0 | Action validator / ability-bitmask read |
| FUN_8002CDD0 / FUN_8002C2E4 / FUN_8002C488 | Status-HUD panel / per-member row / icon drawer; FUN_8002C2E4 pins the status bitfield at record +0x12E |
| 0x801C9370 (data) | 8-actor pointer table (party 0-2, monsters 3-7); HP at +0x14C |
| PROT 0967 | Sparring-tutorial overlay, co-resident at 0x801F69D8 during the Tetsu fight |
Deep dive: loaders and the records screen
FUN_80020050uploads PROT entry0x366(extraction 870) twice viaFUN_8001FC00→FUN_8003E8A8; VRAM region set up byFUN_80017888/FUN_8001E54C(arg0xF000); gated on_DAT_8007B868 == 0.FUN_800520F0pulls0x367..0x36Dinstead.FUN_801ED710draws nine rows: battles + escapes (cap 99999), play time, six per-character categories over the record at0x80084140 + n*0x414(+0x6B4 / +0x6B0,+0x660 / +0x664,+0x74D / +0x704), and averages over the0x801C6460counters.
Menu + title overlay
The pause menu's window table and panel renderers, and the title screen's attract timer.
| Function | Role |
|---|---|
| FUN_801DD35C | Title-overlay per-frame tick; attract countdown underflow starts MV1.STR |
| FUN_801D33D8 | Per-character status / party panel renderer (Status / Magic / Moves / Skills), window descriptor 28 |
| 0x801E4738 (data) | Window descriptor table: 52 × 16 B [content_id][park_edge][class][x,y,w,h][renderer] (parser legaia_asset::menu_windows) |
| FUN_801E1C1C | Shared menu-element renderer |
| FUN_801CF650 | Equipment stat aggregator: 5 slots at +0x196 → DAT_801EF08C..09C |
| FUN_801DD0C0 | Item category / slot validity check |
Deep dive: attract timer and window renderers by id
FUN_801DD35Cdecrements the countdown at0x801DDCCCby_DAT_1F800393each frame; on underflow writes_DAT_8007B83C = 0x1Aand zeroes_DAT_8007BA78- the same fall-through as the field-VM FMV trigger. Dumpfuncs/overlay_title_801ddccc.txt.- Window renderers by id: 50
FUN_801CFD68(command list), 49FUN_801D0148(money / play time), 51FUN_801D030C(party panel), 26 / 21FUN_801D2094(party list), 27FUN_801D30A4(Condition pager), 30FUN_801D31EC(summary), 22FUN_801D21C0(equip), 48FUN_801DCEF0(options), 0..4 title tabs. Live windows are 0x5C-stride linked structs with the animated rect at+0xA. FUN_801D33D8draws into a caller-supplied rect (WX = *(i16*)(a0+0xA),WY = *(i16*)(a0+0xC)), content only; submenu idDAT_801E46C0 & 0xFFFfolded to 0..5. Pixel layout: field menu.
Audio (PsyQ)
Sony's libsnd / libspu stack as linked into the executable, plus the game's own bank loaders.
| Function | Role |
|---|---|
| FUN_8001FA88 | Sound init / .dpk loader |
| FUN_8001FC00 | Streaming-asset loader (sound\) |
| FUN_800243F0 | BGM lookup (PROT-relative) |
| FUN_8003E104 | monster.snd loader |
| 0x80061000..0x80067FFF | libsnd SsAPI sequencer cluster |
| FUN_80068D94 | SsVabOpenHead core: validates pBAV, builds the program → tone-page rank map, allocates SPU memory (port engine-audio::VabBank::upload) |
| 0x80068000..0x8006DFFF | libspu SPU control cluster |
| FUN_80069B18 | SPU DMA transfer state machine |
| FUN_8001EBEC | Equipment-conditional per-character TMD group patch (objects 10 / 11 swap) |
World map + field debug tools
The overworld controller, its developer menu, and the field overlay's debug editors the menu reaches into.
| Function | Role |
|---|---|
| FUN_801E76D4 | World-map controller: top-view debug toggle, camera scroll / azimuth / zoom |
| FUN_801EAD98 | Developer menu renderer, 24-entry list (display only; port engine_vm::world_map_overlay) |
| FUN_801EE094 / FUN_801EE328 (0897) | The travel arts Riremito / Rula (subsystem handlers 0x29 / 0x2B): what the Door of Light / Door of Wind items run through the pause-menu session FUN_801ED308; each scans the resident CDNAME define table 0x80088758 for its destination. The developer menu reaches them too |
| FUN_801DBD04 (0897) | Dev-menu EVENT FLAG editor over ids 0..0xFFF; can set any story flag including the spine gates |
| FUN_801EF014 (0897) | Flag-window picker widget (op-0x49-installed); only genuine disc use is the Uru Mais warp-pad memory |
| FUN_801F159C (0897) | Subsystem actor handler: jalr by value through the 52-entry id table 0x801F33B4 - 7 the state pick FUN_801F1F4C, 0x30 the pause-menu session, 0x29 / 0x2B the travel arts. Reached by value, so no address reference names a slot |
| FUN_801ED308 (0897) | Pause-menu session (handler 0x30): brightness ramp, menu spawn, parks on the exit code _DAT_8007B43C; its phases 6 / 7 hand a Door of Light / Door of Wind use to Riremito / Rula. Not a fade/flash |
| FUN_801D2EBC (0897) | Timed-flag scheduler consumer (installer op 0x4C 0xD3); retail use: chitei2 escape timer, flag 0x4C7 |
| FUN_801DA51C | Per-entity tick; 5-state SM on entity[+0x8A] |
| FUN_801CFC40 | Top-view sprite batcher |
| FUN_80016444 | Per-frame render tick (SCUS); jal 0x801D7EA0 gated by _DAT_8007BC3C == 2 |
| FUN_801D7EA0 / FUN_801D8258 | POLY_FT4 batch emitter, one-shot gated by _DAT_801F351C; its gate setter |
| FUN_801D1344 | Gate-arm wrapper (field overlay 0897's player master frame handler); 0x801C2B2C is the same body printed 0xE818 low, not a relocated copy |
Deep dive: the field-overlay debug editors
- Warp appliers walk the map table at
_DAT_80084628, load the scene viaFUN_8001FD44, then teleport the player. - The EVENT FLAG editor keeps its cursor at
DAT_801F2AA0; SET / CLEAR appliers at0x801EA4F8 / 52C. It is also the register-pointer editor that produced the FMV trigger corpus. FUN_801EF014: descriptor via_DAT_8007B450,+2first visible row,+3visible count; confirming a different row setsbase + selection, re-picking the current row or cancelling sets nothing. Disc census (man-scripts --op49-window-census, 209 sites): the only flag-window family is kor / kor3 / kor4 sub-4base=0x138 count=8; no window contains a spine flag.
BIOS + libapi
PlayStation kernel entry points and C-library routines the executable links in; ignored by the port catalog as PsyQ infrastructure.
| Address | Routine |
|---|---|
| 0x80056678 / 0x80056688 | EnterCriticalSection / ExitCriticalSection |
| 0x80056658 / 0x8006B844 | TestEvent / WaitEvent |
| 0x80057024 / 0x8005ACAC / 0x8005E540 | memmove / memset / memcpy |
| 0x80056698+, stride 0x10 | BIOS B-vector thunk cluster |
Full directory
The complete lookup table is one Markdown page per subsystem in the repo. Grep them for your address, read the row, then open the dump or the linked subsystem page.
| Directory page | Covers | Site page |
|---|---|---|
asset-loading.md | Asset loading + dispatch, per-stage asset tables, disc / loader chain, scene init | Asset loader |
runtime-libs.md | PSX runtime libraries, CD / file-system, helpers, static actor templates | Boot |
game-modes.md | Input + debug, move / animation, the game-mode state machine, the title overlay | Boot, Move VM |
battle.md | Battle, HUD + 2D effects, per-frame draw, sparring tutorial (0967), command persistence (0898), field→battle transition | Battle, Battle action SM |
cast-modules.md | Slot-B cast / summon module band (PROT 0903..0966) - the addresses that are not entries: each image's own data tail, and the neighbour's bytes every image ends in | Battle |
script-vms.md | Script VMs, field-locomotion math helpers | Field VM, Locomotion |
renderer.md | Renderer, GPU primitives, ANM, MES / dialog text interpreter | Renderer |
audio.md | libsnd / libspu stack, SsAPI sequencer, SPU transfer engine, XA streaming | Audio |
menus.md | Records screen, status / equip panels (0897), inventory / spell list, shop panels, menu-overlay callees (0899) | Field menu, Shop |
world-map.md | World map controller, dev menu, render pipeline | World map |
minigames-debug.md | Minigames, debug-menu overlay (0971), other-game minigame overlay (0977) | Minigames |