Files
BFM-decomp/phase-ends/PhaseEnd_Phase3.md
T
Drew T bd5e21b366 feat(phase-3): file-loader & overlay-map RE + PCSX-Redux runtime oracle
- RE'd the full file-loader/overlay subsystem in Ghidra via MCP: CdReadRequest ->
  CdReadStateMachine (hand-rolled CdControl SeekL/ReadN reader, NOT PsyQ CdRead);
  LIST.CD RAM cache cdFileLocTable + ResourceGetCdLoc/resourceIdMap lookup; LZSS
  staging buffer 0x80079A70; loadDestPtrTable; ~50 symbols named (T1-T5,T8)
- T6b MILESTONE: resident blob @0x800CEDF8 + location overlay @0x80128158 proven
  byte-identical (sha1-equal contiguous prefixes) vs live PCSX-Redux RAM; blobs
  imported into Ghidra (ImportOverlay.java); overlay-map table -> memory-map.md §4.3
- PCSX-Redux runtime oracle: Windows-native build, WSL reads RAM via the web API
  at 172.17.208.1:8081; ledger #4 resolved
- live-verified player/time state: gold(/10) HP BP day hour tiredness boss-HP;
  save-header template (Q#5) + debug-menu dispatch (Q#9: DEBUG.BIN path is dead)
- overlay map: 13 area-states across SC01/02/03, 2 duplicate pairs, location x
  chapter keying, free-roam vs menu-hub granularity
- resolves Open Q#1/#3/#4/#5/#6/#8/#9/#12; corrects SLUS-90029 (FF8 demo, not BFM)
- 2 US prototypes acquired + CRC-verified (Aug-31 1369DE07, Sep-8 5C24728E)
- rules R9 (verify persistence), R10 (>=3-datapoint live verification),
  R11 (runtime oracle Windows-native, bridged to WSL)
- bumps project version 1.2.0 -> 1.3.0
2026-06-14 02:04:28 -06:00

160 lines
14 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# PhaseEnd — Phase 3: File-loader & overlay-map RE (+ PCSX-Redux harness)
**Date:** 2026-06-14 · **Project Version:** 1.3.0 · **Phase Status:** Complete
## Build Log
**Files created/changed and complete — do not recreate:**
- `tools/ghidra_mcp_start.sh` / `ghidra_mcp_stop.sh` / `ghidra_mcp_verify.sh` — oracle lifecycle: start
detached headless MCP server / clean save+stop (the ONLY way headless MCP writes persist) / read-only
R9 verify. Used every RE session.
- `tools/ghidra_scripts/BfmMcpServer.java` — headless MCP server + commit-on-clean-shutdown; run by `start.sh`.
- `tools/ghidra_scripts/GetSymbolAt.java` — read-only symbol probe consumed by `verify.sh` (R9).
- `tools/ghidra_scripts/ImportOverlay.java` — headless: loads the proven resident blob (→0x800CEDF8) +
one location overlay (→0x80128158) into the program (`RAM` block, convertToInitialized+setBytes);
idempotent. (T7)
- `tools/ghidra_scripts/VerifyOverlay.java` — read-only R9 check that the imported blob bytes persisted. (T7)
- `tools/ghidra_scripts/ExportSymbols.java` + **`config/symbols.us.txt`** — headless export of all 967
USER_DEFINED symbols (PsyQ-signature names + manual RE) to committed text — the durable, diffable,
re-importable record of the RE work (the `.rep` is gitignored); Phase-5 splat input. (phase-close)
- **`dumps/INDEX.md`** + `dumps/*.bin` — the 28-dump live RAM corpus, **committed** (H1 relaxed while
private; static, not regenerable, off-machine backup); INDEX maps each dump → state. (phase-close)
- `.gitignore` — added `/tools/pcsx-redux/` (AppImage), `/.run/` (runtime scratch); dumps committed.
- `.claude/settings.local.json` — SessionStart hook that auto-starts the oracle. (T0)
- **`/tmp` → project-local migration:** `ghidra_mcp_start.sh`/`stop.sh` + `BfmMcpServer.java` now use
`~/bfm-decomp/.run/{ghidra-mcp.log,mcp-stop.req}` (no `/tmp`); full start/stop/save/R9 cycle re-certified. (R12)
- `docs/memory-map.md` — **the phase's main written deliverable.** Added: the T1 "imported into Ghidra"
block; the T2 (CdRead chain) / T3 (LIST.CD cache+lookup) / T4 (LZSS staging) / T5 (US load addresses) /
T6b (MILESTONE proof) blocks; **§4.3 overlay-map table (13 area-states + the loader model)**; §3.4
player-state promoted to `verified` (gold/HP/BP/day/hour/tiredness-raw) + refutations; §2.3
`saveHeaderTemplate`; §5 debug-menu dispatch; Open Questions **#1,#3,#4,#5,#6,#8,#9,#12 resolved**;
SLUS-90029 corrected (FF8 demo, not BFM).
- `docs/SETUP.md` — §3 PCSX-Redux as-built (Windows-native + WSL web-API bridge); ledger #4 RESOLVED
(web server :8081, dump URL).
- `phase-ends/CURRENT_PHASE.md` — **absorbed into this file and deleted** (P8).
- `phase-ends/PhaseEnd_Phase3.md` — this file.
**Local artifacts (gitignored / outside repo — present on this machine, do NOT recreate; a fresh clone redoes them):**
- `~/bfm-decomp/ghidra/bfm.{gpr,rep}` — the saved Ghidra project. `SLUS_007.26` now carries the T1 seeds
**plus ~50 Phase-3 symbols**: loader cluster (`LoaderInitFileTable`, `CdReadRequest`,
`CdReadStateMachine`, `CdReadSectorReadyCB`, `CdQueueBusy`, `cdReq_*` control block), resource loader
(`ResourceLoadStateMachine`, `ResourceGetCdLoc`, `resourceIdMap`, `resLoad_*`), `cdFileLocTable`,
`listCdBuffer`, `lzss_sectorStagingBuf`, `StreamLoadStateMachine`, `loadDestPtrTable`,
`GameModeDispatch`+`gameModeHandlerTable`, `DebugMenuHandler`, `SaveLoadRoutine`, `saveHeaderTemplate`,
`debugBinPresent`, player-state fields — **plus 2 imported memory blocks** (resident blob @0x800CEDF8,
tut_forest overlay @0x80128158). R9-verified; DB saved (clean shutdown).
- `tools/pcsx-redux/PCSX-Redux-HEAD-x86_64.AppImage` — Linux AppImage (downloaded; **NOT used** — WSLg
display + WSL controller failed; the Windows-native build is the actual oracle). Gitignored.
- `disks/` — 2 CRC-verified US prototypes (**Aug-31-1998 "Musashi Demo" 1369DE07**, **Sep-8-1998 "Musashi
Master" 5C24728E**) + a "Squaresoft on PlayStation Vol.2 (FF8 Demo)" disc. Gitignored.
**Tools/packages installed:**
- **PCSX-Redux** (runtime oracle). The Linux AppImage was fetched to `tools/pcsx-redux/` but is unused;
the **Windows-native build** (`distrib.app/.../dev-win-x64`) is what runs — web server on :8081, reached
from WSL at **172.17.208.1:8081** (NAT gateway). Ships OpenBIOS (no BIOS dump needed).
- **usbipd-win** (Windows, installed by Drew) — attempted DualSense→WSL passthrough; abandoned (WSL2 kernel
has no `CONFIG_HID_PLAYSTATION`/`joydev`) in favour of the Windows-native emulator.
**Verification results (literal):**
- **MILESTONE byte-proof (live PCSX-Redux RAM):** resident `MAIN.CD/FILE_010/1.1` @0x800CEDF8 = **18,788-byte
contiguous exact prefix, sha1 `dbc55765593d3c157965a22af5808990714dbae9`** (RAM slice == extracted, EQUAL);
overlay `SC01/FILE_077/0.4.dec` @0x80128158 = **389,400-byte contiguous exact prefix, sha1
`8e40afab29200f00752573bfc28f9a8676000f85`** EQUAL. EXE resident: RAM[0x10000:] == extracted
`SLUS_007.26`[0x800:] (8192/8192).
- **R9 persistence verifies — all PASS:** T1 (`CdPathTable`, db.5); checkpoint#1 (`StreamLoadStateMachine`);
#2 (`loadDestPtrTable`); T7 (`VerifyOverlay` — both blobs init=true, bytes correct); #3 (`DebugMenuHandler`).
- PCSX-Redux web API `GET :8081/api/v1/cpu/ram/raw` → exactly **2,097,152 B**.
- **Overlay map:** 13 area-states identified by RAM-vs-extracted byte-match across **SC01/SC02/SC03**;
2 duplicate-overlay pairs (`SC02/000≡003`, `SC01/005≡006`).
- **Player/time state live-verified** (multi-datapoint / controlled diff): `gold` 0x80078E8C (÷10),
`hp_max/cur` 0x80078EB2/EB4, `bp_max/cur` 0x80078EB6/EB8, `dayCounter` 0x80078EAC, `hour` 0x80078EB1,
`tiredness_raw` 0x80078EA4, `bossHp_SteamKnight` 0x801E4398. **Four 2-datapoint false positives caught
and refuted** (clock 0x8007CE82, two tiredness guesses, a dow byte) → see R10.
- `git status`: only docs + tooling/scripts tracked; all ROM-derived (`ghidra/`, `disks/`, extracted bulk,
the PCSX AppImage) gitignored.
**Milestone achieved:** `docs/memory-map.md §4.3` holds a loader/overlay table where the resident blob
(0x800CEDF8) and a location overlay (0x80128158) have US load addresses **proven byte-identical** (sha1-equal
contiguous prefixes) against a **live PCSX-Redux RAM dump** — the project's central "what loads where"
unknown, resolved.
**Next:** **Phase 4 = PROTOTYPE RESEARCH SPIKE** (Drew's directive — supersedes the roadmap's "build
toolchain" as the *next* phase; toolchain follows). Start with a symbol/debug-info hunt in the Aug-31
"Musashi Demo". See "Notes for Future Phases" below for the full brief.
## Deviations
| Item | Plan | Actual | Reason |
|---|---|---|---|
| Runtime-oracle location | PCSX-Redux under WSLg (all-in-WSL) | **Windows-native** PCSX-Redux; WSL fetches RAM via 172.17.208.1:8081 | WSLg wouldn't paint the window; WSL2 kernel can't do the DualSense. Hybrid bridged by the web API (→ R11) |
| Overlay-mapping scope | ≥1 overlay (milestone) | 13 area-states across SC01/02/03 + full loader model | Drew-driven live tour; high value for Gen2 overlay configs |
| Player-state RE | not in plan | gold/HP/BP/day/hour/tiredness/boss-HP verified; save-header found | Opportunistic live triangulation (Drew fed ground truth) |
| Prototypes | parking lot | 2 US protos acquired + CRC-verified; FF8 demo disc | Drew acquired them; seed the Phase-4 spike |
| Next phase | Phase 4 = build toolchain (roadmap) | Phase 4 = prototype research spike | Drew's directive — assess proto value before matching |
| T6b proof form | "sha1 of dump slice == sha1 of extracted blob" (whole blob) | sha1-equal large **contiguous prefix** (runtime data diverges past the code) | Runtime writes mutate the blob's data after load; the code prefix is byte-identical at the exact vaddr — conclusive for the load address (P9) |
| `.DA`/proto context | — | SLUS-90029 corrected to FF8 demo (not a BFM demo); no playable US BFM demo exists | Drew correction + web research |
## Commit Message
```
feat(phase-3): file-loader & overlay-map RE + PCSX-Redux runtime oracle
- RE'd the full file-loader/overlay subsystem in Ghidra via MCP: CdReadRequest ->
CdReadStateMachine (hand-rolled CdControl SeekL/ReadN reader, NOT PsyQ CdRead);
LIST.CD RAM cache cdFileLocTable + ResourceGetCdLoc/resourceIdMap lookup; LZSS
staging buffer 0x80079A70; loadDestPtrTable; ~50 symbols named (T1-T5,T8)
- T6b MILESTONE: resident blob @0x800CEDF8 + location overlay @0x80128158 proven
byte-identical (sha1-equal contiguous prefixes) vs live PCSX-Redux RAM; blobs
imported into Ghidra (ImportOverlay.java); overlay-map table -> memory-map.md §4.3
- PCSX-Redux runtime oracle: Windows-native build, WSL reads RAM via the web API
at 172.17.208.1:8081; ledger #4 resolved
- live-verified player/time state: gold(/10) HP BP day hour tiredness boss-HP;
save-header template (Q#5) + debug-menu dispatch (Q#9: DEBUG.BIN path is dead)
- overlay map: 13 area-states across SC01/02/03, 2 duplicate pairs, location x
chapter keying, free-roam vs menu-hub granularity
- resolves Open Q#1/#3/#4/#5/#6/#8/#9/#12; corrects SLUS-90029 (FF8 demo, not BFM)
- 2 US prototypes acquired + CRC-verified (Aug-31 1369DE07, Sep-8 5C24728E)
- rules R9 (verify persistence), R10 (>=3-datapoint live verification),
R11 (runtime oracle Windows-native, bridged to WSL)
- bumps project version 1.2.0 -> 1.3.0
```
## Rules Added This Phase
| Rule | Reason |
|---|---|
| **R9 — Verify Ghidra persistence after every save-shutdown.** Read-only re-open (`tools/ghidra_mcp_verify.sh <addr> <name>`) and confirm a recent edit survived — never trust "Save succeeded" alone. Mandatory at phase end; recommended at each checkpoint. | Headless MCP writes persist only on clean shutdown; the live server holds an open transaction. Catches a lost save before it strands work. (Candidate since this phase; formalized.) |
| **R10 — Multi-datapoint live-RAM verification.** A RAM-address finding from PCSX-Redux is `verified` only with **≥3 consistent datapoints** OR a **controlled before/after diff** (e.g. paused snapshots, sleep reset). Two-datapoint matches stay `candidate` (coincidence-prone). | Four 2-datapoint false positives this phase (clock 0x8007CE82, two tiredness guesses, a day-of-week byte) were each disproven by a 3rd datapoint. Aligns G1/P9 (bytes are truth). |
| **R11 — Runtime oracle is Windows-native, bridged to WSL via the web API** (`http://172.17.208.1:8081`). Build, Ghidra, splat, asm-differ, analysis all stay in WSL (R2/H2 unchanged for those); only the emulator GUI + controller live on Windows — the explicit all-in-WSL exception. | WSLg can't render PCSX-Redux and the WSL2 kernel can't expose a DualSense; the web API is a clean, low-friction WSL↔Windows boundary that preserves the rest of the architecture. |
| **R12 — No `/tmp`; project-local data only.** All project data — including transient runtime scratch (logs, sentinels, temp outputs) — lives under `~/bfm-decomp` (runtime scratch → `.run/`, gitignored). Never `/tmp`. | Drew directive 2026-06-14: `/tmp` is volatile (28 non-regenerable RAM dumps nearly lost on a reboot). Keeps the clone self-contained and durable. |
## PhaseEnd Changelog
**v1.2.0 → v1.3.0** — Phase 3 complete. The file-loader/overlay subsystem is fully reverse-engineered in
Ghidra and the central "what loads where" unknown is **byte-proven against live hardware behavior**: the
resident blob (0x800CEDF8) and location overlays (0x80128158) load position-locked and verbatim; the loader
is a hand-rolled `CdControl` async reader (not PsyQ `CdRead`) driven by `cdFileLocTable` (the parsed LIST.CD
TOC) + a two-level resource-ID map. A live overlay map of 13 area-states across SC01/SC02/SC03 was captured
(duplicate overlays, location×chapter keying, free-roam-vs-menu-hub granularity). PCSX-Redux stood up as the
runtime oracle (Windows-native, WSL web-API bridge). Bonus live-verified the player/time state block
(gold÷10, HP/BP, day, hour, tiredness) and the save-header (Q#5) + debug-menu dispatch (Q#9: DEBUG.BIN dead).
Open Questions #1/#3/#4/#5/#6/#8/#9/#12 resolved. Three rules added (R9 persistence, R10 multi-datapoint
verification, R11 Windows-native oracle). Two US prototypes acquired + CRC-verified, seeding the Phase-4 spike.
## Notes for Future Phases — Phase 4 RESEARCH SPIKE brief (Drew's directive)
Analyze the two CRC-verified US prototypes (`disks/`) **before** the build/matching phases:
1. **Debug info?** Hunt the Aug-31 "Musashi Demo" + Sep-8 "Musashi Master" for `.SYM`/symbol tables, debug
strings, assertion/source-path text, less-stripped data. (Hidden Palace said "no symbols" — UNVERIFIED.)
2. **Is the demo easier to decomp, and does it help the full game?** Assess optimization level vs retail.
**Caveat:** demo ≠ retail bytes → demo *matches don't transfer* to SLUS-00726; value is **labels + a
readable reference + shared tables**, not matched bytes.
3. **Free-label lead (already identified):** the Aug-31 demo's title scene/area-select very likely shares
code/tables with the retail debug menu mapped in T8 — `GameModeDispatch` (0x80010B40),
`gameModeHandlerTable` (0x800629F4), `DebugMenuHandler` (0x80011144). Binary-diff to lift labels in.
4. **Deliverable:** a go/no-go on whether the protos accelerate Gen1 matching, plus any labels imported.
This is a **roadmap insertion** before Phase 4 (build toolchain), which then follows. Also outstanding for
later: the save-data block format (trace `SaveLoadRoutine` 0x8002B154, Q#5); the exact tiredness-% / minute /
day-vs-dow encoding; importing more overlays as Ghidra overlay address spaces (Gen2).
## 🛑 Stop Here
PhaseEnd written; `CURRENT_PHASE.md` absorbed. **Drew commits AND pushes** this PhaseEnd (R6/R8). Start a
fresh session for the next phase. Keep this file forever.