Files
BFM-decomp/docs/memory-map.md
T
Drew T 7e9394f691 fix(phase-30 S45 p4): R14 correction — the MAIN/7/SC03-trio 'loader fn' leads were scanner phantoms
- the quick hi/lo sweep paired lui/lo16 WITHOUT tracking base registers -> phantom refs
  (0x800AE868 read where the true target was 0x8018E868); register-tracked rescan: the ONLY
  literal loc-table ref fleet-wide is SC02/9's (solved)
- standing truth: MAIN/7, MAIN/9, SC03/53/54/56 all load via table-INDEXED paths; homework
  respecified (descriptor-data hunt + ResourceGetCdLoc/StreamLoad index math)
- fn 0x80161E08's real gate: currentLocationId vs {0x3012,0x3054,0x3079,0x3096} — the
  'variable 0x800C3054' never existed; cookbook §155 (track the register)
2026-08-07 15:13:50 -06:00

58 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.