- docs/commit-map.tsv: 4,032 rows (ordinal of the ORIGINAL main -> rewritten hash, author/committer dates, subject); 1 pruned row of zeros (ordinal 1712, "session archive update"); 0 old hashes asserted; ordinal 1 unchanged by the rewrite (byte-identical) - resolve_tokens: 1,238 commit:NNNN tokens -> shortest-unique new hashes in 98 files (docs, phase-ends, logs, tool docstrings, 2 C comments, the A5 evidence logs); residue left as tokens: commit:1712 x4 (the pruned commit), commit:orphan-24 x2, commit:orphan-26, commit:orphan-35 (cited commits that exist in no lineage) - the rewrite (C4): filter-repo 2.47.0 on a bare clone of the C2 tip, 311 s, exactly 1 pruned, main 4,032 -> 4,031; the pre-rewrite history is mirrored in the private archive repo and in the local bundle - the proof (C5): verify_rewrite 4,031 pairs / 0 failures; absent_scan 0 offenders; gate_scan 0 offenders on the clone - adoption (C6): 100 text files differ at the tip, 0 purge paths, 0 added/deleted; leftover refs dropped; no gc yet - resolver skips tools/public_rewrite/ (its self-test fixtures are the token grammar, not citations); repo-local identity is the GitHub noreply address from here on; CURRENT_PHASE: C4–C7 logged, checkpoint -> NEXT = C8
9.2 KiB
Verify it yourself — the byte-identity contract, and how to check it with your own disc
BFM-decomp's claim is narrow and machine-checkable: 218 binaries (the main executable
SLUS_007.26, the resident engine blob, 138 location overlays and 78 code modules) rebuild byte-for-byte from the C insrc/, compared against the originals extracted from a redump-verified USA disc. This page is the recipe, the expected output of every step, and the record of the last full run. Nothing ROM-derived is in the repository (tools/audit_public.pyguards that), so every check below that touches game bytes needs YOUR disc dump. What CI proves without a disc is at the end.
0. What you need
| Item | Detail |
|---|---|
| The disc | Brave Fencer Musashi (USA) (SLUS-00726), redump layout: 4-track BIN/CUE. Track 1 SHA1 b44f0f0a19936f23b26188b658e13201a6a9c211, CRC32 c238191b. A Track-1-only dump also works (the 3 CD-audio tracks are then reported as unverified, never silently passed). |
| Linux (WSL2 Ubuntu 24.04 is what the project uses) | binutils-mipsel-linux-gnu, cpp-mipsel-linux-gnu, python3.12, git, make — make bootstrap prints the exact apt line and installs the rest (venv, submodules, the tracked gcc-2.7.2 cc1 tarballs, sha256-checked). |
| Disk / time | ≈3 GB for the build tree; the full clean rebuild takes ≈5 min on 16 cores (measured 4 m 18 s on a fresh clone, single user 45 CPU-min). |
The PsyQ SDK objects are optional and never needed for byte-identity: without them the main executable links the
project's own INCLUDE_ASM fallback tiles for Sony's library regions and is still byte-identical (this is the WITHOUT leg
of make sdk-dual, and it is what every fresh clone builds). See docs/SETUP.md §P33 B4 if you own the SDK.
1. The recipe (R22: a CLEAN rebuild — never an incremental one)
git clone --recurse-submodules https://github.com/Druthulu/BFM-decomp.git && cd BFM-decomp
make bootstrap # -> "check-env: OK" (one WARN: run make disc-extract)
mkdir -p disks && cp '/path/to/Brave Fencer Musashi (USA)'*.{bin,cue} disks/
make disc-extract # -> "disc-extract: OK" (extracts the disc, compares 1,801 files to the committed manifest)
make clean && make -j"$(nproc)" extract-all && make -j"$(nproc)" check-all
# -> "extract-all: 217 extracted, 0 failed of 217 (+ main, serial)"
# -> "check-all: 218 passed, 0 failed of 218" <- THE contract line
make sdk-dual # optional, only with the SDK objects -> "sdk-dual: OK — main 143dbb89… byte-identical WITH and WITHOUT the PsyQ objects"
make tools-health # -> "tools-health: OK — sigs fresh; corpus(+resident) + cdecl + binaries + report(lint+dedup) + cookbook-index all green."
make report # -> the three 100% lines below, "INCLUDE_ASM stubs : 0"
Every make target exits non-zero on any failure; read the exit code, not the presence of an output file (a failed build
leaves the previous binary in place — rule R53). make help lists every target with one line each.
What each step proves
| Step | Proves | Expected last line |
|---|---|---|
make bootstrap |
the toolchain is present and pinned (cc1 tarballs sha256 500a459b… / 42bb0df9…, maspsx submodule at the pinned commit, binutils) |
check-env: OK |
make disc-extract |
your disc is the redump image and extracts to the SAME 1,801 files the project was built against (extracted/retail/manifest.jsonl + .sha1, committed) |
disc-extract: OK (or PARTIAL: 1,798/1,798 code+data verified; 3 .DA unverified for Track-1-only) |
make extract-all |
splat splits every binary from the extracted payloads (asm/ and the linker scripts are regenerated, never edited) | extract-all: 217 extracted, 0 failed of 217 (+ main, serial) |
make check-all |
every binary's build SHA1 equals config/check.<alias>.sha — main 143dbb89f34491258bbc27810d0a12ec8b43a8dd |
check-all: 218 passed, 0 failed of 218 |
make sdk-dual |
main is byte-identical both with Sony's real objects linked and with the fallback tiles | sdk-dual: OK — … |
make tools-health |
the two independent function-boundary oracles agree (0 phantom / 0 truncated / 0 pad-tail), the dedup registry validates (2,220 groups / 0 failures), the audits and derived indexes are fresh | tools-health: OK — … |
make report |
the three progress metrics, source-derived | FLEET fn-count byte-ident: 363214 / 363214 = 100.00% · FLEET instr-weighted : 13492113 / 13492113 = 100.0% · FLEET distinct-code(uniq): 5820205 / 5820205 = 100.0% · MAIN game-code weighted : 45150 / 45150 = 100.0% |
What is NOT our C (stated plainly)
- 1,256 functions of the main executable are Sony's PsyQ library objects, linked byte-identical from the SDK when you
have it and represented by assembly fallback tiles when you do not (
src/lib*.c,src/apicard*.c—INCLUDE_ASMstubs whose bytes come from the disc). Reimplementing Sony's libraries from source is out of scope. - 5 functions fleet-wide are hand-written assembly in the original (
config/verbatim_manifest.json, PERMANENT rows); they are kept as verbatim__asm__bodies, audited bytools/verbatim_check.py --strict. - Everything else — every game-code function in all 218 binaries — is C.
2. The last recorded run
The project's own full run is recorded, log by log, under .run/P33/verify/ (tracked: NN_<step>.log, each ending in
EXIT=<rc> and a timestamp, plus SUMMARY.md), produced by tools/verify_contract.sh on the committed tree. The table
below is copied from that SUMMARY.md; regenerate it with the script, never by hand.
- Tree:
3e0ecfacc(2026-09-06) · Host: Linux 6.18.33.1-microsoft-standard-WSL2, 32 CPUs, JOBS=16 · Run: 2026-09-07T03:11:58Z
| # | Step | EXIT | wall | contract line |
|---|---|---|---|---|
| 00 | tree |
0 | 1s | HEAD 3e0ecfacc 2026-09-06 fix(phase-33): audit_frontier's LINKED-stub count reads the Stub records (the refer |
| 01 | check-env |
0 | 0s | check-env: OK — Phase-4 toolchain ready. |
| 02 | family-hseq |
0 | 11s | family_hseq regenerated: 217 binaries scanned, 0 open instances |
| 03 | r22-clean-fleet |
0 | 157s | check-all: 218 passed, 0 failed of 218 |
| 04 | sdk-dual |
0 | 28s | sdk-dual: OK — main 143dbb89f34491258bbc27810d0a12ec8b43a8dd byte-identical WITH and WITHOUT the PsyQ objects (maps: .run/P33/verify/main_ |
| 05 | tools-health |
0 | 356s | tools-health: OK — sigs fresh; corpus(+resident) + cdecl + binaries + report(lint+dedup) + cookbook-index all green. |
| 06 | audit-frontier |
0 | 9s | audit-frontier: reference = corpus.stubs over 218 binaries -> 1258 open stubs |
| 07 | audit-disc |
0 | 36s | UNCLAIMED code payloads: 0 |
| 08 | report |
0 | 211s | FLEET distinct-code(uniq): 5820205 / 5820205 = 100.0% (90984/90984 unique fns; the DISTINCT-RE number) |
Every NN_<step>.log is tracked under .run/P33/verify/; the tools-health log also carries sig-main-oracle: main is now INDEPENDENT — 0 in-domain stubs, 0 PHANTOM, 0 TRUNCATED, 0 PAD-TAIL and the report INCLUDE_ASM stubs : 0, Open near-misses: 0. Total wall 14 min on 32 CPUs.
The C3 preparatory commit that followed removed the ROM-derived paths from the index with git rm --cached — no
tracked-content byte changed, so this run's evidence holds for that tip as well; C8 repeats the run on the rewritten tree.
The same run was performed after every banked batch of Phases 30–32 (218/218 at every one; the P32 close run is
.run/P32/t4e/r22_check.log) and the history-rewrite of the public flip is followed by one more (C8) on the adopted tree —
a content-preserving rewrite changes no tracked byte, and that run proves it.
3. What CI proves without the disc
.github/workflows/no-rom.yml runs on every push and pull request:
- audits — no ROM-derived bytes, purge paths or >50 MiB files among the tracked files (
tools/audit_public.py, whose ROM-hash set is derived from the committed manifest + everyconfig/check.*.sha); every source is text with portable includes; the verbatim manifest has no drift; the derived indexes (cookbook, Ghidra roster) are fresh; the LZSS decoder's unit tests; no stale symbol references. - compile-only — every eligible translation unit (4,170 of 4,287; the rest are the library tiles that
.includedisc-derived assembly) goes through cpp → gcc-2.7.2cc1→ maspsx → GNUaswith the Makefile's exact flags. A PR compiles four representative binaries (≈1 min); the whole fleet runs weekly and on demand.
CI cannot compare bytes to the originals — that is your step 1. The two together are the full claim.
4. Reverse-engineering artifacts you can also regenerate
The Ghidra project is not in the repository (its database embeds the game's bytes). Its hand-authored content is tracked
as text under config/ghidra/ (ROSTER.md lists the programs) and tools/ghidra_rebuild.sh <program> --proof rebuilds a
program from your disc + the symbol files + that text and proves the result equals it (PROOF PASS). The 28 RAM images
the memory map was derived from are local-only; dumps/CHECKSUMS.sha1 records their identity.