At a glance

Anchor build
SCUS-94254, the NA retail disc (executable SCUS_942.54).
Debug menu
Set the byte 0x8007B98F (GameShark 8007B98F 0001), then press SELECT + △ on controller port 2.
In the port
LEGAIA_DEV_MENU=1 for play-window; the Dev-menu checkbox on the browser play page.
Sources
Public cheat-code databases and save-state guides, cross-checked against the disassembly where a row cites a function or an instruction address.
Terms
PROT is the disc's main archive (PROT.DAT); an overlay is a code image the game loads from it into RAM at runtime.
If you wantGo to
Which discs exist, and how their executables differKnown builds, region enum
How to switch on the debug menu and what it offersDebug flags, pad chords, developer menus
What a CDNAME.TXT block label coversCDNAME subsystem semantics
Cheat-pinned RAM cellsDispatch addresses, mini-game state, character struct

Known builds

IDDateNotes
PAPX-900401996-06-02Trial on PrePre Vol. 14 demo disc
PAPX-900551998-08-19Demo 2 (JP)
PCPX-961301998-08-18Demo 1 / store kiosk video (JP)
SCPS-100591998-09-09JP retail (Regaia Densetsu)
SCPS-100591998-11-16NA prototype (uses the JP serial)
SCUS-943661998-12-21NA demo
SCUS-942541999-01-29NA retail - the project's anchor build
SCES-017521999-09-27EU English retail
SCES-019442000-04-05French
SCES-019452000-05-10German
SCES-019462000-04-26Italian
SCES-019472000 (day unknown)Spanish
SCPS-91246unknown"PlayStation The Best" reissue
SCPS-45340unknownUnknown JP variant

What differs between the retail executables

The French, German and Italian discs are container-identical to the USA disc - same PROT layout, same monster records - but their executables are not the NTSC-U engine with swapped text. Two gameplay differences:

  • No battle-load stat boost. NTSC-U boosts stats at battle load; the PAL executables do not.
  • Different battle spoils. PAL pays half the record's gold and the whole record's EXP; NTSC-U pays a quarter of the gold and three quarters of the EXP.

The JP original (SCPS-10059) matches the PAL behaviour on both counts, so both are NTSC-U additions. The Spanish disc and the EU English disc are unmeasured. Details: PAL localizations.

Cross-region testing priority: SCPS-10059 (original JP), then SCUS-94254 (anchor), then SCES-01752 (EU English).

Region enum

legaia_prot::Region captures the three retail regions. ProtIndex carries it as metadata only - the TOC formula and the CDNAME layout are the same in every region.

VariantProduct codesNotes
Region::NaSCUS-94254NA retail; the anchor build and the default.
Region::JpSCPS-10059, SCPS-91246JP retail and the "PlayStation The Best" reissue. A separate compile: the +0x1B90 shift against NA holds only for the cells it was derived from.
Region::EuSCES-01752, SCES-01944..01947EU English plus the FR / DE / IT / ES localisations. Same PROT.DAT layout as NA with the text swapped; the executables differ in spoils and stat boost. No address shift is applied.

Region::from_product_code(code) maps a disc-ID prefix to its variant; Region::translate_addr(na_addr) applies the per-region shift to an NA-anchor address.

Developer attribution

Primary explorer
Prokion (also styled "Procyon"; ぷろきおん in Japanese).
Co-credited
Contrail.
Publisher
SCEI (JP), SCEA (US), SCEE (EU).

That explains the developer paths visible in SCUS_942.54 strings (H:\PROT\FIELD\<stage>\tim.dat and similar): Sony PsyQ on a Windows host with H: mapped to the project tree.

Debug flags

Three flags in the same RAM page decide how much of the developer build the retail disc exposes.

FlagJP addressEffect
0x8007B8C2shifted by 0x1B90Dev / retail loader-path flag. Retail boots it to 1 (the store at 0x80015F08), selecting the PROT-index path; the == 0 path is the h:\PROT\FIELD\<stage>\… developer branch in FUN_800255B8 and its sisters.
0x8007B9B0-Overlay print flag, separate from the menu enable. The slot-A minigame overlays gate their printf-style readouts on it, several of them also on held pad bit 0x2; in the fishing overlay that pair also swaps the bite cadence for a debug value.
0x8007B98F0x8007D51FIn-game debug menu enable. Read as the high byte of the word at 0x8007B98C: the GameShark byte write 8007B98F 0001 makes that little-endian word non-zero (0x01000000), enabling every debug branch with one check.

All three have static writers in SCUS_942.54, reached $gp-relative ($gp = 0x8007B318) - which is why a cross-reference sweep that only looks for lui pairs reports none. Published GameShark codes also prove all three are writable at runtime.

