Files
BFM-decomp/docs/wiki/Overlays-and-modules.md
T

6.1 KiB
Raw Blame History

Overlays and modules — the 218 binaries and the disc

The disc

Brave Fencer Musashi (USA) is a 4-track disc (Track 1 = data, Tracks 2–4 = CD audio). Track 1 holds 27 root files and no directories: the 413,696-byte executable SLUS_007.26, SYSTEM.CNF, LIST.CD (the file-location table the loader indexes), and the container files MAIN.CD and SC01.CD … SC07.CD (one per chapter), plus three .DA entries pointing into the audio tracks. Each .CD is a table of contents over PAC archives; each PAC entry is a typed payload, and type 4 is an LZSS-compressed stream with the game's own termination semantics (the reference tool's differ — the extractor implements the game's). The full formats, verified against the bytes, are in docs/formats.md; the extractor is tools/bfm_extract/ and its output is the 1,801-file manifest your make disc-extract is checked against.

PAC type payloads code-bearing what
0, 2, 3, 6, 7, 8 885 0 graphics, blobs, structured data
1 166 78 raw (uncompressed) payloads: the resident engine, the code modules, three overlays stored uncompressed
4 138 138 LZSS location overlays

"Code-bearing" is decided by make audit-disc (tools/disc_audit.py): whole-payload classification at both the raw and the decompressed layer, a residue-0 partition over every byte of the disc, and claimed-by derived from the per-binary contracts — so a code payload nobody onboarded cannot hide behind a green fleet (the byte gate is blind to what it was never asked to build — rule R34). At the close: UNCLAIMED 0 of 220 code payloads. The audit exists because the count was wrong twice: 4 overlays that put their code at PAC entry 1 instead of 0 were invisible for a month, and a first sweep with a 4,096-word window counted 39 hidden modules where the honest number was 78 (docs/disc-completeness.md, docs/disc-ledger.md).

The 218 binaries

Kind Count Alias Loads at Source
The executable 1 main 0x80010000 (PS-X EXE header; t_size 0x64800) SLUS_007.26
The resident engine 1 resident 0x800CEDF8 (the boot slot) MAIN.CD/FILE_010/1.1, 365,404 B, type 1
Location overlays 141 ov_<DISC>_<FILE> 0x80128158 — the shared overlay slot, position-locked 138 type-4 LZSS payloads + 3 stored uncompressed (ov_MAIN_012, ov_SC02_037, ov_SC03_107)
Code modules 75 md_<DISC>_<FILE> their own slots: module slot A 0x800CAE08 (MAIN/13–41), slot B 0x800CCB1C (MAIN/42–47), the boot slot 0x800CEDF8 (boot trio + the two opening-demo modules), the SC07 pair 0x801A00D8, four per-chapter script-module slots, and the five last payloads at bases derived from their own bytes type-1 payloads across MAIN.CD and the chapter discs

The registries config/overlays.mk and config/modules.mk (generated by the onboarding scripts) give each binary its payload path, load address, source and build directories; config/splat.<alias>.yaml its split; config/check.<alias>.sha its contract. One parameterized pipeline builds any of them (make check BINARY=<alias>). The overlays share one symbol space because they share one address space — The dedup engine is the consequence.

How the load addresses were proven

Every address in docs/memory-map.md carries its source and its verification status (rule G5: never a JP address assumed for US, never an overlay address treated as a static symbol until the map says so).

  1. Runtime proof (Phase 3). The loader was reverse-engineered — a hand-rolled CD reader driven by the location table and a resource map, with an LZSS staging buffer at 0x80079A70 — and the resident and the first overlay were proven by byte-comparing a live PCSX-Redux RAM dump against the extracted, decompressed payloads (sha1-equal prefixes of 18,788 and 389,400 bytes at the derived addresses). A finding is verified only with ≥ 3 consistent datapoints or a controlled before/after diff (R10). The 28 RAM images are catalogued in dumps/INDEX.md.
  2. Static derivation (Phase 30). For 46 of the 78 unclaimed payloads the addresses were readable without an emulator: the executable's load-destination pointer table, the boot loaders' literal table operands, and two index tables inside the resident that route MAIN/13–41 and MAIN/42–47 to the two module slots — corroborated by base voting over shared code (about 500:1) and jal alignment.
  3. The emulator tour (Phase 30/31). The 28 per-chapter script modules and a few stragglers were captured live through the debug menu, each byte-proven at its slot.
  4. From their own bytes (Phase 32). The last five payloads — never seen loaded — were placed by tools/payload_base_evidence.py: score a bounded candidate list on how many self-calls and function-pointer-table entries land exactly on the payload's own function starts (controls: seven known modules re-derive their proven bases from the payload alone). An all-assembly first build is a null oracle for fine base errors; each base was then proven by the first C bank whose body calls an internal sibling.

Working on one binary

make extract BINARY=ov_SC01_077 && make -j8 check BINARY=ov_SC01_077      # split, build, compare to check.ov_SC01_077.sha
make -j8 check BINARY=main                                                # main: read the exit code; gate changes by a clean rebuild

Some translation units are carved: a function whose jump table sits in a rodata island between two others is split into its own file at segmentation time, -O0 regions are carved out of -O2 files, and the carve state — the yaml and the registry lines — belongs to the binary that owns it (a gate commit carries only its own binary's lines; R60). tools/split_indicator.py, in make tools-health, refuses a carve that cannot build.