mirror of
https://github.com/Druthulu/BFM-decomp
synced 2026-10-04 08:21:50 -04:00
ca51930605
Surgically correct PROJECT_CONTEXT.md env sections (H1/H2, Quick Ref, Architecture diagram, Assumptions/Risks, Phase 1 & 4 roadmap) to Linux-first single-clone; all rules/roadmap content otherwise preserved. SETUP.md rewritten Linux-first with all facts intact + redump disc-provenance added. .gitignore permits ROM-derived content (asm/assets/extracted) while private; raw dump + build churn stay ignored.
465 lines
38 KiB
Markdown
465 lines
38 KiB
Markdown
# SETUP.md — Environment Setup & Daily Operations Reference
|
||
|
||
> **This file is the EVOLVABLE reference layer.** Unlike `PROJECT_CONTEXT.md` (permanent, never edited), this document holds volatile facts — pinned versions, URLs, commands, ports — and **may be updated freely** as tools move. Note each change in the active phase log (`CURRENT_PHASE.md`). Items marked **TBD** / **UNVERIFIED** / **JP-only — re-derive for US** are honest gaps: confirm before relying on them, then update this file.
|
||
|
||
Last full revision: 2026-06-10 (initial authoring, pre-Phase-1 — nothing below is installed yet except the repo itself; same-day conversion to the all-in-WSL / Linux-first architecture — everything now runs inside a single WSL2 Ubuntu 24.04 clone, no Windows/WSL split).
|
||
|
||
## Version pin summary
|
||
|
||
All components run inside WSL2 Ubuntu 24.04 (single ext4 clone). The "Side" column below is retained for historical continuity but every entry is now **WSL2 (Linux)**.
|
||
|
||
| Component | Pinned version | Side | Phase |
|
||
|---|---|---|---|
|
||
| JDK | 21 (Temurin) | WSL2 (Linux) | 1 |
|
||
| Ghidra | **12.1 PUBLIC exactly** (`ghidra_12.1_PUBLIC_20260513.zip`) — NOT 12.1.2 | WSL2 (Linux) | 1 |
|
||
| GhidrAssistMCP | v2.8.0 (`ghidra_12.1_PUBLIC_20260530_GhidrAssistMCP.zip`) | WSL2 (Linux) | 1 |
|
||
| ghidra_psx_ldr | release 2026.06.04 (`ghidra_12.1_PUBLIC_20260604_ghidra_psx_ldr.zip`) | WSL2 (Linux) | 1 |
|
||
| PCSX-Redux | Linux build (AppImage/flatpak; no version pin; record build on install) | WSL2 (Linux) | 3 |
|
||
| WSL2 distro | Ubuntu-24.04 (Python 3.12 ships with it) | WSL2 (Linux) | 4 |
|
||
| splat | pip `splat64[mips]` `>=0.41.0,<1.0.0`; freeze exact version once Phase 5 is green | WSL2 (Linux) | 4 |
|
||
| Vintage compiler | decompals/old-gcc **release 0.17**: `gcc-2.7.2-psx` + `gcc-2.7.2-cdk` | WSL2 (Linux) | 4 |
|
||
| maspsx | git submodule, `mkst/maspsx` (decomp.me pins commit `874855c53f65f8fa57447e1da6bde6236dbef9d5` — reasonable default pin) | WSL2 (Linux) | 4 |
|
||
| asm-differ / m2c / decomp-permuter | git submodules (URLs in §4.6) | WSL2 (Linux) | 4 |
|
||
| binutils (mipsel) | apt `binutils-mipsel-linux-gnu` — **>=2.38 regression check required**, 2.35 known-good per open-ribbon | WSL2 (Linux) | 4 |
|
||
|
||
---
|
||
|
||
## §1 The all-in-WSL environment (single ext4 clone)
|
||
|
||
```
|
||
┌─ WSL2 Ubuntu 24.04 (ext4) — everything lives here ──────────────────────────┐
|
||
│ │
|
||
│ ~/bfm-decomp ← THE clone (one git working tree, one remote) │
|
||
│ ├─ Ghidra 12.1 PUBLIC (JDK 21 Temurin, GUI via WSLg) │
|
||
│ │ ├─ ghidra_psx_ldr 2026.06.04 (PSX loader, PsyQ signatures, │
|
||
│ │ │ .gdt type archives) │
|
||
│ │ └─ GhidrAssistMCP v2.8.0 ── SSE 127.0.0.1:8080 (local to WSL) │
|
||
│ │ │
|
||
│ ├─ Ghidra project .gpr/.rep (under ~/bfm-decomp or ~/.ghidra) │
|
||
│ ├─ PCSX-Redux (Linux build: runtime oracle — debugger, Lua, │
|
||
│ │ web API RAM dumps, GDB server :3333) │
|
||
│ ├─ Claude Code ← .mcp.json → 127.0.0.1:8080/sse (same box, no boundary) │
|
||
│ │ │
|
||
│ ├─ .venv/ splat64[mips] etc. │
|
||
│ ├─ tools/maspsx, tools/asm-differ, tools/m2c, tools/decomp-permuter (subm.)│
|
||
│ ├─ tools/bin/gcc-2.7.2-psx, tools/bin/gcc-2.7.2-cdk │
|
||
│ │ (old-gcc 0.17, Linux x86-64 binaries) │
|
||
│ ├─ disks/ BIN/CUE dump, copied in ONCE (gitignored) │
|
||
│ └─ asm/ build/ expected/ (generated, never committed) │
|
||
│ │
|
||
│ apt: binutils/gcc-mipsel-linux-gnu, make, ninja, python3.12, JDK 21, ... │
|
||
└───────────────────────────────────┬─────────────────────────────────────────┘
|
||
│
|
||
└── git remote (push/pull off-box only) ──►
|
||
```
|
||
|
||
One repository clone on ext4 at `~/bfm-decomp` holds everything — RE stack, build toolchain, runtime oracle, docs, and Claude Code's working directory. There is **no Windows/WSL split**: no second clone, no cross-OS working-tree edits, no `/mnt` build path. The git remote is used only to push/pull off the machine (backup, collaboration), never to synchronize two local trees. Rationale for keeping the clone on ext4 (not on a `/mnt/*` drvfs mount):
|
||
|
||
- **9P filesystem penalty.** File access on `/mnt/*` drvfs mounts goes through the Plan 9 protocol and is the slowest path WSL2 offers — Microsoft's own numbers put ext4 at 2–20× faster, and community benchmarks measured builds up to 375% faster after moving off `/mnt`. Every `make`, `git status`, and splat extract pays the tax if the build tree sits on a mounted Windows drive. Keeping the clone on ext4 sidesteps this entirely.
|
||
- **inotify works on ext4.** Linux file-watchers get no events for changes on `/mnt/*` drvfs mounts (microsoft/WSL [#4739](https://github.com/microsoft/WSL/issues/4739), [#5424](https://github.com/microsoft/WSL/issues/5424)). asm-differ watch mode fires reliably only when source *and* build outputs live on ext4 — which, all-in-WSL, they always do.
|
||
- **No ghost-file hazard.** anthropics/claude-code [#28015](https://github.com/anthropics/claude-code/issues/28015): Write/Edit on WSL2 drvfs mounts can hit a statx/9P cache-poisoning kernel bug producing "ghost files" (ENOENT for files that exist). Editing only on ext4 avoids the bug; Claude Code runs inside WSL and never touches a drvfs path.
|
||
|
||
One-shot file copies onto ext4 (e.g. the disc dump into `disks/`) from any source are fine — it is sustained/random IO and watchers on drvfs that break, not correctness. Ghidra project files (`.gpr`/`.rep`) live on ext4 alongside the clone (or under `~/.ghidra`), consistent with Ghidra's local-drive-storage recommendation; their `~lock` files behave correctly on a native Linux filesystem.
|
||
|
||
Git hygiene: the committed `.gitattributes` (`* text=auto eol=lf` + binary exclusions) keeps line endings consistent. On the Linux clone `core.filemode true` is fine (ext4) and `core.autocrlf` is `false` by default (LF-native), so no line-ending dance is needed.
|
||
|
||
---
|
||
|
||
## §2 RE stack install (Phase 1, all in WSL)
|
||
|
||
Order matters: JDK → Ghidra → both extensions → import → MCP wiring.
|
||
|
||
### §2.1 JDK 21 (Temurin)
|
||
|
||
```bash
|
||
sudo apt-get update && sudo apt-get install -y openjdk-21-jdk
|
||
```
|
||
|
||
Required by Ghidra 12.x. (The Temurin apt repo — `https://packages.adoptium.net` — is an equivalent alternative if you prefer Temurin specifically.) Verify: `java -version` reports 21.x, and ensure it is the JDK on `PATH` for the shell that launches Ghidra (`ghidraRun` needs JDK 21 visible).
|
||
|
||
### §2.2 Ghidra 12.1 PUBLIC — pin EXACTLY
|
||
|
||
Download **`ghidra_12.1_PUBLIC_20260513.zip`** from
|
||
`https://github.com/NationalSecurityAgency/ghidra/releases/tag/Ghidra_12.1_build` — plain zip, no installer; the same cross-platform Java distribution on every OS. Unzip and launch the GUI under WSLg:
|
||
|
||
```bash
|
||
cd ~ && unzip ghidra_12.1_PUBLIC_20260513.zip
|
||
~/ghidra_12.1_PUBLIC/ghidraRun # WSLg supplies the X11/Wayland display
|
||
```
|
||
|
||
`./ghidraRun` needs JDK 21 on `PATH` (§2.1); the Ghidra GUI renders through WSLg with no extra X-server setup.
|
||
|
||
> ⚠️ **Extension version-lock warning.** Ghidra extensions are point-version-locked via `extension.properties`. Both extension zips below are built against `12.1_PUBLIC`; installing them on 12.1.2 (current latest, 2026-06-05) may trip the version check (red "incompatible" flag — same failure mode as LaurieWired issue #83 on 11.4.1). Whether the 12.1 zips happen to load on 12.1.2 is **UNVERIFIED** — pin **12.1**, and only move to a point release when both extensions ship matching assets or you rebuild them from source (`gradle installExtension` with `GHIDRA_INSTALL_DIR` set). The extension assets are platform-independent Java (`ghidra_12.1_PUBLIC_..._GhidrAssistMCP.zip` / `..._ghidra_psx_ldr.zip` — the "12.1" is the Ghidra version, not the OS), so the same asset filenames install on Linux Ghidra 12.1.
|
||
|
||
### §2.3 GhidrAssistMCP v2.8.0
|
||
|
||
- Asset for our Ghidra: **`ghidra_12.1_PUBLIC_20260530_GhidrAssistMCP.zip`** from
|
||
`https://github.com/symgraph/GhidrAssistMCP/releases/tag/2.8.0`
|
||
(repo moved from `jtang613/GhidrAssistMCP` to `symgraph/GhidrAssistMCP`; GitHub redirects. The release carries TWO assets — take the `12.1` one, not `12.0`.)
|
||
- Install: Ghidra → **File → Install Extensions… → `+`** → select zip → restart Ghidra.
|
||
- Enable plugin: **File → Configure → Configure Plugins → check `GhidrAssistMCP`**.
|
||
- Control panel: **Window → GhidrAssistMCP** → set **Host = localhost, Port = 8080**. Server exposes `/sse` (SSE), `/message`, and `/mcp` (streamable HTTP). No Python bridge process — Claude Code connects directly.
|
||
- This is the server the psxrecomp proof-of-concept actually ran with Claude Code (their port was 7777, a user setting; we standardize on 8080 to match the committed `.mcp.json`).
|
||
|
||
**Fallbacks (documented, not installed):**
|
||
|
||
| Server | Status | Why fallback only |
|
||
|---|---|---|
|
||
| bethington/ghidra-mcp v5.13.x | active, claims Ghidra 12.1, 249 tools | Unaudited, single-maintainer with day-apart releases, needs a Python bridge (plugin HTTP :8089), 249 tools swamp Claude's tool context. Consider only if a missing niche tool (mass `create_enum`, `batch_decompile`) becomes a bottleneck. |
|
||
| clearbluejar/pyghidra-mcp | active, pip/uvx, headless multi-binary | **No struct-creation or apply-type-at-address tools** — dead end as primary server for matching-decomp data work; fine for scripted headless batch passes. |
|
||
| LaurieWired/GhidraMCP 1.4 | **unmaintained — DO NOT INSTALL** | Caps at Ghidra 11.3.2 (issues #83/#131 open, no fix since 2025-06-23); ghidra_psx_ldr dropped 11.x. Older write-ups (including the 1379.tech psxrecomp blog post) credit it incorrectly. |
|
||
| ismaelcaraballo-afk/GhidraMCP-12 | stale one-off recompile | 12.0.1 only, no struct tools, not viable. |
|
||
|
||
### §2.4 ghidra_psx_ldr release 2026.06.04
|
||
|
||
- Asset: **`ghidra_12.1_PUBLIC_20260604_ghidra_psx_ldr.zip`** (~9.15 MB, PsyQ signatures bundled — no separate OBJ download needed) from
|
||
`https://github.com/lab313ru/ghidra_psx_ldr/releases/tag/2026.06.04`
|
||
- Install via the same **File → Install Extensions…** path → restart.
|
||
- Coexists with GhidrAssistMCP without conflicts (PSX loader/analyzer/SLEIGH vs HTTP-server plugin).
|
||
|
||
### §2.5 PSX-EXE import flow (SLUS_007.26)
|
||
|
||
1. Extract `SLUS_007.26` from the disc image (LBA 24, 0x65000 bytes, track 1 is MODE2/2352) and import into Ghidra. The loader auto-selects **"PSX Executables Loader"**, language `PSX:LE:32:default`, and builds the full PS1 memory map: RAM around the image at 0x80000000, scratchpad 0x1F800000, all IO/DMA/timer/CD/GPU/SPU register blocks, **and a synthetic GTEMAC segment at 0x20000000 automatically** — do NOT run the `CreateGteMacSegment` script (that is only for migrating legacy non-PSX projects).
|
||
2. Run auto-analysis with the **"PsyQ Signatures"** analyzer enabled (auto-enabled for PSX-language programs). Analyzer options: "Only first match", "Minimal signature entropy" (default 3.0 — can skip tiny low-entropy library stubs), "PsyQ Version if not found" (manual override).
|
||
3. Read the detected PsyQ version: **Edit → Options for Program → Program Information → "PsyQ Version"**. Expected: **4.0** (our own EXE scan found 12 genuine `Ps` stamps: 9× 4.0, one 4.0.1x on libnum 16, one 4.2 on libnum 0, one 4.2.1x on libnum 12 — i.e. PsyQ 4.0 libs + 4.2 library updates; see §5.1). If detection errors with `'psyq/xx' cannot be found`, append `.0` to the version field. Record the detected value in the phase log.
|
||
*(Note: a raw track-1 scan during research reported slightly different per-libnum details — raw 2352-byte-sector scans produce false positives; the extracted-EXE scan is the ground truth, and DetectPsyQ at import is the final word.)*
|
||
4. **One-time manual `.gdt` attach (GUI only — no MCP tool opens archives):** in the CodeBrowser Data Type Manager, attach the bundled PsyQ type archive for the detected version — `psyq400.gdt` (`psyq420.gdt` also exists). This gives PsyQ struct/typedef types for retyping work.
|
||
5. **Early MCP type-resolution test (run before any bulk typing):** via MCP, run the `types` tool with `action=set` applying a PsyQ type (e.g. apply a known PsyQ struct at some address) and confirm it resolves. **UNVERIFIED** whether the `types`/`struct` tools can reference types living in the attached archive or only types already copied into the program's own data type manager — this 5-minute test decides the typing workflow. Record the answer here when known.
|
||
|
||
### §2.6 `.mcp.json` wiring + verification
|
||
|
||
The committed repo-root `.mcp.json` (already present):
|
||
|
||
```json
|
||
{
|
||
"mcpServers": {
|
||
"ghidra": {
|
||
"type": "sse",
|
||
"url": "http://127.0.0.1:8080/sse"
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
Single `ghidra` entry only — do NOT copy psxrecomp's duplicated `ghidra` + `ghidra_psx` pair (same URL twice = every tool duplicated in context).
|
||
|
||
Verify in Claude Code with `/mcp`: **~38 `mcp__ghidra__*` tools** should appear. These are the **v2.8.0 names** — e.g. `get_binary_info`, `get_code` (format: disassembly|decompiler), `disassemble_at`, `analyze_function`, `xrefs`, `get_functions` (paginated), `struct` (actions: create/modify/merge/set_field/name_gap/auto_create/rename_field/field_xrefs), `types` (list/get_info/set/delete), `variables` (list/rename/set_type/set_prototype), `rename_symbol`, `batch_rename`, `create_data_var`, `create_function`, `search_bytes`, `patch_bytes`, `assemble_code`, `export_program`. **NOT the pre-2.4.0 names** (`get_function_info`, `list_data`) that appear in psxrecomp's PLAN.md and older write-ups — those were renamed in v2.4.0 (upstream commit `aa3ffc7d`, 2026-03-14).
|
||
|
||
Operational cautions:
|
||
- Tools operate on the program currently open in CodeBrowser and fail (sometimes silently) if none is open.
|
||
- Do not run Ghidra auto-analysis concurrently with MCP-driven writes (renames/comments) — both mutate the program DB and can stall each other.
|
||
|
||
### §2.7 Headless mode (batch passes)
|
||
|
||
For unattended batch passes, GhidrAssistMCP runs headless (supported since v2.3.0):
|
||
|
||
```bash
|
||
<ghidra>/support/analyzeHeadless <projects-dir> McpHeadless \
|
||
-import <binary> \
|
||
-scriptPath "<extension-dir>/ghidra_scripts" \
|
||
-preScript GAMCPStartServerScript.java "host=127.0.0.1" "port=8080" "wait=true"
|
||
```
|
||
|
||
- **`wait=true` is mandatory** — without it the analyzeHeadless process exits right after the prescript instead of serving MCP clients.
|
||
- On subsequent runs use **`-process SLUS_007.26`** (not `-import`) to reuse the existing project.
|
||
|
||
---
|
||
|
||
## §3 PCSX-Redux (Linux) — the runtime oracle
|
||
|
||
**Role:** Ghidra is the static oracle; PCSX-Redux is the **runtime oracle**. It proves overlay load addresses by live RAM comparison, validates decompressed blobs byte-for-byte against what the game actually loads, and drives the debug-menu/loader RE.
|
||
|
||
- **Install:** Linux build (AppImage or flatpak) from `https://distrib.app/pub/org/pcsx-redux/project` (no stable pin exists — record the build date in the phase log when installed). The GUI runs under WSLg. Docs: `https://pcsx-redux.consoledev.net`. Ships OpenBIOS (boots without a retail BIOS dump).
|
||
- **RAM dumps (primary use):** enable the built-in web server, then
|
||
**`GET http://localhost:<redux-port>/api/v1/cpu/ram/raw`** returns the full 2 MB RAM image — diff slices of it against our extractor's output to prove byte-identity. (`<redux-port>` = whatever port Redux's web server is configured to after moving it off 8080 — substitute the real value here at Phase 3 install; ledger #4.)
|
||
> ⚠️ **Port collision (derived, plan around it):** PCSX-Redux's web server documents `localhost:8080` — the same port as GhidrAssistMCP. Keep GhidrAssistMCP on 8080 (matches the committed `.mcp.json`) and move the Redux web server to another port (e.g. 8081) when enabling it; the exact Redux config field is **TBD — confirm in its settings UI on install and update this line + the dump URL**.
|
||
- **Lua scripting:** LuaJIT 2.1.0-beta3 (Lua 5.2 compat), FFI direct memory access; console + editor under the Debug menu. Gotcha: the editor autosaves to `pcsx.lua` and reloads it at startup — a crashing script wedges the emulator across restarts (delete `pcsx.lua` to recover); long-running scripts must yield via coroutines (~15 ms/frame budget).
|
||
- **Debugger:** fully featured MIPS debugger, VRAM/SPU viewers. GDB server on port 3333 (Configuration → Emulation → Enable GDB server); for Ghidra-attached debugging launch Redux with `-interpreter -debugger -gdb` (dynarec breaks debugging) and connect `gdb-multiarch -i mi2` → `target remote localhost:3333`.
|
||
- PSX RAM at 0x80000000 mirrors physical 0x0 (0x800425D0 ≡ 0x000425D0) — relevant when reading dump offsets.
|
||
|
||
---
|
||
|
||
## §4 Build environment (Phase 4)
|
||
|
||
Everything below installs inside the same WSL2 Ubuntu 24.04 that already hosts Ghidra and Claude Code (§1). Where Phase 1 (§2) already set up the distro and JDK, this phase adds the build toolchain on top.
|
||
|
||
### §4.1 WSL2 + Ubuntu 24.04 (already present from Phase 1)
|
||
|
||
The all-in-WSL architecture means WSL2 Ubuntu 24.04 is the single host for the whole project, so it exists before Phase 4 begins (it is the same environment §2 installed Ghidra into). Confirm it is the expected distro and version:
|
||
|
||
```bash
|
||
cat /etc/os-release # Ubuntu 24.04
|
||
uname -a # Linux kernel (WSL2)
|
||
whoami # the Linux username; ~ resolves to /home/<user>
|
||
```
|
||
|
||
All project paths are plain Linux paths under `~/bfm-decomp` — there is no Windows distro name or `wsl.exe --cd` target to track.
|
||
|
||
### §4.2 Networking: MCP is local
|
||
|
||
Under the all-in-WSL architecture there is **no cross-OS networking**. Ghidra/GhidrAssistMCP, PCSX-Redux, and Claude Code all run inside the same WSL2 instance, so the MCP endpoint is plain loopback: **MCP is local to WSL at `http://127.0.0.1:8080`; no mirrored-mode `.wslconfig`, firewall rule, or host-IP discovery is needed.** Smoke-test with Ghidra running: `curl http://127.0.0.1:8080/` from any shell in the same WSL instance.
|
||
|
||
### §4.3 The clone on ext4
|
||
|
||
```bash
|
||
cd ~ && git clone <remote-url> bfm-decomp
|
||
```
|
||
|
||
The single clone lives at `~/bfm-decomp` (ext4). Builds, splat, asm-differ, Ghidra, and Claude Code all run here. In this clone: `git config core.filemode true`. **TBD:** the canonical remote URL (GitHub private repo planned; not created as of this writing).
|
||
|
||
### §4.4 Copy the disc dump into the clone
|
||
|
||
One-shot copy onto ext4 is fine (and required once):
|
||
|
||
```bash
|
||
mkdir -p ~/bfm-decomp/disks
|
||
cp '<dump-source>/Brave Fencer Musashi (USA)/'*.bin \
|
||
'<dump-source>/Brave Fencer Musashi (USA)/'*.cue ~/bfm-decomp/disks/
|
||
```
|
||
|
||
`<dump-source>` is wherever the disc dump currently lives (e.g. a one-time download into `~/Downloads`, or a one-shot copy from external media). `disks/` is gitignored — no ROM-derived bytes ever reach the remote (rule H1).
|
||
|
||
### §4.5 apt packages
|
||
|
||
Adapted from sotn-decomp's `tools/requirements-debian.txt` (dropped Saturn/PSP-only items `binutils-sh-elf`, `xfonts-utils`; Rust/Go deferred until a duplicate-detector or asset tool needs them):
|
||
|
||
```bash
|
||
sudo apt-get update && sudo apt-get install -y \
|
||
bchunk binutils-mipsel-linux-gnu bsdmainutils clang-format coreutils curl \
|
||
gcc-mipsel-linux-gnu git libelf-dev make ninja-build p7zip-full \
|
||
python3-pip python3-venv unzip wget
|
||
```
|
||
|
||
> ⚠️ **binutils regression check (mandatory before trusting builds):** open-ribbon documents that `binutils-mipsel-linux-gnu >= 2.38` generated broken binaries; **2.35 is the known-good reference**. Ubuntu 24.04 ships newer binutils — **VERIFY on 24.04**: after Phase 5's first full build, if the SHA1 check mysteriously fails with correct-looking asm, suspect the assembler first (`mipsel-linux-gnu-as --version`), and pin/downgrade or build binutils 2.35 if confirmed. Record the verdict here.
|
||
|
||
### §4.6 Python venv + splat + submodules
|
||
|
||
```bash
|
||
cd ~/bfm-decomp
|
||
python3 -m venv .venv # Python >= 3.12 required (24.04 ships 3.12; older = f-string SyntaxError mid-build)
|
||
.venv/bin/pip install -U 'splat64[mips]>=0.41.0,<1.0.0'
|
||
```
|
||
|
||
The PyPI package is **`splat64`** (not `splat`), and the `[mips]` extra is required for PSX (pulls spimdisasm/rabbitizer). Always invoke as `.venv/bin/splat` or `.venv/bin/python3 -m splat` — `splat: command not found` means you're outside the venv. Once Phase 5 builds green, freeze the exact working version in a committed `tools/requirements-python.txt`.
|
||
|
||
Submodules (add under `tools/`):
|
||
|
||
| Submodule | URL | Pin |
|
||
|---|---|---|
|
||
| `tools/maspsx` | `https://github.com/mkst/maspsx.git` | commit `874855c53f65f8fa57447e1da6bde6236dbef9d5` (decomp.me's pin — keeps local results comparable to decomp.me scratches) |
|
||
| `tools/asm-differ` | `https://github.com/simonlindholm/asm-differ.git` | pin current HEAD at adoption |
|
||
| `tools/m2c` | `https://github.com/matt-kempster/m2c.git` | pin current HEAD at adoption |
|
||
| `tools/decomp-permuter` | `https://github.com/simonlindholm/decomp-permuter` | sotn pins `b44b0622269fb4bff29e79fbbad26b9f47beda79` — sane default |
|
||
|
||
Pin all four (sotn precedent: blindly updating submodules breaks tooling). Note: sotn's asm-differ `--overlay` flag is **sotn-fork-specific**, not upstream — for BFM overlay diffing use upstream's `-o` object mode or port their fork later.
|
||
|
||
### §4.7 Vintage compilers (old-gcc 0.17)
|
||
|
||
Linux x86-64 prebuilts from decompals/old-gcc, **release 0.17**:
|
||
|
||
```bash
|
||
mkdir -p ~/bfm-decomp/tools/bin && cd ~/bfm-decomp/tools/bin
|
||
wget https://github.com/decompals/old-gcc/releases/download/0.17/gcc-2.7.2-psx.tar.gz
|
||
wget https://github.com/decompals/old-gcc/releases/download/0.17/gcc-2.7.2-cdk.tar.gz
|
||
sha256sum gcc-2.7.2-*.tar.gz # record hashes in a committed tools/bin/*.sha256 on first download,
|
||
# then verify with `sha256sum --check` on every fresh setup (sotn pattern)
|
||
tar xzf gcc-2.7.2-psx.tar.gz ; tar xzf gcc-2.7.2-cdk.tar.gz
|
||
```
|
||
|
||
- `gcc-2.7.2-psx` = community GCC 2.7.2 PSX build (primary candidate).
|
||
- `gcc-2.7.2-cdk` = **cygnus-2.7.2-970404**, the exact base of PsyQ 4.0/4.1's CC1PSX (added in old-gcc 0.14).
|
||
- **TBD:** the sha256 values themselves — not captured in research; record at first download.
|
||
- These are x86-64 Linux ELF binaries — they are *why* the build side must be Linux/WSL2 at all.
|
||
|
||
### §4.8 Optional: PsyQ 4.0/4.1 binaries for arbitration (via Wine)
|
||
|
||
For byte-exact arbitration when maspsx output is in doubt, the **real** PsyQ Win32 tools can be driven from WSL under Wine (`sudo apt-get install -y wine`):
|
||
|
||
- `https://github.com/mkst/esa/releases/download/psyq-binaries/psyq4.0.tar.gz`
|
||
- `https://github.com/mkst/esa/releases/download/psyq-binaries/psyq4.1.tar.gz`
|
||
(contain `CC1PSX.EXE`, `ASPSX.EXE`, `CCPSX.EXE`, `PSYLINK.EXE`, `PSYLIB.EXE`; 1–2.3 MB each)
|
||
- Their `.OBJ` output converts to ELF with **psyq-obj-parser** (part of pcsx-redux; prebuilt Linux binary: `https://github.com/decompme/compilers/releases/download/compilers/psyq-obj-parser.tar.gz`).
|
||
|
||
Keep these under `tools/` on ext4 (not committed); they are a tie-breaker, not the daily pipeline. (decomp.me runs these same Win32 tools under Wine for its psyq presets — the precedent that this works headless.)
|
||
|
||
### §4.9 `make check-env` (Phase 4 exit milestone)
|
||
|
||
Phase 4's observable milestone: a `check-env` make target that asserts every §4 component (venv + splat import, cc1 binaries executable, maspsx present, mipsel-as/ld/objcopy on PATH, python >= 3.12) and exits 0 when invoked directly in the WSL clone (see §6.1).
|
||
|
||
---
|
||
|
||
## §5 Compiler candidate ladder (Phase 6 fingerprinting)
|
||
|
||
### §5.1 The evidence
|
||
|
||
Locally verified on the extracted US EXE (DetectPsyQ-style masked-pattern scan): **13 pattern hits, of which 12 are genuine `Ps` library stamps** — 9× PsyQ **4.0** (libnums 2, 3, 4, 6, 7, 8, 9, 17, 24), 1× **4.0.1x** (libnum 16), 1× **4.2** (libnum 0), 1× **4.2.1x** (libnum 12); the remaining hit (ver 0x0000 at vaddr 0x8005CD20) is a code false positive. A raw-track scan during research reported 16 hits with extra spurious 4.0 stamps — the extracted-EXE scan is ground truth, and ghidra_psx_ldr's detection at import is the final word (ledger #12). Library copyright string `(c) 1993-1997 Sony` corroborates the era. Conclusion: BFM links **PsyQ 4.0 libraries with 4.2 library updates** ⇒ the GCC 2.7.2/SN32-era toolchain — **NOT sotn's GCC 2.6.3** (the starting-point doc's claim is corrected). PsyQ 4.2 was a library-only refresh: no 4.2 toolchain disc survives (absent from redump/arthus sets), so 4.2 stamps still mean the 4.0/4.1 toolchain.
|
||
|
||
Caveat: `Ps` stamps date the **linked libraries**, not the compiler that built game code — Square mixed cc1 builds within one EXE (see §5.5). The final triple is pinned only by Phase-6 fingerprinting.
|
||
|
||
### §5.2 SDK → GCC → ASPSX mapping (verified from the actual binaries in mkst/esa psyq-binaries tarballs)
|
||
|
||
| PsyQ SDK | CC1PSX identifies as | ASPSX | old-gcc 0.17 artifact | maspsx flag |
|
||
|---|---|---|---|---|
|
||
| 3.3 | GNU C 2.6.0 [AL 1.1, MM 40] | 2.21 | `gcc-2.6.0-psx` | `--aspsx-version=2.21` |
|
||
| 3.5 | GNU C 2.6.0 (same binary) | 2.34 | `gcc-2.6.0-psx` | `--aspsx-version=2.34` |
|
||
| 3.6 | GNU C 2.7.2.SN.1 | 2.34 | `gcc-2.7.2` (vanilla-ish) | `--aspsx-version=2.34` |
|
||
| **4.0** | **GNU C 2.7.2.SN32.3.7.0002** | **2.56** | **`gcc-2.7.2-psx` / `gcc-2.7.2-cdk`** | **`--aspsx-version=2.56`** |
|
||
| **4.1** | **cygnus-2.7.2-970404 SN32.3.7.0004 (SonyPSX)** | **2.67** | **`gcc-2.7.2-cdk`** (exact base) | **`--aspsx-version=2.67`** |
|
||
| 4.2 | *library-only release — no toolchain exists* | n/a | use 4.0/4.1 row | use 4.0/4.1 row |
|
||
| 4.3 | GNU C 2.8.0 SN32 Build 4.0.0007 (community shorthand "2.8.1" is wrong — that's 4.4) | 2.77 | `gcc-2.8.0-psx` | `--aspsx-version=2.77` |
|
||
| 4.4 | GNU C 2.8.1 SN32 BUILD 4.0.0010 | 2.79 | `gcc-2.8.1-psx` | `--aspsx-version=2.79` |
|
||
| 4.5 | egcs-2.91.66 (egcs-1.1.2) | 2.81 | `gcc-2.91.66-psx` | `--aspsx-version=2.81` |
|
||
| 4.6 | GNU C 2.95.2 BUILD 4.0.0030 | 2.86 | `gcc-2.95.2-psx` | `--aspsx-version=2.86` |
|
||
|
||
> ⚠️ **`--aspsx-version` MUST always be passed explicitly.** maspsx with no flag is *not* "latest behavior" — the dataclass defaults approximate ASPSX ~2.3x (`expand_li=True`, `sltu_at=True`, `nop_mflo_mfhi=True`). Behavior thresholds: `expand_li` off ≥2.50; `sltu_at` off ≥2.60; `$gp` symbol+offset ≥2.70; `$gp` for `la` ≥2.80. The observable 2.56 vs 2.67 tell: "$at for `sltu < 0`" present at 2.56, gone at 2.67, and %hi/%lo support arrives at 2.67 — decide on functions with unsigned comparisons / li/sltu idioms.
|
||
|
||
### §5.3 -G0 vs -G8
|
||
|
||
Read `gp_value` from the SLUS_007.26 EXE header and check for `$gp`-relative loads in Ghidra **before** fixing the flag (**TBD — not yet read**). Precedent: FF7 used `-G 0`; Xenogears used `-G8` for game code. maspsx forces `-G0` to GNU `as` by default — non-zero `$gp` needs `-G8` passed to maspsx and a look at `--dont-force-G0`.
|
||
|
||
### §5.4 Candidate ladder (try in this order)
|
||
|
||
1. **`gcc-2.7.2-psx` cc1 + `--aspsx-version=2.56`**, flags `-O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker` (FF7 style; swap to -G8 if §5.3 says so).
|
||
2. Same cc1 + `--aspsx-version=2.67` (PsyQ 4.1 assembler era).
|
||
3. **`gcc-2.7.2-cdk`** (cygnus-2.7.2-970404, the exact CC1PSX 4.0/4.1 base) × 2.56, then × 2.67.
|
||
4. Real PsyQ 4.0/4.1 `CC1PSX.EXE` + `ASPSX.EXE` (under Wine, §4.8) + psyq-obj-parser — byte-exact arbitration when maspsx emulation is in question.
|
||
5. Only if diffs show GCC 2.8-style codegen: `gcc-2.8.0-psx` + 2.77 (unlikely — the JP master predates PsyQ 4.3-era adoption).
|
||
|
||
**Library-code preset** (for PsyQ SDK functions linked into the EXE): `--aspsx-version=2.56 --expand-div`, `-O3 -G0` — the Xenogears precedent, consistent with our 4.0 lib stamps.
|
||
|
||
### §5.5 Per-module mixing warning
|
||
|
||
Xenogears (the closest comparable: Square US, Oct 1998) mixes **three** cc1 builds in one EXE (`gcc-2.7.2-psx`, `gcc-2.6.0-psx`, `gcc-2.7.2-cdk`) with different maspsx flags per module. Expect per-module variation in BFM: fingerprint **several modules independently** (3–5 medium leaf functions each), and design the build config for per-file compiler/flag overrides from day one (sotn does this with `//!` comments in line 1–2 of a .c file; Xenogears with `gears.toml` presets).
|
||
|
||
Reference repos for build-config patterns: `https://github.com/ladysilverberg/xenogears-decomp` (gears.toml presets), `https://github.com/Drahsid/ffvii` (Makefile).
|
||
|
||
---
|
||
|
||
## §6 Daily command crib
|
||
|
||
### §6.1 Running builds (native, in the WSL clone)
|
||
|
||
Claude Code runs inside WSL, so builds are plain native commands — no `wsl.exe` wrapper, no cross-shell quoting:
|
||
|
||
```bash
|
||
cd ~/bfm-decomp
|
||
make -j$(nproc) build # exit status is the build result; nonzero = failed
|
||
```
|
||
|
||
- The shell exit code (`$?`) carries the build result directly — no launcher layer to distinguish from a real failure.
|
||
- Output is native UTF-8; logs capture/pipe cleanly with no encoding workaround.
|
||
- The clone always lives on ext4 under `~/bfm-decomp`, so builds never accidentally touch a `/mnt` drvfs path.
|
||
|
||
### §6.2 Canonical compile pipeline (one object)
|
||
|
||
```bash
|
||
mipsel-linux-gnu-cpp -lang-c -Iinclude -undef -Wall -fno-builtin \
|
||
-Dmips -D__GNUC__=2 -D__OPTIMIZE__ -Dpsx -D_PSYQ -D_MIPSEL -D_LANGUAGE_C src/foo.c \
|
||
| bin/gcc-2.7.2-psx/cc1 -quiet -O2 -G0 -mips1 -mcpu=3000 -mgas -msoft-float -fgnu-linker \
|
||
| python3 tools/maspsx/maspsx.py --aspsx-version=2.56 \
|
||
| mipsel-linux-gnu-as -Iinclude -march=r3000 -mtune=r3000 -no-pad-sections -O1 -G0 -o build/foo.o
|
||
```
|
||
|
||
Modern cpp preprocesses → **vintage cc1** compiles to asm → **maspsx** emulates ASPSX quirks → modern GNU `as` assembles. Then `mipsel-linux-gnu-ld` with the splat-generated linker script, `objcopy -O binary` to the PS-EXE, SHA1-compare. cc1 path/flags above reflect the §5.4 first candidate — the exact flag set is **pinned only after Phase-6 fingerprinting** (`-funsigned-char`, `-fpeephole`, etc. are decided then; the cpp defines list is the sotn convention, adjust as evidence dictates).
|
||
|
||
### §6.3 Planned make targets (Phase 5 builds these; names fixed now)
|
||
|
||
| Target | Does |
|
||
|---|---|
|
||
| `make extract` | splat split per `config/splat.us.*.yaml` → `asm/`, linker scripts |
|
||
| `make build` | full pipeline → `build/us/SLUS_007.26`, auto-runs the SHA1 check |
|
||
| `make check` | standalone SHA1 manifest verification (byte-for-byte = the only "OK") |
|
||
| `make expected` | snapshot `build/us` → `expected/build/us` (asm-differ baseline) |
|
||
| `make check-env` | toolchain preflight, exit 0 = environment sane (§4.9) |
|
||
| `make clean` | mandatory after ANY `config/` change, before re-extract |
|
||
|
||
### §6.4 asm-differ + baseline discipline
|
||
|
||
```bash
|
||
.venv/bin/python3 tools/asm-differ/diff.py -mwo3 <function> # -m rebuild, -w watch, -o vs object, -3 three-way
|
||
```
|
||
|
||
- Watch mode works **only** with source and build outputs on ext4, modified from inside Linux (§1).
|
||
- **Re-snapshot `expected/` only on green:** run `make expected` exclusively after a build whose check passed. A stale `expected/` makes asm-differ silently diff against the wrong baseline — the classic "phantom regression/phantom match".
|
||
- Diff score 0 = matched; anything else is not matched, no matter how close.
|
||
|
||
### §6.5 decomp.me settings for BFM
|
||
|
||
- Platform: **PlayStation**; Compiler: **`gcc2.7.2-psx`** (or `gcc2.7.2-cdk`) **+ maspsx** family — these images bundle old-gcc cc1 + maspsx at the same pinned commit we use.
|
||
- Starting flags: `-O2 -G0` (adjust per §5).
|
||
- **Do NOT use the SOTN preset** (`Castlevania: Symphony of the Night` / `gcc 2.6.3-psx` / `psyq_263_221`) — wrong era, guaranteed near-miss diffs.
|
||
- decomp.me's API is Cloudflare-challenged (403 to scripts) — scratch searches/uploads needing the API must be done manually in a browser.
|
||
|
||
---
|
||
|
||
## §7 Session-start ritual
|
||
|
||
Order is load-bearing — MCP tools fail (sometimes silently) without an open program.
|
||
|
||
1. **Start Ghidra** (in WSL, under WSLg): `~/ghidra_12.1_PUBLIC/ghidraRun`.
|
||
2. **Open the BFM project → open `SLUS_007.26` in CodeBrowser.** Confirm the GhidrAssistMCP control panel (Window → GhidrAssistMCP) shows the server up on `localhost:8080`.
|
||
3. **Start/resume Claude Code** in `~/bfm-decomp`. Run `/mcp` — the `ghidra` server must be connected with ~38 tools. Then make one cheap verification call (e.g. `get_binary_info`) before any heavier work. No MCP round-trip = no RE work this session (rule G2).
|
||
4. **Build preflight** (when the session involves building, Phase 4+):
|
||
```bash
|
||
cd ~/bfm-decomp
|
||
git status --short && make check-env
|
||
```
|
||
One tree to check — clean or intentionally dirty before starting.
|
||
5. If PCSX-Redux work is planned: launch it (under WSLg), confirm the web server port (§3) and that a RAM dump fetch returns 2 MB.
|
||
|
||
Shutdown note: Ghidra writes the program DB on save — save (or deliberately discard) before closing, and never kill Ghidra mid-MCP-write.
|
||
|
||
---
|
||
|
||
## §8 Model strategy per phase
|
||
|
||
Principle: the oracles (SHA1 check, asm-differ, RAM-dump byte-compares) make *correctness* model-independent — a weaker model can't fake a match. Model tier therefore buys **fewer dead ends in ambiguous work**, not safer results. Spend the strongest available model where ambiguity is highest; let the oracle-protected grind run on cheaper tiers. (Precedent: psxrecomp's post-mortem — model capability was load-bearing exactly once, on the most ambiguous subsystem.)
|
||
|
||
| Phase | Reasoning demand | Recommended tier |
|
||
|---|---|---|
|
||
| 1 — Installs, EXE import | Mechanical | Standard (Opus-class) |
|
||
| 2 — Extraction pipeline | Well-specified coding vs byte-exact oracle | Standard |
|
||
| **3 — File-loader & overlay-map RE** | **Highest in project** — raw MIPS reading, US address derivation, RAM-dump experiment design | **Strongest available** |
|
||
| 4 — WSL setup | Mechanical; **order-flexible** (nothing in 1–3 depends on it — schedule it when the strong-model window is closed or limits are exhausted) | Any |
|
||
| 5 — splat config + build skeleton | Iterative debugging, loud error signals | Standard; strongest if available |
|
||
| **6 — Compiler fingerprint + first matches** | **Second highest** — ASPSX 2.56-vs-2.67 idiom discrimination is subtle. The fingerprint *analysis* is pure RE and can be front-run before Phase 4/5 exist if a strong-model window is closing | **Strongest available** |
|
||
| 7 — Matching at scale | Pattern grind against hard oracle | Standard; smaller tiers acceptable for bulk iteration (cost = wasted iterations, never wrong matches) |
|
||
|
||
Budget notes (Max 20x plan): long autonomous RE sessions are token-hungry; prefer single-agent flow with oracle checks for in-phase grind, reserving multi-agent fan-outs for verification moments. *Window note (2026-06-10): Fable 5 access expires ~2026-06-22 — priority order for that window: Phases 1→2 fast, then maximum depth on Phase 3, then Phase 6 fingerprint analysis if time remains; defer Phase 4 past the window.*
|
||
|
||
---
|
||
|
||
## Disc provenance (redump — canonical integrity reference)
|
||
|
||
The `SLUS_007.26` executable's own hash is not published anywhere (redump hashes disc *tracks*, not inner files), so dump authenticity is proven at the track level. `tools/bfm_extract/extract_exe.py --verify-disc` checks the Track-1 BIN against these:
|
||
|
||
| Track 1 (data) | Value |
|
||
|---|---|
|
||
| SHA1 | `b44f0f0a19936f23b26188b658e13201a6a9c211` |
|
||
| CRC32 | `c238191b` |
|
||
| MD5 | `838e9ecf8b4e4810f3f82228b40e77d8` |
|
||
| EXE build date | 1998-08-25 |
|
||
|
||
A Track-1 match proves the dump is the canonical redump dump, which transitively validates the extracted EXE (our own SHA1 `143dbb89f34491258bbc27810d0a12ec8b43a8dd`, 413,696 bytes — stable, independently reproduced from raw sectors). Audio tracks 2–4 SHA1s are in the research archive if ever needed.
|
||
|
||
---
|
||
|
||
## Known-unverified ledger (recheck and update in place)
|
||
|
||
| # | Item | Status |
|
||
|---|---|---|
|
||
| 1 | GhidrAssistMCP/psx_ldr 12.1 zips on Ghidra 12.1.2 | **UNVERIFIED** — pin 12.1 (§2.2) |
|
||
| 2 | MCP `types`/`struct` resolution of attached-archive (.gdt) types | **UNVERIFIED** — early test, §2.5 step 5 |
|
||
| 3 | GhidrAssistMCP struct-tool ergonomics under matching-decomp load | **UNPROVEN** — psxrecomp never exercised heavy struct creation |
|
||
| 4 | PCSX-Redux web-server port config field (8080 collision) | **TBD** on install (§3) |
|
||
| 5 | WSL distro is Ubuntu 24.04 (the single all-in-WSL host) | confirm via `/etc/os-release` (§4.1); username no longer load-bearing (no `wsl.exe --cd` targets) |
|
||
| 6 | binutils ≥2.38 regression on Ubuntu 24.04's shipped binutils | **VERIFY** (§4.5; 2.35 known-good per open-ribbon) |
|
||
| 7 | sha256 hashes of old-gcc 0.17 tarballs | **TBD** record at first download (§4.7) |
|
||
| 8 | `gp_value` in SLUS_007.26 header → -G0 vs -G8 | **TBD** (§5.3) |
|
||
| 9 | ASPSX tier for game code: 2.56 vs 2.67 | **OPEN** — Phase 6 empirical (§5.2 tell) |
|
||
| 10 | Cross-OS networking | **N/A under all-in-WSL** — MCP is local loopback (§4.2); no mirrored mode, firewall rule, or host-IP discovery |
|
||
| 11 | Canonical git remote URL (off-box push/pull backup) | **TBD** (§4.3) |
|
||
| 12 | Per-libnum stamp detail (raw-track scan reported 16 hits vs 12 genuine in extracted EXE — extracted-EXE scan is ground truth, see §5.1) | re-confirm via DetectPsyQ at import (§2.5 step 3) |
|
||
| 13 | Overlay load addresses (resident 0x800CDF58 / location 0x80128508, EXE ptr table ~0x62620) | **JP-only — re-derive for US** (owned by docs/memory-map.md) |
|
||
| 14 | Greenfield claim: decomp.me scratch search is script-blocked (Cloudflare) | **TBD** — one-time manual browser check for BFM scratches |
|