CellWriter(s)
0x8007B8C2sh at 0x80015F08 (gp+0x5AA) - the retail boot-to-1 store
0x8007B98Csw at 0x8001D444 (clear), 0x8001D490, 0x8001D4B0 (gp+0x674)
0x8007B9B0sw zero at 0x8001D448 (gp+0x698)

Only the byte 0x8007B98F has no accessor of its own; it is reached as the top byte of the word at 0x8007B98C.

JP addresses are not one shift away

The 0x1B90-byte shift between the JP and NA debug-menu byte is what Region::translate_addr applies, and it holds for the cells it was derived from (the published cheat pair), not for the build as a whole. SCPS_100.59 is a separate compile, and no single shift maps NA addresses onto it:

WhatJPNA
Inventory bag0x801CBFC80x80085958
Gold0x801CAC0C0x8008459C
Coins0x801CAC140x800845A4
Overlay slot A (PROT 0897 / 0898 / 0899)0x801D05C80x801CE818 (shift 0x1DB0)
Slot-B cast modules (PROT 0903..0966)0x801F9DB00x801F69D8 (shift 0x33D8)
Scripted-fight flagctx[+0x1E7]ctx[+0x287]
Cast phasectx[+0x1D9]ctx[+0x279]
Round counterctx[+0x1EA]ctx[+0x28A]

The battle-context fields sit 0xA0 lower in JP. The French SCES_019.44, by contrast, is the NA code relocated, with slot A at 0x801CF0A8 and slot B at 0x801F7400.

Debug pad chords

Once 0x8007B98F is set, these chords are live. The dispatcher is FUN_8001822C.

ChordEffect
SELECT + △Open the debug menu (item give, map change, stats, event flags, …)
SELECT + STARTStart the game in Debug Mode - the full-screen configuration tester in PROT 0971, reached as game mode 0 / CONFIG INIT (see boot)
R1 + R2 + ✕Coordinates debug overlay + free camera
○ (in coordinates mode)Dim lighting
▢ (in coordinates mode)Toggle the verbose overlay

Each frame FUN_8001822C reads the BIOS pad data at 0x800840F8 and builds a 32-bit button mask, _DAT_8007B850. If the debug word _DAT_8007B98C is zero it strips the upper 16 bits - the controller-2 / debug half - and that is the master gate. A large block guarded by _DAT_8007B98C != 0 then handles the chords.

button mask _DAT_8007B850 (32 bits) bits 31..16port 2 - the debug chords bits 15..0port 1 - normal play debug word == 0mask &= 0xFFFF: half stripped always kepta chord here is ignored by the debug block
The debug block reads the upper half of the mask, so a chord must arrive on the second controller port; the same buttons on port 1 land in the lower half and do nothing.

Two consequences matter when driving the menu externally:

  • The chords are port-2 bindings. The same chord on port 1 is ignored.
  • The gate does not latch for the session. The debug word's only sw writer is the shared menu / title / save-init routine, which scene transitions run, so entering a scene can clear _DAT_8007B98C mid-session. Re-assert it rather than setting it once.

Globals behind GameShark D-codes

0x8007B7C0
Previous-frame button mask, so D007B7C0 XXXX means "wait for the player to press XXXX".
_DAT_8007B874
mask & ~prev - newly pressed this frame.
0x8007B7C4
mask ^ prev - buttons that changed.

Developer menus

SELECT + △ opens the in-game debug menu, but it is not one screen: it is four tools, each resident in a different overlay, that the retail debug branches reach.

ToolRetail entryLives in
Row list (MAP CHANGE, CAMERA, ENCOUNT, … 24 rows)FUN_801EAD98 draws it; FUN_801E9F64 applies each row's pad editsField overlay (PROT 0897) - the world map is a 0897-hosted mode, not an overlay of its own
EVENT FLAG editorFUN_801DBD04 value step; FUN_801DB8B4 / FUN_801DB8F4 list cursorField overlay (0897)
Character-parameter editorFUN_801D6E18Menu overlay (0899)
Equip commitFUN_801E5A08Field overlay (0897)

Controls. In the row list, Right steps the hovered row's value up and Left steps it down (the cursor lives at ctx + 0x9E; FUN_801E9F64 dispatches on cursor - 3). The two editors take the whole pad: Up / Down walk their own cursor and Left / Right edit the hovered value, read from the packed words _DAT_8007BB84 (newly pressed) and _DAT_8007B850 (held).

The character editor is worth knowing about even outside a debug build: at the end of every tick it runs a two-stage clamp over all four party records, whichever row is hovered. That clamp is the game's own statement of its stat caps; the records themselves are described on the save screen page.

Reaching the menus in the port

The Rust port carries all four tool families, and both play hosts mount them only behind an explicit opt-in:

