Files
BFM-decomp/docs/memory-map.md
T
Drew T 9351b17f48 feat(phase-30 S46-2): the master IDXTAB/DESTPTR load map — and the tracker blind spot that hid it
Drew's S45 idea, delivered fleet-wide + wired into the permanent references.

- THE BLOCKER WAS OUR INSTRUMENT (R35, the 3rd time): the S45 plan ("require a
  register-verified reference to the run's address") returns ZERO for both byte-proved
  tables. They are read by gcc's indexed global-array form —
      lui $at,0x8019 ; addu $at,$at,$a0 ; lh $v0,-0x2844($at)  -> 0x8018D7BC
  — where the address exists only as (lui imm, LOAD offset) with the index add between.
  find_addr_refs killed the lui register at the addu, so the halves never rejoined and
  the tables looked unreachable. Now it carries the hi half through the index add (still
  strictly register-tracked, never window-paired) and labels those hits `-indexed`.
- tools/idxtab_map.py (NEW): fleet-wide payload -> owning binary -> load address.
  Controls-gated (refuses to emit unless ov_SC01_000 0x8017EEC8/37 + *0x801A3234, and
  ov_SC03_001 0x8018D7BC/5 + *0x801EBC68 reproduce from the images alone). Index space
  DERIVED from the extracted tree (reproduces §S44's table independently). Process-pooled.
  Rejects all-zero and majority-zero runs (132 of the first pass's 452 "tables" were that).
- RESULT: 213 binaries -> 143 with a referenced table (294), 141 with a DESTPTR (141/141
  resolved from the binary's OWN image), 61 payloads. The two dominant tables are
  fleet-wide CONSTANTS (5-entry and 37-entry, identical in all 141 overlays); the
  per-binary variable is the destination (134 distinct).
- CORRECTION 1 (R14): §S45 p6's "the SC03 trio are owned by ov_SC03_001" is refuted —
  that 5-entry table is identical in ALL 141 overlays. The byte-observed parts stand.
- CORRECTION 2 (P9): this route CANNOT settle MAIN/7+9. They are absent from all 294
  tables — but so are MAIN/13/20/34/42/44, which are byte-proved to load. Absence here
  means "not on this route", nothing more. Recorded so it is not re-derived as a finding.
- Confidence is stated per-claim in docs/idxtab-map.md: proven (controls) / high (283
  fleet-wide-class tables) / low (3 named rare rows) / UNMEASURED (recall — no oracle
  for "all tables" exists beyond the 2 controls).
- Wired in permanently: docs/idxtab-map.md (the how/when/limits), memory-map.md §S46,
  cookbook §155c (the generalizable law: "no code references X" is a claim about your
  DECODER until it is shown to recognise the forms the compiler emits), SETUP.md
  tooling inventory (R21).
2026-08-07 22:09:05 -06:00

78 KiB
Raw Blame History

Memory Map — Seed Address Ledger (SLUS-00726)

Evolvable reference layer. This file may be updated freely as RE progresses; note each change in the active phase log (CURRENT_PHASE.md). It is the single canonical address ledger for the project. Once an address is imported into Ghidra, Ghidra becomes the authoritative record for that symbol (name, type, xrefs); this file then serves as the provenance record and the staging area for addresses not yet imported.

Phase 3 T1 — Imported into Ghidra (authoritative as of 2026-06-13)

Per the rule above, Ghidra is now authoritative for these symbols' names/types/xrefs; the rows in the tables below remain the provenance record. Import policy applied (G5): verified→named symbols; reported→labels + a confidence=reported Ghidra comment; JP-only / overlay-state- dependent→not imported as static symbols. Seeded, saved, and R9-verified in the bfm project.

Verified (named symbols — §2.1, §2.2, §3.1):

  • LZSS: LzssDecodeSector 0x80018730 (+plate), LzssStateTable 0x80072A30 (+plate), lzss_state 0x800C7D24, lzss_curMask 0x800747A0, lzss_curToken 0x800747A4, lzss_outPtr 0x800747AC, lzss_ringIndex 0x800747B0, lzss_partialCode 0x800747B4, lzss_ringBuffer 0x1F800000.
  • CD-path: CdPathTable 0x80062C24 (+plate w/ full 23-entry structure), cdpath_MAIN_CD 0x80062C54, cdpath_MUSA_ID 0x80063014, cdpath_DEBUG_BIN 0x80063044 (+ghost-anchor note).

Reported (labels + confidence=reported comment; verify before relying — §3.3, §3.6): gameMode 0x800B99DE, currentLocationId 0x800B9A08, padState_raw 0x80078DC2, zoneLoadedBlock 0x80075400.

Deferred (not yet imported): the player/stat block (§3.4) and game-flags block (§3.5) — all reported, low Phase-3 relevance; candidates for a single Ghidra struct in a later phase (verify live first). JP-only (§4.1) and overlay-state-dependent (§4.2) addresses are not imported.

Phase 3 T2 — File-loader / CD-read chain (imported into Ghidra 2026-06-13)

Static trace of the file-loader from the two T1 anchors (CdPathTable 0x80062C24, cdpath_DEBUG_BIN 0x80063044). Ghidra is authoritative for these names/types; confidence verified (static — read directly from the local EXE's disassembly). The game does not call PsyQ CdRead() — it rolls its own polled async reader via CdControl.

Call chain: boot 0x800101FC → LoaderInitFileTable 0x8001971C → { LoaderResetReadState 0x80019990; CdSearchFile ×(DEBUG.BIN + 21 CdPathTable entries); CdReadRequest reads LIST.CD (0xE40 B) → listCdBuffer; builds cdFileLocTable over the 8 .CD files }. Game file-load API (5 sites 0x80010CA4/0F1C/1100/1178/12F0) → CdReadRequest 0x80019A24 → { CdQueueBusy 0x80034B98 gate; CdReadStateMachine 0x80019AF8 }. State machine issues CdControl(0x0E SetMode 0xA0 → 0x15 SeekL + CdPosToInt → 0x06 ReadN + CdReadyCallback CdReadSectorReadyCB 0x8001A338 → 0x09 Pause + CdSync/CdFlush); sectors drained by CdGetSector in the callback.

Functions named: LoaderInitFileTable 0x8001971C (+plate), LoaderResetReadState 0x80019990, CdReadRequest 0x80019A24 (+plate), CdReadStateMachine 0x80019AF8 (+plate), CdQueueBusy 0x80034B98, CdReadSectorReadyCB 0x8001A338 (function created).

Data named: read control block cdReq_* 0x800AE6F0–0x800AE7B0 (cdReq_state 6F0, cdReq_curSector 708, cdReq_sink 728, cdReq_dest 72C, cdReq_savedReadyCB 734, cdReq_cdResult 738, cdReq_drainPhase 74C, cdReq_retry 750, cdReq_timeout 754, cdReq_cdlFile 7A4, cdReq_size 7A8, cdReq_posInt 7AC, cdReq_result 7B0); cdFileLocTable 0x800AE830 (per-sub-file CdlLOC+size, built from LIST.CD); listCdBuffer 0x80180000 (LIST.CD staging, 0xE40 B); debugBinPresent 0x800747D0 (DEBUG.BIN CdSearchFile result); cdReq_sectorHdrBuf 0x80078E40 (CdGetSector header scratch).

Resolves: Q#4 (CdRead chain) ✅; Q#3 (LIST.CD RAM cache = listCdBuffer raw + cdFileLocTable parsed) located, pending live proof; Q#9 (DEBUG.BIN path is referenced, result in debugBinPresent) ✅ static. Feeds T3 (cdFileLocTable lookup) and T4 (per-sector sink FUN_8002fd14 + the 0x80035 streaming cluster reached via CdQueueBusy).

Phase 3 T3 — LIST.CD RAM cache + sub-file lookup (imported into Ghidra 2026-06-13)

Confidence verified (static). The in-RAM file system is two structures + a two-level lookup:

  • listCdBuffer 0x80180000 — raw LIST.CD (0xE40 B), read once by LoaderInitFileTable.
  • cdFileLocTable 0x800AE830 — parsed flat array, 8-byte entries {CdlLOC pos; u32 size}, one per sub-file. LoaderInitFileTable builds it by walking listCdBuffer over the 8 .CD files (MAIN, SC01–SC07), CdIntToPos(cdFileBaseSector + subfileSectorOffset) per entry. Count cross-check (✓): total entries = 447 = the exact sum of the Phase-2 per-.CD sub-file counts (MAIN 49 + SC01 86 + SC02 43 + SC03 140 + SC04 31 + SC05 30 + SC06 39 + SC07 29 = 447).
  • resourceIdMap 0x80063138 — resource-ID → file map, stride 6 = {u16 field0 = cdFileLocTable index (0xFFFF = special/non-CD); u16 field1 = loadParam; u16 field2 = procParam}.
  • Lookup: ResourceGetCdLoc(id) 0x8001B788 = cdFileLocTable[ resourceIdMap[id].field0 ].pos. Driver: ResourceLoadStateMachine 0x8001B3C4 (load-once cache via resLoad_lastId / resLoad_loadedFileIdx; streams via FUN_800363cc(field1, &cdFileLocTable[idx], 0x10); special path FUN_80036d58(field1); post-process FUN_8002d4c8(field2, 0)).

Functions named: ResourceLoadStateMachine 0x8001B3C4 (+plate), ResourceGetCdLoc 0x8001B788 (+plate). Data named: resourceIdMap 0x80063138 (+plate), resLoad_curId 0x800C6D34, resLoad_lastId 0x800A654C, resLoad_state 0x800AE7EC, resLoad_result 0x800C6D10, resLoad_loadedFileIdx 0x800747F8. Resolves Q#3 (static — count cross-check ✓; byte-level LIST.CD parse vs F4 deferred to the T6 live pass). Leads to T4: the 0x80036 streaming cluster (FUN_800363cc = StreamLoadStateMachine, the System-B loader — see T4; NB it does not itself call the LZSS decoder).

Phase 3 T4 — LZSS staging buffer (US) + streaming call sites (imported into Ghidra 2026-06-13)

Confidence verified (static).

  • lzss_sectorStagingBuf 0x80079A70 — US LZSS input staging buffer (0x800 B = 1 sector). Byte-verified from US code (lui 0x8008; addiu -0x6590 @ 0x8001A8FC). Upgrades the JP-only §3.2 datapoint (JP 0x80078BD0 + 0xEA0); the +0xEA0 delta is now confirmed for this buffer.
  • Streaming model: LzssDecodeSector (0x80018730) has exactly one caller — CdReadSectorReadyCB (0x8001A338) @ 0x8001A90C — so overlays are decompressed inline, one CD sector at a time, in the read-ready callback (no decode-after-load buffer). Raw (uncompressed) files take a parallel path in the same callback: CdGetSector → FUN_8002fc64 (memcpy 0x800 B) → cdReq_sink (0x800AE728), decrementing cdReq_wordsRemaining (0x800AE748).
  • Second loader (System B): StreamLoadStateMachine (FUN_800363cc) — a separate 0x12-state CD reader (streamLoad_state 0x8006AF00, own callback LAB_800377d8) driven by ResourceLoadStateMachine. It does NOT call LzssDecodeSector; its decode/transfer path and role (overlay vs real-time stream) → T5.

Named: lzss_sectorStagingBuf 0x80079A70 (+plate), cdReq_wordsRemaining 0x800AE748, StreamLoadStateMachine 0x800363CC (+plate), streamLoad_state 0x8006AF00, streamLoad_cbActive 0x80076110, streamLoad_savedReadyCB 0x8006AEF0; +plate on CdReadSectorReadyCB. Resolves Q#6. Two CD-load subsystems are now identified — A: CdReadRequest/CdReadStateMachine (inline LZSS); B: ResourceLoadStateMachine/StreamLoadStateMachine — T5 pins which loads the resident blob + overlays and at what US addresses.

Phase 3 T5 — US load addresses (static derivation; HEURISTIC pending T6b)

The dest addresses are EXE-constant load destinations (static-verified — the literal a1 args to CdReadRequest); their roles + runtime layout are heuristic until the T6b RAM-dump proof. Source: loadDestPtrTable 0x80072C70 (5-entry destination table read by the boot loaders).

US addr Role (heuristic) JP analogue Cross-check
0x800CEDF8 resident blob base 0x800CDF58 = JP +0xEA0 (main-block delta) ✓; literal dest of all 5 boot loads
0x80128158 location script overlay base 0x80128508 0x8012xxxx; spans AP-world US patch sites 0x8013–0x8018xxxx + boss-HP 0x8018EE00..0x801F8714 ✓
0x800CAE08 / 0x800CCB1C / 0x800C7F08 resident-region blobs (boot) — 0x800Cxxxx resident band

Boot-load mechanics: 5 CdReadRequest sites (in gameMode-dispatched handlers via FUN_80010B40 / PTR_LAB_800629F4) stream fixed cdFileLocTable sub-files ([1],[3],[8],[10],[11]) to loadDestPtrTable[0] = 0x800CEDF8. Entries [1]+ are runtime-indexed (no static xref); the overlay (entry[1]) dest 0x80128158 is selected per-location. Note: low boss-HP (0x8012052E..0x801212CA) sits below 0x80128158 ⇒ a separate per-location data/entity block — resolve at T6b/T7.

Resolves Q#1 (static): US resident 0x800CEDF8 + overlay 0x80128158 derived; staging buffer 0x80079A70 already verified (T4). Not planted as static Ghidra symbols per G5 rule 4 (overlay region is state-dependent) — recorded here, to be promoted to verified + imported in T6b/T7.

Phase 3 T6b — MILESTONE ✅: US load addresses proven against live RAM (2026-06-13)

Proven against a live PCSX-Redux RAM dump (Windows-native emulator; WSL fetched the full 2 MB via http://172.17.208.1:8081/api/v1/cpu/ram/raw; EXE verified resident, RAM[0x10000:]==extracted EXE). Game state: standing still in the tutorial forest zone (tut_forest), freshly loaded. (The game opens with an Allucaneet FMV, but actual gameplay begins in tut_forest — a never-revisited tutorial area with scripted ability-assimilation events.)

Blob Extracted source US load addr Proof (RAM slice vs extracted)
Resident blob MAIN.CD/FILE_010/1.1 (type-1, 365,404 B) 0x800CEDF8 verified 18,788 B contiguous exact prefix, sha1 dbc55765… EQUAL; 92.98% total identical; divergences = runtime data writes
Location overlay SC01.CD/FILE_077/0.4.dec (type-4 LZSS, 731,607 B) 0x80128158 verified 389,400 B contiguous exact prefix, sha1 8e40afab… EQUAL; 98.59% total identical; first divergence @+0x5F118 is a zero field the loader fills with live ptr 0x80180910

Both T5-derived addresses CONFIRMED. The +0xEA0 resident-block delta (JP 0x800CDF58→US 0x800CEDF8) is now byte-proven. Overlays are position-locked (loaded verbatim to a fixed vaddr — resolves Open Q#8): the large contiguous exact-sha1 prefixes at the exact vaddrs prove no relocation; divergence is purely runtime data init (P9). Overlay-map datapoint: tut_forest (tutorial forest, first playable zone) ⇒ SC01.CD FILE_077 "0.4" → 0x80128158. The method also re-validates the extractor — the live game's decompressed overlay == our LZSS decoder output for 389,400 contiguous bytes.

Phase 3.5 — Prototype correspondence (2026-06-14)

Two CRC-verified US prototypes imported into the bfm Ghidra project and diffed against retail. Full report + go/no-go: docs/proto-correspondence.md. Verdict: NO-GO as a Gen1 label/symbol accelerator (neither proto carries symbols retail lacks; both have FEWER names than retail); retain Sep-8 as a Phase-6 compiler-determinism corroborant and both as Gen2 overlay/engine assets.

Build Main EXE (region) Load addr SHA1 Funcs vs retail
Sep-8 "Master" SLUS_007.26 (PROTO) 0x80010000 43006a31… 1726 99.6% byte-identical; only 3 substantial funcs differ
Aug-31 "Demo" MUSASHI/USA_DEMO.EXE (DEMO) 0x80018000 6150b0f8… 1737 66% norm-identical; 862 1:1 pairs; SC02 region
  • All three builds detected PsyQ 4.0.0 (independent corroboration for the Phase-6 fingerprint).
  • Sep-8 pre-release diff (the ONLY 3 substantial code changes vs retail — a Phase-6 cross-check): DebugMenuHandler 0x80011144, CdReadSectorReadyCB 0x8001a338 (424 ins), SaveLoadRoutine 0x8002b154.
  • DEMO dispatch anchors (confidence verified vs the demo binary; DEMO region — NOT valid for US retail): GameModeDispatch 0x80018b48, gameModeHandlerTable 0x8006ae8c (18 entries, same order + same idx[10]==idx[15] duplicate as retail's 0x800629f4), gameMode 0x800c1eb6, debug-menu slot[7] handler 0x800194cc (code diverged from retail DebugMenuHandler). NB demo gameMode 0x800c1eb6 ≠ retail 0x800b99de and is NOT the JP −0xEA0 delta — the demo has its own layout.
  • Proto EXEs gitignored under extracted/proto/; programs live in the bfm Ghidra project (gitignored).

Purpose and rules

  1. Every address carries a Region and a Source. No exceptions.
  2. JP addresses are NEVER assumed valid for US. The Archipelago AP-world documents cross-region deltas (see Region deltas), but these are block-specific and heuristic only — any US address derived by delta must be marked heuristic until byte-verified against the US binary or a live US RAM dump.
  3. Confidence levels:
    • verified — byte-checked against the local SLUS-00726 dump in this project (or an address referenced directly by byte-verified code).
    • reported — published by an external source; plausible and often multi-source, but not yet locally verified against the US binary/RAM.
    • heuristic — derived via a cross-region delta or pattern match; must be re-derived before any use as a symbol.
  4. Overlay-region addresses (≳0x80120000) are state-dependent: they are only valid while a specific location overlay is loaded. Never import them as static EXE symbols.

Region codes

Code Meaning
US SLUS-00726, USA retail (final build 1998-09-22 per Hidden Palace)
JP SLPS-01490 Brave Fencer Musashiden (1998-07-16); re-release SLPS-02769 unexamined
JPDEMO Japanese demo build (known only from TCRF's per-version debug GameShark code)
PROTO US prototypes: Aug-31-1998 demo proto, Sep-8-1998 near-final master (Hidden Palace)
DEMO Corrected 2026-06-13 (Drew): SLUS-90029 = the FF8 playable demo bundled in the BFM case — not BFM content. No known playable US BFM demo exists. Closest: SLUS-90028 "Squaresoft on PlayStation Vol.1" carries BFM preview FMV only (its playable demo is Xenogears). The Aug-31-1998 proto is the actual playable pre-release BFM build.

Region deltas (JP ↔ US) — heuristic only

From the AP-world client (client.py), which supports both discs and is independently consistent with TCRF's per-region debug codes:

Block Delta (US → JP) Cross-check
Main state block (0x800B9xxx) −0xEA0 US menu-state 0x800B99DE ↔ JP 0x800B8B3E — matches TCRF debug codes exactly
Zone-loaded block (0x80075400) −0xE70 AP-world only
Stats table −0xEA4 AP-world only (note: not the same as the main-block delta)
Code regions varies: +0x2A8, +0xA70, +0x344, +0x2D0, +0xE8 per-overlay, no single delta — never apply blindly

UNVERIFIED inconsistency carried from research: AP-world notes the town-ID-check patch at US 0x8015A7E4 with "JP equivalent 0x8015AB20, delta +0x2A8", but 0x8015AB20 − 0x8015A7E4 = 0x33C, not 0x2A8. Do not resolve by guesswork; re-check client.py when importing.

Address-base conversion for external sources

Source Convention Conversion to PSX virtual address
AP-world client.py BizHawk MAIN_RAM offset add 0x80000000 (e.g. 0x078EB4 → 0x80078EB4)
Autosplitter (Aalaizah gist) BizHawk octoshock.dll offset subtract 0x30DF90, add 0x80000000 (conversion cross-verified via playerLocation)
GameShark codes (libretro cht / gamehacking.org) 0x80-prefixed or bare offset already KSEG0 (8007xxxx etc.)

1. PS-X EXE header facts (locally verified 2026-06-10)

Extracted from SLUS_007.26 (disc LBA 24, 413,696 bytes, MODE2/2352 track 1; user data at raw-sector offset 24).

Field Value Confidence Notes
magic PS-X EXE verified
pc0 (entry) 0x80010000 verified
gp0 0x00000000 verified Header carries no $gp ⇒ −G0 CONFIRMED (Phase 5): the splat disasm has zero ($gp) base accesses / %gp_rel relocs
t_addr 0x80010000 verified
t_size 0x64800 verified EXE image spans 0x80010000–0x80074800
sp 0x801FFFF0 verified Matches SYSTEM.CNF STACK = 801ffff0

The EXE is tiny (~400 KB) relative to the game: the bulk of engine/script code lives in the .CD overlays (see §4).

Script / event system — RESOLVED: compiled-MIPS dispatch, NO bytecode VM (Phase 12, byte-backed)

The Gen2-roadmap "Script VM instruction set" open question is resolved by matching the resident engine (123/146 fns, 85.6% byte-identical from C source, Phase 12): BFM has no bytecode/script VM. "Scripting" is compiled MIPS code structured as state machines dispatched through function-pointer tables indexed by game-state variables — the same idiom at every level:

  • EXE top level: GameModeDispatch (0x80010B40) → gameModeHandlerTable[gameMode]() (18 entries @0x800629F4; gameMode @0x800B99DE).
  • Resident engine (matched, the proof): the same idiom at finer grain — func_800CEDFC = if (D_800B99F0 < 0x14) D_800D3430[D_800B99F0](); also D_800D3480[D_800B99F6]() (func_800CEE40), the D_800D3488[…] cluster (func_800CF4D4 etc.), D_800D3490[D_800B99FC](), and the func_800D2F0C/2FB0/30C8/31C0/31FC cluster dispatching D_800D39xx[entity->state](). The dispatch index is always a game-state var (game mode, currentLocationId 0x800B9A08, per-entity sub-state at struct +0x14/+0x15).
  • Location overlays (0x80128158): contain compiled MIPS (text/font pointers materialized inline by lui/addiu), not bytecode — they call the resident engine's fns at fixed addresses.

So "matching the script VM" means matching these compiled dispatchers + handlers (done in the harvest), NOT writing a bytecode interpreter. The VM_* symbols are PsyQ libsnd (refuted as a game VM, Phase-12 T1). Basis for the gen2-roadmap EXIT criterion #2 amendment (T6): "script/event system resolved (compiled-MIPS state/mode dispatch, no bytecode VM)."

Text→data split (Phase 5, splat). config/splat.us.exe.yaml splits file [0x800,0x531DC) → vram [0x80010000,0x800629DC) as code (asm) and [0x531DC,0x65000) → [0x800629DC,0x80074800) as data (the splat psxexeinfo estimate; vram = fileoff + 0x8000F800). The true code↔data transition is a mixed region — last func USERFUNC_OBJ_84 @0x8006290C, then small data (D_800629xx), then gameModeHandlerTable @0x800629F4 (the game-mode handler pointer table). splat's in-asm data detection labels it bar one address, D_800629D4 (referenced by the boot code), added by hand to config/symbols.us.txt. Byte-identity is robust to the exact boundary (the disasm round-trips); a clean rodata/data segmentation is a Gen2 refinement.


2. Static EXE region — 0x80010000–0x80074800 (US)

2.1 LZSS decompressor (byte-verified vs AiroRom listing)

The annotated MIPS listing at https://raw.githubusercontent.com/wiki/pleonex/AiroRom/Brave-fencer-Musashi.md (the GitHub wiki UI fails to render it; fetch raw) was byte-verified in-session against the local SLUS-00726 EXE (words checked at 0x80018730/34/90, 0x800187BC, 0x80018824, 0x80018910, and the table at 0x80072A30). The routine is a resumable 5-state machine consuming input in 0x800-byte CD-sector chunks. Format details live in docs/formats.md.

Address Symbol/Name (proposed) Region Source/Provenance Confidence Notes
0x80018730 LzssDecodeSector (entry) US AiroRom wiki listing; byte-verified vs local EXE verified Primary Phase-1 milestone anchor (MCP round-trip target)
0x80018790 .LzssState1_initToken US AiroRom listing (state table), byte-verified code verified State 1: init token/ring
0x800187A4 .LzssState2_checkBit US same verified State 2: check token bit
0x800187F8 .LzssState3_match US same verified State 3: process match
0x80018824 .LzssState0_finish US same verified State 0: finish
0x800188B4 .LzssState4_nextBit US same verified State 4: next token bit
0x80018910 (return point) US same verified Routine return
0x80072A30 LzssStateTable US AiroRom listing; byte-verified vs local EXE verified Data: 00000000 FFFFFFFF then 5 pointers to the state entry points above

2.2 CD-path string table (locally verified 2026-06-10, exact)

Re-derived in-session by scanning the extracted EXE: a table of \NAME;1 path strings, 23 entries, 0x30-byte stride, spanning 0x80062C24–0x8006307x. (An earlier session note said "≈0x80063045"; the exact string starts, including the backslash, are below.)

Address Symbol/Name (proposed) Region Source/Provenance Confidence Notes
0x80062C24 CdPathTable (first entry: \LIST.CD;1) US local EXE scan 2026-06-10 verified Entry stride 0x30; order: LIST.CD, MAIN.CD, SC01–SC07.CD, then STR movies
0x80062C54 \MAIN.CD;1 US local EXE scan verified
0x80062C84 \SC01.CD;1 … (SC02–SC07 follow at +0x30 each) US local EXE scan verified SC07 at 0x80062DA5−1 = 0x80062DA4
0x80062DD4–0x80062FE4 STR movie paths (ST01–ST06, SHOPS01, OPEN, END, OUT; entry starts) US local EXE scan verified ST04 and ST05 each appear twice; LOGOA/LOGOB.STR and the .DA audio files are absent from this table — referenced elsewhere (TBD)
0x80063014 \MUSA.ID;1 US local EXE scan verified Disc-ID check file (9 bytes MUSASHI\r\n on disc)
0x80063044 \DEBUG.BIN;1 US local EXE scan verified DEBUG.BIN is absent from the retail ISO ⇒ a surviving debug-loader code path references this string. Prime RE anchor: find xrefs to locate the debug loader

2.3 Other static EXE data

Address Symbol/Name (proposed) Region Source/Provenance Confidence Notes
0x800638FA BodyStatLevelTable US AP-world client.py (0x0638FA + 16*level) reported 16-byte records indexed by level; inside EXE static data — verify in Ghidra
0x80072DF0 saveHeaderTemplate US VERIFIED live (T6b 2026-06-14; RAM==EXE) verified PS1 memcard save-header template: Hero default name (SJIS full-width) @+0; memcard filename BASLUS-00726MUSASHI @+0xC; title BRAVE FENCER MUSASHI (SJIS) @+0x20; save/load handler code ptrs 0x8002B154 / 0x8002B1AC / 0x8002BEA4 @+0x54. Anchors Q#5
~EXE+0x62620 overlay/script pointer table JP jywjyw note.md (JP EXE file offset) reported (JP-only — re-derive for US) EXE-side pointer table tied to the resident script blob (§4). US analogue TBD — Phase 3

3. BSS / runtime statics (0x80074800–0x800Cxxxx region + scratchpad)

The PS-X EXE header declares no separate BSS; everything above the file image (>0x80074800) is runtime-cleared memory. Addresses 0x800747xx fall inside the loaded image (late data/sbss).

3.1 LZSS decompressor state (code-derived from the byte-verified routine)

Address Symbol/Name (proposed) Region Source/Provenance Confidence Notes
0x800747A0 lzss_curMask US AiroRom listing (referenced by verified code) verified Current flag-bit mask (byte), 0x01→0x80
0x800747A4 lzss_curToken US same verified Current token byte
0x800747AC lzss_outPtr US same verified Output write pointer
0x800747B0 lzss_ringIndex US same verified Ring-buffer write index
0x800747B4 lzss_partialCode US same verified Partial 16-bit match code (resume across sector boundary)
0x800C7D24 lzss_state US AiroRom listing (referenced by verified code) verified State variable 0–4; indexes LzssStateTable @ 0x80072A30. Lives beyond the EXE image (runtime BSS)
0x1F800000 lzss_ringBuffer US (hardware) AiroRom listing; byte-verified code verified 1024-byte ring window in PS1 scratchpad (D-cache). Match offsets are absolute ring indices with −1 bias — see docs/formats.md

3.2 Loader buffers

Address Symbol/Name (proposed) Region Source/Provenance Confidence Notes
0x80079A70–0x8007A270 lzss_sectorStagingBuf (0x800 bytes) US T4: byte-verified from US code (lui 0x8008; addiu -0x6590 @ 0x8001A8FC) verified Sector-at-a-time LZSS input staging buffer; filled by CdGetSector, consumed by LzssDecodeSector inline (see Phase 3 T4 block). JP analogue 0x80078BD0 (+0xEA0 delta — confirmed for this buffer)
0x80180000 / 0x800AE830 listCdBuffer (raw, 0xE40) / cdFileLocTable (parsed) US T2/T3: located in US code verified LIST.CD raw staging + the parsed {CdlLOC;size}×447 table; replaces the old listCd_ramCache TBD (see Phase 3 T2/T3 blocks)

3.3 Pad / input state

Address Symbol/Name (proposed) Region Source/Provenance Confidence Notes
0x80078DC2 padState_raw (halfword) US TCRF debug GameShark condition (D0078DC2 0200) reported 0x0200 = L3. Distinct from 0x80078DCA — do not conflate
0x80078DCA padState_mirror US gamehacking.org #88529 / libretro cht (joker code) reported Separate pad mirror/field used by GameShark joker codes

3.4 Player state block (0x80078E7C–0x80078F08)

Sources: gamehacking.org NTSC-U page 88529 and the libretro-database GameShark .cht (35 cheats; addresses recovered via the libretro mirror because gamehacking.org blocks scripts), AP-world client.py, and the LiveSplit autosplitter gist. A coherent player/stats struct clearly lives here — good candidate for a single Ghidra struct.

Address Symbol/Name (proposed) Region Source/Provenance Confidence Notes
0x80078E7C gameClock (2 words) US libretro cht reported In-game clock
0x80078E80 storyProgress (u32) US autosplitter (octoshock conversion cross-verified) reported Progress/story counter
0x80078E8C gold (u16, stored as gold ÷ 10) US VERIFIED live (T6b, 4-state triangulation 2026-06-14) verified Holds 145/155/165 for displayed gold 1450/1550/1650 (game stores gold/10 — gold is always ×10, which defeated naive value searches). Mirror copy at 0x8011F804. Supersedes the reported 0x80078E8E ("money", which reads 0 — refuted)
0x80078EA4 tiredness_raw (u16) US VERIFIED live (T7): 12984→55620→0 (accumulates w/ activity, resets to 0 on sleep) verified Raw tiredness accumulator; displayed % is derived (likely overflow-counted). Supersedes 0x80078EA6 ("tiredness" — refuted: moved 119→160 on sleep, not →0)
0x80078EAC dayCounter (u8) US VERIFIED live (T7): 1→2 on the Day1→Day2 sleep rollover verified In-game day number (day N = day-of-week N early game; dow not yet separable; 0x80078EAF refuted as dow)
0x80078EB1 hour (u8) US VERIFIED live (T7): 13→14→15→18→19→3 across 6 dumps verified In-game hour 0-23; time advances in 15-min jumps (~7.5 IRL s each). Minute field not cleanly located; old 0x8007CE82 "clock" refuted (coincidence)
0x80078EB2 hp_max (u16) US VERIFIED live (T6b change-detection 2026-06-13) verified =150 across two states (HP max); pairs {max,cur} with 0xEB4
0x80078EB4 hp_current (u16) US VERIFIED live (146→136 tracked Drew's HP) verified Resolves Q#12: AP-world right — 0xEB4 = current HP, 0xEB2 = max
0x80078EB6 / 0x80078EB8 bp_max / bp_current (u16) US VERIFIED live (0xEB8 137→91 tracked Drew's BP) verified Ledger corrected — labels were SWAPPED: 0xEB6=max(150), 0xEB8=current
0x80078EE4 / 0x80078EE8 bodyLevel / bodyExp US gamehacking / cht reported
0x80078EEC / 0x80078EF0 mindLevel / mindExp US gamehacking / cht reported
0x80078EF4 / 0x80078EF8 fusionLevel / fusionExp US gamehacking / cht reported Fusion sword (JP らいこうまる)
0x80078EFC / 0x80078F00 luminaLevel / luminaExp US gamehacking / cht reported Lumina sword (JP Reigando); exp max 0x031B per cht
0x80078F08 exp_quickLevel US gamehacking / cht reported

Known-bad datum: the cht's "Charging Bar Always Full" code is listed as 80078EEBE (9 digits — typo for 0x078EEB or 0x078EBE). UNVERIFIED — test in emulator before use.

3.5 Game flags block (0x800AE6xx)

Address Symbol/Name (proposed) Region Source/Provenance Confidence Notes
0x800AE640 musicId (byte) US gamehacking / cht reported Music modifier; track 128 is the unused TCRF tune
0x800AE64A–0x800AE64C scrolls_legendaryArmor (3 bytes) US AP-world; cht (armor 0x0AE64B–4C); autosplitter (0x800AE64B) reported
0x800AE650 minkuFlags US AP-world reported
0x800AE651 chestFlags (5 bytes) US AP-world; autosplitter uses byte 0x800AE652 within it reported
0x800AE658 techsBitfield US gamehacking / cht reported All-techniques bitfield
0x800AE666 castleGuardState US AP-world reported
0x800AE671 binchoRescueFlags (9 bytes) US AP-world reported

3.6 Mode / location / world state

Address Symbol/Name (proposed) Region Source/Provenance Confidence Notes
0x800B99DE gameMode US TCRF debug code (800B99DE 0007); AP-world ("main-menu flag") reported Writing 7 (while pad == 0x0200) enters the debug menu — see §5. JP: 0x800B8B3E; JPDEMO: 0x800B894E
0x800B9A08 currentLocationId (s16/u16) US autosplitter; AP-world; gamehacking "map modifier" (3 independent sources) reported Map IDs: 0x1010 town, 0x2018 interiors, 0x3000+ dungeons; autosplitter start-trigger value 12293 (0x3005); ~25 room IDs enumerated in the gist
0x80075400 zoneLoadedBlock US AP-world (zone-loaded + portal-destination control block) reported JP delta −0xE70
0x800BA1E7 inventory US AP-world reported
0x800BA1F3 geezerFlag US AP-world reported
0x800BA1F8–0x800BA21A rescueCompleteFlags US libretro cht reported
0x800BA21B toyCollection (43 bytes) US AP-world reported
0x800BA285 steamwoodStatus US AP-world reported Adjacent to:
0x800BA286 raftLogs US gamehacking / cht reported
0x8000BA94 buildIdBytes US+JP AP-world (region auto-detect reads MAIN_RAM 0x00BA94) reported Below the EXE base (kernel-area RAM) — nature TBD; AP uses it to distinguish US vs JP at runtime

3.7 Misc reported (low priority)

Address Symbol/Name (proposed) Region Source/Provenance Confidence Notes
0x800A63F3 shading byte US gamehacking / cht reported
0x800AFAF9/FB/FD screen-tilt bytes US gamehacking (FB/FD) / cht (F9/FB/FD) reported
0x800AFB0C "model weirdness" US gamehacking / cht reported

4. Overlay region (≳0x800B9480 / 0x800CDF58+)

4.1 JP overlay map — JP-only, re-derive every address for US in Phase 3

Source: jywjyw's bravefencer-hack doc/note.md (Japanese SLPS-01490; same engine). jywjyw's IDA workflow — load the EXE, then "load additional binary" for the two blobs at the addresses below — is directly replicable in Ghidra once US equivalents are found. Research explicitly warns: US addresses will differ (e.g. US font offset 0x4CBC vs JP 0x4C4C).

Address Symbol/Name (proposed) Region Source/Provenance Confidence Notes
0x80010000–0x80073FFF JP EXE image JP jywjyw note.md reported JP file offset = vaddr − 0x8000F800; JP image slightly smaller than US (US ends 0x80074800)
0x800B9480 SQV/audio region JP jywjyw note.md reported (JP-only)
0x800CDF58–0x80128504 residentScriptBlob — MAIN.CD FILE_010 PAC entry "1.1" (type 1, uncompressed) JP jywjyw note.md reported (JP-only); US (T5): 0x800CEDF8 (heuristic) Memory-resident script blob loaded at game start; pointer table in EXE near JP file offset ~0x62620. US base 0x800CEDF8 = JP +0xEA0 — loadDestPtrTable[0]; pending T6b proof
0x80128508+ locationScriptOverlay — current location's type-4 "0.4" blob (LZSS-decompressed, ~800 KB) JP jywjyw note.md reported (JP-only); US (T5): 0x80128158 (heuristic) Contains MIPS code: text/font pointers materialized by lui/addiu pairs inside the blob (JP examples 0x8013928C/0x8013920C/0x801391EC; Vehek's US example at 0x8017E4DC). Third (town-dialog) script chains from inside the second. US base 0x80128158 = loadDestPtrTable[1]; pending T6b proof
0x801FF800–0x801FFFFF script runtime scratch JP jywjyw note.md reported (JP-only) Top of RAM, below initial SP 0x801FFFF0

4.2 US overlay-resident addresses (state-dependent — only valid with the right overlay loaded)

Boss HP living at 0x8012xxxx–0x801Exxxx is the key observation that overlays load high (≳0x80120000), consistent with the JP map's 0x80128508 base. Source: LiveSplit autosplitter (octoshock offsets, conversion cross-verified) unless noted.

Address Symbol/Name (proposed) Region Source/Provenance Confidence Notes
0x8012052E bossHp_BenEd US autosplitter reported overlay-state-dependent
0x80120316 bossHp_Skullpion/QueenAnt/TowerOfDeath/DarkLumina2 US autosplitter reported same slot reused by 4 bosses
0x801204A8 steamwoodValveTimer US libretro cht reported
0x801212CA bossHp_Kojiro US autosplitter reported
0x80126B8E player Y-velocity area ("moon jump") US gamehacking / cht reported code/data in overlay
0x80126C00 jump/physics code US libretro cht reported code patch site
0x80126D98 midair-jump counter US gamehacking / cht reported
0x8013F430 scroll-cursor patch site US AP-world (code patch; JP delta +0x344) reported instruction rewrite site
0x8014BCF8 max-BP calculation patch site US AP-world (nop'd addu $v0,$a1; JP delta +0x2D0) reported
0x8015A7E4 town-ID check patch site (andi $v0,$s0,0x4000) US AP-world (JP noted 0x8015AB20 — see delta inconsistency in header) reported
0x8018E096 entrance-index patch site US AP-world (JP delta +0xE8) reported
0x8018EE00 bossHp_RelicKeeper US autosplitter reported
0x801E4398 bossHp_SteamKnight US autosplitter; VERIFIED live (T6b 2026-06-14): 27→0 as boss defeated verified (state-dependent) Valid only while the Allucaneet Castle overlay (SC02/FILE_005) is loaded; live-anchors the §4.2 boss-HP region
0x801EFD28 bossHp_FrostDragon US autosplitter reported
0x801F8714 bossHp_DarkLuminaFinale US autosplitter reported

(Also documented by AP-world but not a RAM address: hair-color palette edited directly in VRAM at GPURAM offset 0x86AC4.)

4.3 Overlay map — live-verified (Phase 3 T6b/T7, PCSX-Redux RAM dumps 2026-06-14)

Every overlay below was identified by byte-matching the live RAM at the overlay slot 0x80128158 against our extracted */0.4.dec (LZSS-decompressed) files; the resident blob stayed at 0x800CEDF8 (MAIN.CD/FILE_010/1.1) throughout. Confidence verified (live RAM == extracted blob, large exact prefix). Format: location/state → SC??.CD / FILE_nnn.

Location / state Overlay (*/0.4) Notes
Resident blob (always) MAIN.CD/FILE_010/1.1 @0x800CEDF8 type-1 uncompressed; the milestone resident blob
tut_forest (tutorial forest — game start, never revisited) SC01/FILE_077 the milestone overlay (daytime)
Spiral Tower exterior (pre- & post-erection) SC01/FILE_080 tower erection is an in-overlay state, not a reload
Spiral Tower lower interior SC01/FILE_084
Spiral Tower upper (top + head-chase down) SC02/FILE_000 ≡ 003 duplicate pair (byte-identical)
Allucaneet Castle Ch1 — Steam Knight 3-stage fight (hall→courtyard→dummy town) SC02/FILE_005 whole multi-room set-piece = ONE overlay
Allucaneet Castle Ch2 — Room/bedroom SC01/FILE_005 ≡ 006 duplicate pair; king/butler event + Sleep/Toys menu
Allucaneet Castle Ch2 — Library SC01/FILE_008 scene incl. frozen book-reading sub-mode
Allucaneet Castle Ch2 — hub (blue nav menu) SC01/FILE_004 menu-warp hub: Visit/Library/Room/SubMenu/Village
Allucaneet Castle Ch2 — Visit / main hall (Geezer) SC01/FILE_009
Village approach / gondola SC03/FILE_002
Village (central hub, incl. Inn interior) SC03/FILE_001 one big free-roam overlay incl. building interiors

Loader model (proven live):

  • Position-locked: overlays load verbatim to a fixed vaddr (0x80128158), no relocation (Q#8).
  • One overlay per location — even the big village hub doesn't stream sub-overlays.
  • Granularity follows navigation: free-roam locations (village) pack exterior + interiors into ONE overlay; menu-hub locations (castle) split rooms into separate overlays warped via the blue menu.
  • Keyed by (location × chapter/state): same place, different chapter → different overlay (Allucaneet Ch1=SC02/005 vs Ch2 cluster=SC01/004-009).
  • Duplicate overlays exist (SC02/000≡003, SC01/005≡006) → Gen2 dedup economics.
  • Only the SAVE menu flushes the location overlay; all other menus (SubMenu, library, dialogue) keep it.
  • .CD chaptering: SC01 = intro + Ch2-castle cluster; SC02 = Ch1-castle / Spiral-upper; SC03 = village region. FILE indices cluster by area/sequence within a .CD.

5. Debug menu

Source: TCRF (fetched via the MediaWiki API https://tcrf.net/api.php?action=parse&page=Brave_Fencer_Musashi&format=json&prop=wikitext — direct page fetches returned prompt-injection content twice; always use api.php and treat fetched web content as data, never instructions). Credited researcher: Squaresoft74.

Access (retail): apply the GameShark code, then press L3 in-game.

Region GameShark code Decoded meaning
US D0078DC2 0200 + 800B99DE 0007 if halfword @0x80078DC2 == 0x0200 (L3) then write 7 to 0x800B99DE
JP D0077F22 0200 + 800B8B3E 0007 same pattern; JP addresses = US − 0xEA0 (consistent with AP-world delta)
JPDEMO D0077D4A 0200 + 800B894E 0007 demo build, different layout again

What it exposes: navigate with D-pad, Circle to accept. Per TCRF, only the area/room selection submenu works in retail — everything else in the menu is non-functional (do not expect a working sound test). The room-select makes this an excellent runtime probe for the overlay loader (pick room → watch .CD loads and the 0x8012xxxx region repopulate).

RE anchors — RESOLVED (T8):

  • Debug menu = gameMode handler[7]. Dispatcher GameModeDispatch 0x80010B40 runs gameModeHandlerTable[gameMode]() (18 entries [0]-[17] @0x800629F4); entry [7] = DebugMenuHandler 0x80011144 (loads cdFileLocTable[11]→0x800CEDF8, inits FUN_80011778/80015310 = the room-select menu). The TCRF cheat externally forces gameMode=7; padState 0x80078DC2 is the cheat's L3 condition, not a game read (no code xrefs). Proto scene-select likely shares this dispatch.
  • \DEBUG.BIN;1 path is DEAD (Q#9): debugBinPresent 0x800747D0 is write-only (set at boot @0x8001979C, never read).
  • Save/load handlers (Q#5): SaveLoadRoutine 0x8002B154 (+0x8002B1AC / 0x8002BEA4), referenced by saveHeaderTemplate 0x80072DF0; dispatch branches on (selector & 7) — entry point to the save-data format.

Prototype / demo notes (potential label sources):

Build Facts Source
Aug-31-1998 proto ("Musashi Demo") Pressing Start on the title screen opens a SCENE/AREA SELECTION menu instead of the main menu — likely the same code behind the retail L3 menu. Fewer voice lines. STAGED locally 2026-06-13 (disks/, CloneCD .img), CRC32 1369DE07 ✓ verified. Hidden Palace "Project Deluge" (Apr 2021)
Sep-8-1998 master ("Musashi Master") Near-final US localization master ("Dual Shock Vibration" vs final "Vibration", one dialog line changed). Useful to validate diff tooling. STAGED locally 2026-06-13 (disks/, CloneCD .img), CRC32 5C24728E ✓ verified. Hidden Palace
SLUS-90029 (and "Squaresoft on PlayStation Vol.2") Not BFM — an FF8 playable demo disc bundled in the BFM retail case (corrected 2026-06-13, Drew). The FF8-demo collector disc is staged in disks/ but carries no BFM code. No playable US BFM demo is known; SLUS-90028 "Squaresoft on PlayStation Vol.1" (bundled with Parasite Eve) has BFM preview FMV only (its playable demo is Xenogears). Drew; WebSearch (game-rave, emuparadise)

No debug symbols in either prototype — VERIFIED (Phase 3.5): both main EXEs are stripped identically to retail (no .SYM, no source paths, no symbol tables; only \DEBUG.BIN;1 + the stock SetGraphDebug format string, as in retail). The disc-wide _dbg_RE1L@55 / C:\TIMPACK\ strings are in retail too (shipped data, not a debug build). The proto's scene-select shares the retail debug-menu architecture (identical 18-mode dispatch table) but not its handler code or any labels — see docs/proto-correspondence.md (Q#10 resolved).


6. Open questions (mirrors Phase 3 targets)

# Question Why it matters Suggested attack
1 VERIFIED (T6b) — resident 0x800CEDF8 (= JP +0xEA0), overlay 0x80128158 both byte-proven vs live RAM (sha1-equal prefixes); staging buffer 0x80079A70 (T4) Required before any overlay can be imported into Ghidra at the right vaddr Done; import at these vaddrs in T7
2 US analogue of the JP EXE pointer table (~JP EXE offset 0x62620) Maps which sub-file loads where — the overlay map Search US EXE for the same pointer pattern; xref from the resident-blob loader
3 RESOLVED (T3, static) — LIST.CD raw → listCdBuffer 0x80180000 (0xE40 B); parsed → cdFileLocTable 0x800AE830 ({CdlLOC;size}×447, count cross-checks Phase-2 exactly); ID indirection via resourceIdMap 0x80063138; lookup ResourceGetCdLoc 0x8001B788 The game reads sub-file TOCs from this in-RAM copy; key to the loader Byte-level LIST.CD parse (F4) deferred to T6 live pass
4 CdRead call chain RESOLVED (T2) — CdReadRequest 0x80019A24 → CdReadStateMachine 0x80019AF8 (hand-rolled CdControl SeekL/ReadN, not PsyQ CdRead); LIST.CD read by LoaderInitFileTable 0x8001971C Core of the file-loader RE (Phase 3 milestone) See the "Phase 3 T2" block above
5 ANCHORED (T6b) — save-header template saveHeaderTemplate 0x80072DF0 (filename BASLUS-00726MUSASHI, SJIS title BRAVE FENCER MUSASHI); save/load handlers ~0x8002B154/1AC/BEA4. Full data-block format + checksum still TBD No public documentation exists anywhere Trace the 0x8002Bxxx handlers in Ghidra (T7/T8) + PsyQ memcard/write xrefs
6 RESOLVED (T4) — single caller CdReadSectorReadyCB 0x8001A338 @ 0x8001A90C; staging buffer lzss_sectorStagingBuf 0x80079A70 filled by CdGetSector; inline per-sector decode in the read-ready callback Connects CD streaming to decompression —
7 PAC types 6/7 semantics; meaning of PAC header u32 at +0x08 Unknown to every prior source (CUE: "???"; jywjyw: "??") Ghidra analysis of the PAC-header parser in the loader
8 CONFIRMED position-locked (T6b) — overlay loaded verbatim to fixed vaddr 0x80128158 (389,400 B contiguous exact match at the exact address; no relocation) Determines splat segment strategy — simple fixed-address segments Done
9 RESOLVED (T8): the \DEBUG.BIN;1 path is DEAD in retail. LoaderInitFileTable 0x8001971C CdSearchFiles it at boot and stores the result to debugBinPresent 0x800747D0, but that flag is write-only — zero readers (only xref is the boot WRITE @0x8001979C). So nothing acts on it; the debug-loader path is dormant/dead Possible dormant debug functionality Done
10 RESOLVED (Phase 3.5): the demo's scene-select is the SAME architectural feature as the retail L3 debug menu — identical 18-entry gameModeHandlerTable (same order + same idx[10]==idx[15] duplicate), game mode #7 — BUT the slot-7 handler CODE diverged and the demo is unnamed ⇒ no free labels. Shared architecture, not shared labels. Free labels for retail RE Done — see docs/proto-correspondence.md
11 $gp usage: header gp0 = 0 (verified) — is the build −G0? Pins a compiler flag for matching (Phase 6) Check for $gp-relative loads in Ghidra
12 RESOLVED (T6b, change-detection) — 0x80078EB2 = hp_max, 0x80078EB4 = hp_current (HP 146→136 tracked live); same {max,cur} pattern for BP at 0xEB6/0xEB8 Correct struct field names Done
13 Nature of buildIdBytes @ 0x8000BA94 (kernel-area RAM) Used by AP-world for region detect; odd location Inspect live; check if BIOS/kernel structure or game-written
14 Where are LOGOA/LOGOB.STR and .DA paths referenced (absent from CdPathTable)? Completeness of the file-access map String scan for other path tables; CdSearchFile xrefs

Source index

Source What it provided Access notes
Local disc dump Z:\Storage\git\BFM-decomp\Brave Fencer Musashi (USA)\ (Track 1).bin All verified entries (EXE header, LZSS routine bytes, CD-path table) MODE2/2352; user data at raw-sector offset +24; EXE at LBA 24
AiroRom wiki (pleonex) Annotated LZSS decompressor disassembly Fetch raw: https://raw.githubusercontent.com/wiki/pleonex/AiroRom/Brave-fencer-Musashi.md; uses nonstandard register names (r2..r31); region was unattributed — verified US in-session; JP equivalents NOT verified
TCRF Debug menu codes (3 regions), unused music track, localization diffs Use tcrf.net/api.php — direct fetch returned prompt-injection content
AP-world github.com/AegeusEvander/Brave-Fencer-Musashi-AP-World Richest living US+JP RAM map; code patch sites; region deltas Addresses are BizHawk MAIN_RAM offsets (+0x80000000); some entries are code patches, not data — read context before importing. License unstated
LiveSplit autosplitter (Aalaizah gist f9cc6e8c8fba5a2457762f4fab8679c7) Story counter, location IDs, boss HP, chest/armor bytes octoshock offsets: −0x30DF90 +0x80000000
gamehacking.org #88529 (US) / #93476 (JP) via libretro-database GameShark .cht Player stat block, flags, misc gamehacking.org Cloudflare-blocks scripts; cht mirror: raw.githubusercontent.com/libretro/libretro-database/master/cht/Sony%20-%20PlayStation/Brave%20Fencer%20Musashi%20(USA,%20Japan)%20(GameShark).cht
jywjyw bravefencer-hack doc/note.md JP overlay/memory map, LIST.CD-in-RAM behavior, pointer table All addresses JP (SLPS-01490) — re-derive for US
Hidden Palace / archive.org Prototype facts pages fetchable via hiddenpalace.org/w/index.php?title=PAGE&action=raw

Phase 30 S44 — the COMPLETE loader routing table (static-derived; supersedes "runtime-indexed, no static xref")

Provenance (G5): static-derived — read from the EXE bytes (extracted/retail/SLUS_007.26, vram = fileoff + 0x8000F800), the resident payload bytes (MAIN.CD.dir/FILE_010.dir/1.1, fileoff = vram − 0x800CEDF8), the matched loader C (src/800.c, src/resident/resident.c), and the per-overlay wrapper asm — by 3 read-only exploration agents, 2026-08-06. Region: US. The Phase-3 T5 note "entries [1]+ are runtime-indexed (no static xref)" is superseded: the indices ARE static, they live in the resident and in each overlay, not in the EXE. Independently corroborated by two corpus-side methods (h_exact base voting at ~500:1; distinct-jal→prologue alignment voting) — and the resident control reproduces its known 0x800CEDF8 and ends at 0x80128154, four bytes under the overlay slot.

loadDestPtrTable — 0x80072C70 (EXE fileoff 0x63470), 5 × u32

slot value role (byte-proven)
[0] 0x800CEDF8 resident-module slot (boot loaders)
[1] 0x80128158 location-overlay slot
[2] 0x800CAE08 module slot A (small actor modules)
[3] 0x800CCB1C module slot B (small actor modules)
[4] 0x800C7F08 PAC-type-7 fixed destination

Who loads what where (all statically enumerated)

loader index source payloads dest
5 boot loaders (literal &cdFileLocTable[k] at 0x80010CA4 / 0x80010F1C / 0x800112F0 / 0x80011100 / 0x80011144) k ∈ {1,3,8,10,11} MAIN/1,3,8,10 (=resident),11 loadDestPtrTable[0] = 0x800CEDF8
resident func_800D02D0 D_800D3764 = 29 × {u32 cdFileLocIdx; u32 param} MAIN/13…41 (contiguous) [2] = 0x800CAE08
resident func_800D0488 D_800D384C = 6 × {u32,u32} MAIN/42…47 [3] = 0x800CCB1C
resident func_800CF94C (src/resident/resident.c:641) &cdFileLocTable[12] MAIN/12 (an UNCOMPRESSED overlay) [1] = 0x80128158
per-overlay wrapper func_80128CFC (every overlay) per-overlay IDXTAB (s16, −1-terminated, 37 entries, same list fleet-wide) + *DESTPTR (per-overlay initialized word) resources incl. the SC0x sets per-overlay dest (e.g. ov_SC01_000: IDXTAB 0x8017EEC8, *0x801A3234 = 0x801A58E8)
SC07 endgame pair header-derived (id word + fn-ptr table; first table target − first prologue fileoff) SC07/3 (code@0xFC), SC07/4 (code@0x158) 0x801A00D8 (own slot, overlaps the overlay tail — disc-7 layout)

The arithmetic

  • Global cdFileLocTable index = gbase[cd] + subfile; gbase = MAIN:0 SC01:49 SC02:135 SC03:178 SC04:318 SC05:349 SC06:379 SC07:418 (LIST.CD counts 49/86/43/140/31/30/39/29, byte-verified; LIST.CD carries LBA + length only, never load addresses).
  • Slot adjacency proof: 0x800CAE08 + 7,444 (max slot-A payload, MAIN/34) = 0x800CCB1C; 0x800CCB1C + 8,920 (max slot-B, MAIN/44) = 0x800CEDF4 → resident at 0x800CEDF8. The three regions are back-to-back, each sized to its largest member. MAIN/46's self-calls (base+0x724/0x978/0xAB0) confirm slot B.
  • Module-ID law: payload word0 is a global module id (dense 0x13…0x73 across all discs; the resident is 0x36). 75/78 unclaimed payloads carry it; only the 3 raw uncompressed overlays (MAIN/12, SC02/37, SC03/107) start directly with code. MAIN/9 and MAIN/39 both carry id 0x2D (unresolved duplicate). MAIN/0 ≡ MAIN/1 byte-identical (one module stored twice).
  • PAC-type law (extends formats.md): type 1 = uncompressed code/module payload; type 4 = the same class LZSS-compressed. The PAC header's bytes 0x10–0x7FF are never read by the loader (CdGetSector(lzss_sectorStagingBuf, 4) reads 4 words) — no address lives in the payload.

Statically UNRESOLVED (parked for L3 — runtime confirm, R34)

The 28 SC0x script modules (SC03/73-79, SC03/132-138, SC04/24-30, SC05/23-29 = 7 modules × 4 per-disc builds), SC02/9, MAIN/7 (raw file, not PAC), MAIN/9: dest comes through the resourceIdMap / StreamLoadStateMachine descriptor path (D_80068B60[(loadParam−0x100)*0x10]) or per-overlay DESTPTR values — per-disc, not EXE-static. Their jal-vote bases are LOW-CONFIDENCE (3–14 aligned jals, calls almost entirely outward) and are NOT recorded as addresses here.

Phase 30 S45 — the script-module slots + the L3 debug-menu tour (live-verified)

Provenance (G5): live-verified — PCSX-Redux RAM (R11 web API), 2026-08-07 tour driven by Drew via the retail debug menu, watcher logs in .run/s45/l3_tour.jsonl + hit dumps. Region: US.

The debug menu, operationally (extends §5)

  • Entry that actually works: force gameMode (0x800B99DE, u16) = 7 with REPEATED writes (the game rewrites it every frame — a single poke never latches; the GameShark constant-write semantics are required). Web-API gotcha: offset must be DECIMAL — hex offsets are silently parsed as 0 (= writes land at 0x80000000, kernel space).
  • Mode 7 → DebugMenuHandler boot-loads MAIN/11 = md_MAIN_011 (DISELECT) to 0x800CEDF8 — byte-proven live: 24,236/24,240 B identical in RAM. The menu's AREA/SCENE list: docs/debug-menu-list.txt (Drew's transcription).
  • Observed gameMode values: 5 (transition) · 6 (title/main menu) · 7 (debug) · 8 (post-menu) · 10 (scene load) · 11 (field/load-screen) · 15/16 (attract rotation).

The four script-module slots (all live byte-verified, contiguous exact prefixes)

slot payloads meaning
0x801EF468 SC03/73–79 town-interior scripts, chapter-2 period (id 0x44–0x4A)
0x801E25E8 SC03/132–138 town-interior scripts, chapter-3/vampire period (0x4B–0x51)
0x801E7B28 SC04/24–30 chapter-4 period (0x52–0x58)
0x801ED988 SC05/23–29 chapter-5 period (0x59–0x5F)

The routing law: the debug menu's AREA selects the chapter (gbase[cd]); each CITY interior scene (4 FIGURE / 5 PAN / 6 ITEM / 7 INN / 8 RECYCLE / 9 KYOUKAI / 10 SAKABA) streams its own script module — member k of the set ↔ interior k, one module resident per slot at a time (the slot-A/B ping-pong pattern at chapter scale). ACTION scenes, dialogue, quest flags, area transitions: none of them stream script modules (byte-checked negatives, ~15 probes).

MAIN/3 = the main-menu module (DISCOVERED live, S45)

MAIN.CD/FILE_003.dir/1.1 (121,884 B, id 0x39 — completing the boot cluster 0x36–0x3B) runs the main menu at 0x800CEDF8: live byte-proof = 42,632-B contiguous exact prefix. It was mis-bucketed classified-data by BOTH audit oracles (low jr-density module class). Onboarded as md_MAIN_003. Note: 0x800D3200 is a one-word data sentinel (0x00FFFFFF) inside .text; the function at 0x800D3204 materializes that address and reads/writes it (pinned in config/symbols.md_MAIN_003.txt — splat's auto-boundary there is extract-order-sensitive).

Still parked (6 payloads — every cheap state byte-checked negative)

MAIN/7, MAIN/9 (not in: boot, title, attract ×4+ segments, load screen, main menu, OPENING, ENDING-1/ZEN, game-over/continue) · SC02/9 (not in: STEAM-WOOD ×3, GANRYUU, 2ST-BOSS, GEKIRYU) · SC03/53/54/56 (ids 0x40/0x41/0x43; not in: ISEKI, PLAZUMA, AREADEMO2-3, VAMBI-KYOUKAI; SC03/55 = their DATA companion, id-word 0x42 but zero prologues/returns — confirmed data). MAIN/9 shares id 0x2D with slot-A module MAIN/39 (an alternate build). Resolution tier: the CD-read tracer (log cdFileLocTable index per read), not scene-guessing.

S45 part 3 — SC02/9 CAPTURED + retro-verified; the emulator's role closes

  • SC02/9 = the Steam Knight (1ST-BOSS) event module — loads at 0x801E4C60 when currentLocationId == 0x300E (the matched gate func_8012832C, case 0x300E → func_80128998 → the streaming API with &cdFileLocTable[144]). Double-proven: live capture 2026-08-07 AND the Phase-3 dumps/ram_castle.bin (2026-06-14) holds it at the SAME address with the SAME 6,764-B exact prefix (R10 multi-datapoint). bossHp_SteamKnight 0x801E4398 sits inside this module's image. Onboarded as md_SC02_009.
  • Negatives (byte-checked): pause/config menu, memory-box prompt (CENTER-PATH 0x3034), new-game intro, high/low bar game (runs from SC03/79), Minku midnight spawn (slot-A actor 0x15 = md_MAIN_015, candidate naming) — none load any parked payload.
  • The remaining 5 (MAIN/7, MAIN/9, SC03/53/54/56) are STATIC-RE targets, not emulator targets: MAIN/7's loader (fn @0x80161E08 in ov_SC03_126/ov_SC04_021/ov_SC05_019) gates on a global 0x800C3054 ∈ {0x3012, 0x3079, 0x3096} (writer untraced — not written by the EXE); MAIN/9 has NO literal table reference anywhere (table-indexed loader). Homework: trace 0x800C3054's writer, identify the 3 host overlays' locations, decode the SC03/53 loader in ov_SC03_104 (@0x80161FBC), find MAIN/9's indexed loader.

S45 part 4 — CORRECTION (R14/R35): the "MAIN/7 & SC03-trio loader" leads were scanner artifacts

The §S45-part-3 claims that MAIN/7's loader is fn @0x80161E08 (ov_SC03_126/SC04_021/SC05_019, "gate 0x800C3054 ∈ {0x3012,0x3079,0x3096}") and that SC03/53's loader is @0x80161FBC in ov_SC03_104 are REFUTED. The quick hi/lo scanner paired lui/lo16 operands without tracking base registers; a proper register-tracked rescan (rabbitizer-checked disasm) shows:

  • fn 0x80161E08 compares currentLocationId (0x800B9A08) against location ids {0x1A-guard, 0x3012, 0x3054, 0x3079, 0x3096} — the "variable 0x800C3054" never existed (0x3054 is a compared CONSTANT), and its stores target 0x8018E868, not the loc-table.
  • The only register-verified literal loc-table reference in the entire fleet is SC02/9's (the universal wrapper helper func_80128998, solved). Standing truth: MAIN/7, MAIN/9, SC03/53/54/56 have ZERO literal loc-table references anywhere — all five load via table-INDEXED paths (the resourceIdMap / D_80068B60 descriptor route + per-overlay IDXTAB/DESTPTR, per the original §S44 finding). Next-session homework: hunt the DESCRIPTOR DATA (records carrying global indices 7, 9, 231, 232, 234) and decode ResourceGetCdLoc/StreamLoadStateMachine's index math — a structured-data hunt, still fully static. Tooling lesson (cookbook §155): a hi/lo literal scanner MUST track base registers; window-paired lui/lo16 produces convincing phantom cross-references.

S45 part 5 — the resourceIdMap branch is REFUTED for all five; IDXTAB is per-overlay (2026-08-07)

Provenance (G5): static-derived — decoded from the EXE bytes (extracted/retail/SLUS_007.26, vram = fileoff + 0x8000F800) and the onboarded payloads, using the index math taken from our OWN matched C, not from a guess. Region: US. Scripts: .run/s45p5/{decode_resmap,decode_idxtab,scan_idxtabs}.py (regenerable scratch).

The index math, quoted from matched/drafted C (src/800.c) — this is the oracle:

ResourceGetCdLoc(resId):                        /* MATCHED, byte-exact */
    idx = *(s16*)(resourceIdMap + resId*6);     /* global cdFileLocTable index */
    return *(s32*)(cdFileLocTable + idx*8);

ResourceLoadStateMachine:                       /* NON_MATCHING draft, logically faithful */
    fileIdx    = *(s16*)(resourceIdMap + id*6 + 0);   /* <0 => non-CD path func_80036D58 */
    streamId   = *(s16*)(resourceIdMap + id*6 + 2);   /* -> StreamLoadStateMachine param_1 */
    postProcId = *(s16*)(resourceIdMap + id*6 + 4);   /* -> func_8002D4C8 */

StreamLoadStateMachine(param_1, loc, n):        /* NON_MATCHING draft */
    sVar1 = *(s16*)(D_80068B60 + (param_1 - 0x100) * 0x10);

resourceIdMap @ 0x80063138 (fileoff 0x53938), decoded: exactly 162 6-byte records, ending 0x80063504; 2 records carry the documented fileIdx < 0 non-CD sentinel; streamIds are

= 0x100 as the descriptor math requires. The decode is self-consistent with the matched C in every field, so the table model is confirmed, not assumed.

  • Coverage: 98 distinct global indices of 447. Full dump: .run/s45p5/resourceIdMap.tsv.
  • FINDING (the point of the exercise): none of the five parked payloads appears. MAIN/7 (gi 7), MAIN/9 (gi 9), SC03/53 (231), SC03/54 (232), SC03/56 (234) have no resourceIdMap entry, so they cannot reach ResourceGetCdLoc / StreamLoadStateMachine / the D_80068B60 descriptor at all. The §S44 "resourceIdMap / StreamLoadStateMachine descriptor path" branch of the parked-dest disjunction is therefore REFUTED for all five. The surviving branch is the per-overlay IDXTAB/DESTPTR route (or a route not yet enumerated).
  • The module payloads are also absent from resourceIdMap (checked gi 10/11/13/14/42/47) — consistent with §S44: modules load via the resident's D_800D3764/D_800D384C tables and the boot loaders, never the resource system. resourceIdMap is a distinct, per-location data-resource route (its 98 entries are SC0x data sets).

loadDestPtrTable @ 0x80072C70 re-derived independently (R34 second signal): the five u32s read 0x800CEDF8 / 0x80128158 / 0x800CAE08 / 0x800CCB1C / 0x800C7F08 — reproducing the §S44 table exactly, including slot [4] = the PAC-type-7 fixed destination. Four of the five are the addresses S45 byte-proved by onboarding, so this table is corroborated from two independent directions.

CORRECTION (R14) to §S44's IDXTAB row: the claim "per-overlay IDXTAB (s16, -1-terminated, 37 entries, same list fleet-wide)" is wrong on the "same list fleet-wide" part. The 37-entry list at 0x8017EEC8 is genuine for ov_SC01_000 only (decoded: 240,241,242,243, 167,168, 147,148,149, 153,154,155, 306..309, 102..122 — a coherent per-location resource set). Read at the same vaddr across the fleet, 1 of 141 overlays matches and 140 hold unrelated bytes: the IDXTAB is per-overlay data at a per-overlay address. What is fleet-wide is the mechanism (func_80128CFC + a per-overlay IDXTAB/DESTPTR), not the address or the contents.

NEGATIVE TOOLING RESULT (recorded so it is not repeated): a shape-only fleet scan for "-1-terminated s16 runs of valid global indices" cannot discriminate and its hits are not evidence. It passes its own R32 coverage assertion (it re-finds ov_SC01_000's table at 0x8017EEC8, 37 entries) yet still returns 664 "tables" across 212 payloads, whose parked-index hits are transparently (offset, count) pair data — e.g. [44, 2, 48, 7, 62, 6, 74, 7, ...] "contains 7". Small global indices (7, 9) are indistinguishable from ordinary small data by shape alone. This is the §155 phantom failure mode in a new guise: register-blind -> shape-blind. Do not re-run this scan expecting an answer.

The correctly-scoped next instrument: a register-tracked decode of the per-overlay wrapper func_80128CFC (per §155: record lui rt -> hi, match only ops whose base is that rt, invalidate on clobber) to extract, per overlay, the actual IDXTAB pointer and the *DESTPTR word — then test whether any overlay's real IDXTAB references gi 7 / 9 / 231 / 232 / 234, and read that overlay's DESTPTR for the load address. Structure-derived, not shape-guessed.

S45 p5 addendum — WHY the five resist static derivation (three refuted oracles + the region finding)

MAIN/7 and MAIN/9 are IDENTIFIED (semantic, not address): both payloads carry leftover dev-machine path strings immediately after their id word — C:\TIMPACK\OPDEMO0.PAT and C:\TIMPACK\OPDEMO1.PAT (MAIN/7 id 0x3A, MAIN/9 id 0x2D). These are the opening / attract-demo playback modules. (C:\TIMPACK\ is the same build-path family found in retail AND the sep8 proto in Phase 3.5.) S45 p2 recorded "OPENING" as byte-checked negative, so the live target is the attract-mode demo (idle at the title until the game demos itself), which is a different state from the OPENING cutscene. MAIN/9 additionally shares id 0x2D with slot-A module MAIN/39 (§S44's "alternate build" note) — consistent with a demo-specific rebuild of an actor module.

Three payload-side base oracles were built and ALL REFUTED by their own controls (recorded so they are not rebuilt; scripts in .run/s45p5/):

oracle idea controls why it fails
derive_base.py invert the §154 TLO law: base = min(table) − (4+4N) 0/4 assumes code starts right after the fn-ptr table; MAIN/34 has a 0x208-byte header gap between table end and code
vote_base.py vote jal targets + table ptrs against addiu $sp,-N prologues 4/12 modules call almost entirely OUTWARD (§S44's low-confidence note, now explained), and MIPS leaf functions have no prologue, so the anchor set is both noisy and incomplete
vote_base2.py self-consistency: internal jal targets must hit listed functions 0/4 there are essentially no internal jals (self-jals 0/N on every control) — module functions are reached indirectly through the header pointer table (jalr), never by direct call

The third result is the structural one: a module's own bytes do not encode its base, because it makes no self-referential direct calls. Only the header pointer table's value range constrains anything, and only for the payloads that have a table.

The one real address constraint obtained — SC03/54: its header holds 19 absolute pointers spanning 0x801EF718..0x801EFEE8. With size 0x201C and a 0x50-byte table, the base is confined to [0x801EDED0 .. 0x801EF6C8] (6,136-byte window); the module occupies roughly 0x801EDED0..0x801EFEE8.

That window lies INSIDE SC02/9's occupied span (0x801E4C60 + 70,784 = 0x801F60E0). SC02/9 and the SC03 trio (ids 0x40/0x41/0x43, adjacent to SC02/9's 0x3E) are therefore mutually-exclusive event modules sharing one region around 0x801Exxxx — but at DIFFERENT base addresses (SC03/54's base cannot be 0x801E4C60; it is excluded by the window).

Conclusion (this is why they are parked): event-module destinations in this region are per-scene / runtime-determined, not a fixed slot from a static table — which is exactly why no static route exists, why the resourceIdMap branch is empty for them, and why the emulator was what resolved SC02/9. The CD-read tracer (log the cdFileLocTable index per read during play) remains the correct instrument; the OPDEMO identification above makes MAIN/7 and MAIN/9 a targeted capture (attract demo) rather than a search.

S45 p5 — the exclusion proof needs a CONSUMER-side instrument (4th value-scan refuted)

A fourth attempt (.run/s45p5/exclusion_proof.py) tried the proven §S44 table shape ({u32 cdFileLocIdx; u32 param} pairs, as in D_800D3764/D_800D384C). Its R32 control FAILED — it did not re-find either known table, so its output is void (R35).

Standing verdict: no value- or shape-based scan can establish the exclusion. Small global indices (7, 9) are indistinguishable from ordinary small integers; 4/4 such scans have now been refuted by their own controls or shown non-discriminating (§155a). Do not attempt a 5th.

The only sound instrument is CONSUMER-side: enumerate every code site that references cdFileLocTable (0x800AE830) register-tracked (§155) across all 213 binaries — a bounded set, since S45 p4 already showed literal references are almost nonexistent — then resolve each site's index SOURCE and collect the full set of reachable global indices. gi 7/9/231/232/234 absent from that set is the proof; present is the address. This is real work, not a quick scan.

Partial result worth keeping: the large, discriminating indices 231 (SC03/53) and 234 (SC03/56) appear in no pair-shaped table anywhere in the fleet — weak evidence (the instrument was unsound) but directionally consistent with the dead-code reading.

S45 p6 — the parked SC03 trio: a CHAPTER-GATED town-interior script hypothesis (recorded BEFORE test)

Written before the runtime test so the prediction stands on its own (P9). Two independent lines converge on the same answer for SC03/53, SC03/54, SC03/56:

1. The address. SC03/54's base window, derived from its 19 absolute header pointers (0x801EF718..0x801EFEE8, §S45 p5 addendum), is [0x801EDED0 .. 0x801EF6C8]. Of the four byte-verified script-module slots (§S45), exactly one falls inside that window:

slot payloads period in SC03/54's window?
0x801EF468 SC03/73–79 chapter 2 YES
0x801E25E8 SC03/132–138 chapter 3 / vampire no
0x801E7B28 SC04/24–30 chapter 4 no
0x801ED988 SC05/23–29 chapter 5 no

2. The module ids (§S44 word0 law) place the trio immediately BEFORE the chapter-2 block:

0x40 SC03/53 · 0x41 SC03/54 · 0x42 SC03/55 (DATA companion) · 0x43 SC03/56
0x44–0x4A  SC03/73–79     <- chapter 2
0x4B–0x51  SC03/132–138   <- chapter 3

Hypothesis: the trio are town-interior scripts for an earlier game period (chapter 1) — the same class as the 28 script modules captured in S45 p2, sharing the 0x801EF468 slot, selected by CHAPTER/day state rather than by location.

Why every previous sweep missed them: S45 checked the right scenes (ISEKI, PLAZUMA, AREADEMO2-3, VAMBI-KYOUKAI) at the wrong game period. At a late-game save the loader serves the chapter-2/3 variants into that slot and the chapter-1 set is never requested. Location was never the discriminating variable.

FALSIFIABLE PREDICTION: in an early-game town interior, SC03/53 / /54 / /56 load at 0x801EF468. If they do not appear there, the slot coincidence is chance and this dies; the follow-up question is then which state variable gates them, not which room to visit.

S45 p6 — RESULT: the runtime CD-load tracer, and what it settled (2026-08-07)

The instrument (new, tools/cdtrace.py). Three static oracles failed to derive the parked payloads' load addresses this session. The runtime answer turned out to need no breakpoints, no Lua (so no pcsx.lua wedge hazard) and no GDB stub — the loader keeps its whole request in RAM, so both halves are already in the 2 MB dump we could already fetch:

cdReq_curSector 0x800AE708   the sector being read   -> file index via cdFileLocTable
cdReq_dest      0x800AE72C   where it is written     -> the load address
cdFileLocTable  0x800AE830   8B/entry {CdlLOC; size}, GLOBAL index

CdReadRequest(int *cdlFile, void *dest, …) — our own MATCHED C — is why this works: cdlFile points INTO cdFileLocTable, so (ptr − 0x800AE830)/8 is the global index and dest is the destination. Both are mirrored in the control block.

VALIDATED before use (R35), then confirmed 7× against independently byte-proved ground truth:

observed live confirms
MAIN/1, MAIN/3, MAIN/10 → 0x800CEDF8 §S44 loadDestPtrTable[0]; MAIN/10 = the Phase-3 resident; MAIN/3 = S45-p2's md_MAIN_003
MAIN/42, SC03/0 → 0x800CCB1C §S44 [3] slot B — and SC03/0 shows the module slots are NOT MAIN-only
MAIN/12 → 0x80128158 §S44's resident func_800CF94C row, exactly
12 overlays → 0x80128158 §S44 [1], the location slot
LIST.CD (lba 227) → 0x80180000 the LoaderInitFileTable bootstrap read in matched C (src/800.c)

This is the R34 second oracle for the whole §S44 routing table — until now static-only.

THE FINDING — the script-module slot 0x801EF468 is live and general. Observed twice: SC03/76 → 0x801EF468 and SC03/34 → 0x801EF468. SC03/34 is NOT in the SC03/73–79 block, so S45's label ("SC03/73–79, chapter-2 period") describes one tenant, not the slot: it is a general per-scene script-module slot that different SC03 scripts stream through.

Status of the pre-registered hypothesis (recorded above, before the test):

  • ✅ Slot CONFIRMED — 0x801EF468 is real, live, and the class the trio belongs to (it lies inside SC03/54's independently-derived base window [0x801EDED0..0x801EF6C8]).
  • ❌ "Chapter-gated" WEAKENED — scripts swap per SCENE, not per chapter. The id-block adjacency (0x40/0x41/0x43 preceding chapter-2's 0x44–0x4A) remains suggestive but is not the selector.
  • ⬜ Trio: 0 sightings across 38 load events / 2 saves / multiple SC03 scenes.

THE EFFICIENT NEXT STEP (static, no emulator): we now have a concrete anchor the earlier hunts lacked — the constant 0x801EF468. Find the code that loads into it (register-tracked, §155) and decode its SELECTOR — near-certainly a scene→script-index table. That yields the answer for all three at once, instead of sweeping rooms. This is the correctly-scoped successor to the four refuted value-scans.

Also captured: the full attract-cycle load map (.run/attract_loadmap.jsonl) — MAIN/7 and MAIN/9 absent across a complete 304-second cycle (see the OPDEMO section above).

Carried offer (Drew): a fresh full playthrough with the tracer running would map every load in the game in one pass — resolving the trio, MAIN/7/9, and validating the entire routing table. High value, zero marginal effort beyond playing.

S45 p6 — SOLVED (static): the SC03 trio are ov_SC03_001's script modules

The static decode succeeded where four value-scans and three payload-side oracles failed, because the live tracer supplied the missing anchor: the slot address 0x801EF468.

The chain, every link register-verified or byte-observed:

link evidence
ov_SC03_001 IDXTAB @0x8018D7BC — 5 × s16, -1 terminated: 224, 231, 232, 234, 233 direct read; the trio + its DATA companion (233 = SC03/55) in ONE table
func_80128CFC (the §S44 per-overlay wrapper) register-tracked: addiu→0x800AE830 (cdFileLocTable base), lw [0x800AE834] (size), lw [0x800AE830] (loc)
*DESTPTR @0x801EBC68 = 0x801EF468 the ONLY occurrence of that word in the fleet; read 8× by code, twice from inside func_80128CFC (0x80128CD0, 0x80128D34)
the slot is real and live tracer: SC03/76 → 0x801EF468, SC03/34 → 0x801EF468 (2026-08-07)
the slot lies inside SC03/54's own derived base window [0x801EDED0..0x801EF6C8] from its 19 header pointers (§S45 p5 addendum) — independent

VERDICT: SC03/53, SC03/54, SC03/56 are LIVE script modules owned by ov_SC03_001, loaded via func_80128CFC into the shared script-module slot 0x801EF468. Not dead code; not boss-gated; not chapter-gated (that framing is retired — see p6 above, scripts swap PER SCENE). They never appeared in any sweep because every SC03 scene we visited runs a DIFFERENT overlay (ov_SC03_124/125/051); only the scene(s) backed by ov_SC03_001 request them.

Why every earlier hunt failed, precisely: the trio have no literal cdFileLocTable reference (§S45 p4) and no resourceIdMap entry (§S45 p5) because the index never appears in CODE at all — it lives in a per-overlay DATA table, and the destination likewise (*DESTPTR). Both are per-overlay data, invisible to any fleet-wide code scan. That is the structural reason, and it also explains MAIN/7 / MAIN/9 (still open — their owning binary is not yet identified).

NOT YET PROVED (do not overstate): the trio's exact LOAD BASE. 0x801EF468 is the slot; the three payloads differ in size (10,240 / 12,288 / 6,144 raw) so their bases within it are not established, and none was observed loading. The byte-gate is the arbiter: onboard each at 0x801EF468 and let the first build decide (the §S44 module-class recipe, SETUP §6.7).

METHOD NOTE (generalizes): a runtime observation supplied ONE constant (the slot), and that constant made a previously-impossible static decode trivial. Neither alone was enough: four static scans failed without it, and the emulator alone never caught the trio loading. Pair a runtime oracle with static RE rather than choosing between them.

S46 — the master IDXTAB/DESTPTR map: the route decoded fleet-wide, and TWO corrections (2026-08-07/08)

Full write-up + method: docs/idxtab-map.md. Tool: tools/idxtab_map.py (controls-gated).

The blocker was our instrument, again (R35). The S45 plan — "find -1-terminated s16 runs, then require a register-verified reference to the run's address" — returns NOTHING as specified: 0x8018D7BC has zero register-verified references and is stored as a data word nowhere in the fleet. The tables are read by gcc's indexed global-array form, which find_addr_refs was blind to:

lui  $at, 0x8019 ; addu $at, $at, $a0 ; lh $v0, -0x2844($at)   -> 0x8018D7BC

The address is split across the lui and the load, with the index added between, so killing the lui register at the addu (it is written) threw away the only link. Carrying the hi half through the add — still strictly register-tracked, never window-paired (§155) — recovers both byte-proved controls (ov_SC01_000 0x8017EEC8 / 37 entries, ov_SC03_001 0x8018D7BC / 5 entries) from the images alone.

Fleet result: 213 binaries → 146 with a referenced index table (320 tables, after filtering 106 zero-runs), 141 with a DESTPTR, 67 distinct payloads on this route. The two dominant tables are fleet-wide CONSTANTS (identical in all 141 overlays): a 5-entry list SC03/46, SC03/53, SC03/54, SC03/56, SC03/55 and a 37-entry list (SC03/62-65, SC02/32-33, SC02/12-14, SC02/18-20, SC03/128-131, SC01/53-73). The per-binary variable is the DESTPTR — 141 binaries, 134 distinct destinations. Shape: constant list + runtime index + per-binary destination.

CORRECTION 1 (R14) — §S45 p6's ownership claim is refuted. The trio are NOT "owned by ov_SC03_001": the 5-entry table containing SC03/53/54/56 is present with identical content in all 141 overlays. Everything §S45 p6 byte-observed still stands (they are live script modules reached via func_80128CFC; ov_SC03_001's destination is 0x801EF468) — only "only ov_SC03_001 requests them" is wrong.

CORRECTION 2 (P9) — this route CANNOT settle MAIN/7 + MAIN/9. They are absent from all 320 tables, but so are MAIN/13, /20, /34, /42, /44, which are byte-proved to load (the onboarding controls in .run/s45p5/derive_base.py). MAIN payloads referenced by any IDXTAB: 0 1 2 3 4 5 6 8 16 17 24 32 33 — 13 of ~49. So absence here means "not loaded by this route", nothing more; the descriptor (StreamLoadStateMachine) and resourceIdMap paths are separate. MAIN/7 and MAIN/9 remain open. The S45 checkpoint's hope that this would be "the strongest dead-code evidence obtainable" does not survive its own control — recorded so a later session does not re-derive the false conclusion.