Native play-window
Set LEGAIA_DEV_MENU=1 in the environment.
Browser play page
Tick the Dev-menu checkbox. It starts unchecked on every load and is neither persisted nor readable from the URL, so a shared link can never switch it on.
Square
Swaps the row list for the battle-records page, on both hosts.

With the opt-in taken, the row list is drawn and driven once a frame off the same packed pad words retail's developer code reads. Without it nothing runs and nothing is drawn, so a default build or page is unchanged.

The port's list carries the subset of retail's rows whose backing state the engine owns - MAP_CHANGE, ENCOUNT, EVENT_FLAG, PLAYER_PARAM, EQUIP - so that no row steps a value nothing reads. The missing rows need overlay-resident state the engine never loads. The clearest case is the flag-list table (DAT_801F2E94, stride 0xA with an 'X' sentinel): without it, the EVENT FLAG page edits a raw index only.

Camera control axes

The coordinates debug overlay names its camera axes in film-crew terms - a handy string-search anchor for finding the camera-debug renderer.

LabelAxisLabelAxisLabelAxis
VXtruckWXtrackAXcrane
VYpedestalWY-AYtongue / pan
VZzoomWZdollyAZroll
SCfield of view

The labels live in the field overlay (PROT 0897), not in SCUS_942.54. The block starts at 0x801CF1E4 and runs VX VY VZ AX AY AZ WX WZ SC WY, then DCX / DCY / DCZ, MX0 / MY0 / MX1 / MY1, and PLAYER_STAT / HEIGHT_STAT / FREEZE_STAT / ANM_FREEZE_FLAG. Search the extracted PROT entry, not the executable.

CDNAME subsystem semantics

The block labels of CDNAME.TXT past the per-scene blocks. The numbers are the file's own #define values, which are raw in-RAM TOC indices: the extraction entry (the NNNN in a "PROT entry NNNN" citation) is the define minus 2 (see CDNAME). A label is a hint, not a verdict - several blocks hold something other than their name suggests.

#defineLabelExtractionWhat the entries hold
0..864per-scene names-Stage / field blocks (towns, dungeons, world maps, cutscenes), one block per scene name - named on scene names
865..868battle_data863..866The four player battle files PLAYER1..4 (battle data pack)
869monster_data867Monster archive: stat records, meshes, animations
870..871sound_data868..869Sound-driver data
872..875befect_data870..873Battle effects etim / etmd / vdf / efect (effect bundle)
876player_data874Player character pack (character mesh)
877..890sound_data2875..888Sound-driver data
891..892level_up889..890Level-up block; entry 890 is the title TIM
893monster_se891Monster sound effects
894card_data892Memory-card screen kanji font
895..896bat_back_dat893..894summon.dat / readef.DAT battle side-band (summon / readef)
897..971xxx_dat895..969Code overlays: boot init.pak (895), the field / battle / menu images (897..899), the slot-B band (900..966) and the battle stage modules (967..969)
972..973move_program_no970..971STR / MDEC movie player (970) and the debug-menu overlay (971)
974..979other_game972..977Mini-game overlays: fishing (972), dev modules (973 / 974), slot machine (975), Baka Fighter (976), Muscle Dome hub (977)
980..989monster_test978..987More overlays, among them FIELD BACK READ (978), dance (980) and the world-map top view (981)
990..1071music_test / music_01988..1069BGM sequences + banks (music tracks)
1072..1194vab_011070..1192VAB instrument banks
1195..1232other1 / 4 / 5 / 6 / 71193..1230Mini-game and contest assets (e.g. the ringside stills at 1221 / 1222)

Dispatch RAM addresses (GameHacking.org)

PSX RAM cells (KSEG0, 0x80000000 base) that published cheats use - useful anchors for runtime tracing.

AddressPurpose
0x80084540Current map ID (writable; "Map Modifier" and "View Credits" use it)
0x8007B6F4"Small maps" debug mode flag
0x8007B7C0Previous-frame button mask - what D0 / E0 conditional codes compare against. FUN_8001822C loads it, XORs it with the fresh mask into the edge words 0x8007B7C4 / 0x8007B874, then stores the fresh mask back. Not a dispatch selector.
0x8007B450Debug-dispatch parameter slot - the menu-request register the menu overlay polls each frame
0x800422F4Not a RAM cell - a branch inside the executable's inventory-add clamp. The "99 quantity" cheat writes 0001 there, shortening the branch so the clamp always runs (see cheat databases).

Sample values the cheats write to 0x8007B450:

ValueEffect
0x0001Open the Status Modifier menu
0x000DOpen the name modifier for Gala
0x0011Open the name modifier for Vahn
0x0085Show the end-of-game stat page
0x4097"Save Anywhere" prompt

When the trigger is 0x0003, writing a map ID to 0x80084540 warps there.

Mini-game state regions

Each mini-game is a slot-A overlay image: the mode-24 warp streams its PROT entry (fishing 0972, slot machine 0975, Baka Fighter 0976, Muscle Dome 0977, dance 0980) to 0x801CE818 over whatever was resident. The addresses below name mini-game state only while that mini-game's image is loaded; per-image extents are on the RAM map.

AddressMini-gamePurpose
0x801D9168FishingTension
0x801D9274FishingCasting power
0x801D9298FishingLife
0x801D91CCFishingFish ID modifier
0x801D53CCDancingDance points
0x801DBFC4Baka FighterPlayer life
0x801DC06CBaka FighterComputer life

Character struct

Stride 0x414 bytes (1044), confirmed by file-offset deltas in ePSXe save states (Vahn / Noa / Gala). Four slots (Vahn / Noa / Gala / Terra the wolf) make a party block of about 4096 bytes - one PSX RAM page. Offsets are relative to the Level field, all u16 little-endian unless noted.

OffsetField
-0x2C / -0x2AHP max / HP current
-0x28 / -0x26MP max / MP current
-0x20..-0x16AGL / ATK / UDF / LDF / SPD / INT, current
-0x14 / -0x12HP / MP unequipped (base)
-0x0E..-0x04AGL / ATK / UDF / LDF / SPD / INT, unequipped (base)
+0x00Level
+0x55Moves-learned count (u8)
+0x56..Moves array (u8 IDs, contiguous)
+0x177Name (string)

"Current" stats are post-equipment; "unequipped" are the base values. The stat order is fixed across both blocks and all four slots. The full record is on per-character save record.

Money and inventory

Money
24-bit (three consecutive u8 bytes, little-endian). Cap 0x00FFFFFF = 16,777,215 G.
Inventory
One shared list, 2 bytes per entry [item_id, quantity]. Weapons, armor, items and Ra-Seru levels all live in it.

Item ID table

Up to 256 IDs. Canonical NA assignments:

RangeClass
0x65..0x9A, 0xA3Items (consumables, books, flutes)
0x01..0x19Ra-Seru weapons per character × level (Meta = Vahn 1-9, Terra = Noa 1-8, Ozma = Gala 1-7, plus 0x12 = empty Noa slot)
0x1A..0x33, 0x52 (empty), 0xBA (Astral Sword)Normal weapons
0x34..0x42Helmets
0x43..0x57Armors
0x58+Boots

ID 0x12 is "empty wp. for Noa" and 0x52 is "empty wp. for Vahn" - reserved-but-empty slots.

Move tables (Tactical Arts)

Slot 0x00 is the Miracle Art starter on every character (action constant 0x1B = Vahn's Craze / Noa's Ark / Biron Rage, gated by a story flag). Vahn and Gala carry their 14 regular arts at slots 0x01..0x0E. Noa's table runs to slot 0x10 with holes at 0x02 / 0x03 - her Hurricane Kick covers three on-disc levels through one slot - so her slot 0x0F is the ordinary art Acrobatic Blitz, not a Miracle Art. Mirror: legaia_art::tables.

CharacterArts
Vahn (slots 0x01..0x0E)Burning Flare, Fire Blow, Tornado Flame, Cyclone, Hurricane, PK Combo, Spin Combo, Pyro Pummel, Cross-Kick, Power Punch, Slash Kick, Somersault, Charging Scorch, Hyper Elbow
Noa (display order)Lizard Tail, Acrobatic Blitz, Sonic Javelin, Blizzard Bash, Mirage Lancer, Dolphin Attack, Bird Step, Swan Driver, Tough Love, Rushing Gale, Tempest Break, Frost Breath, Vulture Blade, Hurricane Kick
GalaFlying Knee Attack, Battering Ram, Ironhead, Back Punch, Guillotine, Head-Splitter, Side Kick, Black Rain, Neo Raising, Electro Thrash, Bull Horns, Thunder Punch, Lightning Storm, Explosive Fist

The field VM's EXEC_MOVE (op 0x22, see script VM) feeds these IDs into the move-table consumer at FUN_800204F8; the file format is on MDT.

Translation notes

Character names that differ between the JP and NA scripts - they matter when comparing MES text across regions:

NAJP
GalaGaara
VahnVann
NoaNoah
TetsuTodd

Unused content

Useful as fixtures for validating extractors:

  • Unused enemies: index 78 ("Comm") and 140 ("Evil Bat") in the monster table.
  • Unused items: "Something Good" (sells for 50,000 G) and an unnamed accessory that forces Seru-only encounters.
  • Placeholder battle background: a graphic containing ぷろきおん in hiragana, loaded when a real battle background is missing.
  • Placeholder music: tracks copied from Alundra (the Zazan battle theme) and Wild ARMs (battle theme) sit in the SEQ / VAB data.

See also