# On-Disc File Formats — Brave Fencer Musashi (SLUS-00726 USA) > **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 canonical specification of > the disc image layout and the game's container/compression formats (.CD, PAC, LZSS, SQV). > Every entry is tagged with its provenance; anything not byte-verified against the local > US dump is explicitly marked **UNVERIFIED** or **TBD**. RAM addresses are owned by > `docs/memory-map.md` (authoritative); they are cross-referenced here, never redefined. **Primary sources reconciled in this file:** | Source | What it provides | Trust level | |---|---|---| | Local dump `Brave Fencer Musashi (USA)\` (4×.bin + .cue) | All `verified-local` facts (re-parsed 2026-06-10) | Ground truth | | `tools/brave-CUE/brave.c` + `brave.txt` (CUE, 2012-2013, GPLv3) | Reference extractor: .CD TOC, PAC header, LZSS decoder | Authoritative for tool semantics; **diverges from game semantics on LZSS termination (§4.4)** | | AiroRom wiki MIPS listing (pleonex) | Game-side LZSS decompressor, byte-verified vs local US EXE | Verified (see memory-map §2.1) | | jywjyw/bravefencer-hack (Java repacker, 2018) + `doc/note.md` | LIST.CD structure, PAC details, working recompressor, overlay map | **JP (SLPS-01490)** unless noted — re-verify for US | | romhacking.net thread 15730 (Vehek, Dark Fulgore, Auryn, 2013-2014) | LZSS termination/bias discoveries, US datapoints | Reported, partially re-verified | --- ## 1. Disc layout ### 1.1 BIN/CUE — 4 tracks From the local `.cue` and track binaries (verified-local): | Track | Mode | File size (bytes) | Sectors (2352 B raw) | Content | |---|---|---|---|---| | 01 | MODE2/2352 | 364,846,944 | 155,122 | Data (ISO9660 volume `MUSASHI`) | | 02 | AUDIO (CD-DA) | 10,678,080 | 4,540 | `ST01_13A.DA` (see §1.4); INDEX 00 at 00:00:00, INDEX 01 at 00:02:00 | | 03 | AUDIO (CD-DA) | 7,328,832 | 3,116 | `ST01_13B.DA`; same index layout | | 04 | AUDIO (CD-DA) | 33,167,904 | 14,102 | `DUMMY_DA.DA`; same index layout | Volume space size in the PVD = 176,880 sectors = 155,122 + 4,540 + 3,116 + 14,102 exactly (the ISO volume spans all four tracks) (verified-local). ### 1.2 Track 1 sector geometry - Raw sectors are 2352 bytes (MODE2). **User data (2048 bytes) begins at raw-sector offset +24** (12-byte sync + 4-byte header + 8-byte Mode 2 Form 1 subheader). - Byte offset of LBA *n*'s user data within `(Track 1).bin` = `n * 2352 + 24`. - PVD at LBA 16; root directory record at LBA 22 (2048 bytes) (verified-local). ### 1.3 ISO root inventory — 27 files, no directories Re-parsed from the raw image 2026-06-10 (verified-local). All names carry the `;1` version suffix on disc; LBA = 2048-byte-sector index from track start; sizes are exact byte lengths from the directory records. | File | LBA | Size (bytes) | Notes | |---|---|---|---| | SYSTEM.CNF | 23 | 68 | Boot config (§1.5) | | SLUS_007.26 | 24 | 413,696 | PS-X EXE (§1.6) | | MUSA.ID | 226 | 9 | Disc-ID check file; contents `MUSASHI\r\n` | | LIST.CD | 227 | 4,096 | Concatenated .CD TOCs (§2.3) | | MAIN.CD | 229 | 5,988,352 | Resident/global archive, 49 sub-files (§2.2) | | LOGOA.STR | 3,153 | 3,080,192 | FMV (out of scope, §7) | | LOGOB.STR | 4,657 | 3,080,192 | FMV | | SC01.CD | 6,161 | 20,078,592 | Chapter 1 scenario archive | | ST01.STR | 15,965 | 12,795,904 | FMV | | OPEN.STR | 22,213 | 22,724,608 | FMV | | SC02.CD | 33,309 | 22,126,592 | Chapter 2 scenario archive | | ST02.STR | 44,113 | 21,430,272 | FMV | | SC03.CD | 54,577 | 64,569,344 | Chapter 3 scenario archive (largest) | | ST03.STR | 86,105 | 8,028,160 | FMV | | SHOPS01.STR | 90,025 | 7,536,640 | FMV | | OUT.STR | 93,705 | 1,867,776 | FMV | | SC04.CD | 94,617 | 19,425,280 | Chapter 4 scenario archive | | ST04.STR | 104,102 | 17,825,792 | FMV | | SC05.CD | 112,806 | 16,498,688 | Chapter 5 scenario archive | | ST05.STR | 120,862 | 9,240,576 | FMV | | SC06.CD | 125,374 | 23,928,832 | Chapter 6 scenario archive | | ST06.STR | 137,058 | 18,612,224 | FMV | | SC07.CD | 146,146 | 12,734,464 | Chapter 7 scenario archive | | END.STR | 152,364 | 5,341,184 | FMV | | ST01_13A.DA | 155,272 | 8,990,720 | CD-DA pointer → track 2 (§1.4) | | ST01_13B.DA | 159,812 | 6,074,368 | CD-DA pointer → track 3 | | DUMMY_DA.DA | 162,928 | 28,573,696 | CD-DA pointer → track 4 (padding audio) | Observations (verified-local): - **12 STR files** (not 13 as one earlier planning summary said — that was a miscount; this table is the corrected, re-verified inventory). Tally: 12 STR + 3 `.DA` + 12 others (SYSTEM.CNF, SLUS_007.26, MUSA.ID, LIST.CD, MAIN.CD, SC01–SC07.CD) = 27. - STR movies are interleaved between the chapter archives in ascending LBA order (seek-distance optimization). - All `.CD` sizes are exact multiples of 2048. - Layout is contiguous: e.g. SLUS_007.26 occupies LBA 24–225 (202 sectors), MUSA.ID follows at 226; MAIN.CD ends exactly where LOGOA.STR begins. ### 1.4 `.DA` files = directory entries pointing into the CD-DA tracks Verified by arithmetic in-session (2026-06-10): each `.DA` extent LBA equals the first INDEX 01 sector of an audio track, and each size equals `(track sectors − 150 pregap) × 2048` (the directory records use 2048 B/sector accounting even though the underlying sectors are 2352-byte audio): | File | Extent LBA | Track start (cumulative) | + 150 pregap | Size check | |---|---|---|---|---| | ST01_13A.DA | 155,272 | track 2 @ 155,122 | = 155,272 ✓ | (4,540−150)×2048 = 8,990,720 ✓ | | ST01_13B.DA | 159,812 | track 3 @ 159,662 | = 159,812 ✓ | (3,116−150)×2048 = 6,074,368 ✓ | | DUMMY_DA.DA | 162,928 | track 4 @ 162,778 | = 162,928 ✓ | (14,102−150)×2048 = 28,573,696 ✓ | So the `.DA` files contain no data inside track 1; they are the ISO-level handles the game uses to seek CD-DA audio. They are **out of scope** for the extraction pipeline (§7). ### 1.5 SYSTEM.CNF (68 bytes, verified-local) ``` BOOT = cdrom:\SLUS_007.26;1 TCB = 4 EVENT = 16 STACK = 801ffff0 ``` ### 1.6 SLUS_007.26 — PS-X EXE header (verified-local) | Field | Value | Notes | |---|---|---| | magic | `PS-X EXE` | | | `pc0` | 0x80010000 | Entry point | | `gp0` | 0x00000000 | No $gp in header ⇒ likely −G0; confirm in Phase 6 | | `t_addr` | 0x80010000 | Image spans 0x80010000–0x80074800 | | `t_size` | 0x64800 | File = 0x800 header + 0x64800 text = 0x65000 = 413,696 bytes ✓ | | `sp` | 0x801FFFF0 | Matches SYSTEM.CNF STACK | The EXE is tiny (~400 KB) relative to a ~340 MB game: the bulk of code/script lives inside the `.CD` archives as LZSS-compressed PAC type-4 overlays (§3, §4; memory-map §4). ### 1.7 The `\DEBUG.BIN;1` ghost reference The EXE's CD-path string table (`CdPathTable`, base **0x80062C24**, 23 entries, 0x30-byte stride — see memory-map §2.2) contains the string **`\DEBUG.BIN;1` at 0x80063044** (verified-local), adjacent to `\MUSA.ID;1` at 0x80063014. **DEBUG.BIN does not exist on the retail ISO** (see §1.3). A surviving code path therefore references a debug loader file that was stripped from the disc — a prime RE anchor (xref the string to find the debug loader; relates to the TCRF L3 debug menu, memory-map §5). Also note: the table covers LIST.CD, MAIN.CD, SC01–SC07.CD and ten of the STR movies, but `LOGOA.STR`/`LOGOB.STR` and the three `.DA` files are absent from it — referenced elsewhere (**TBD**, memory-map open question #14). --- ## 2. .CD container format ### 2.1 TOC structure (verified-local for MAIN.CD; brave.c is the reference parser) A `.CD` file begins with a table of contents at offset 0: | Offset | Type | Meaning | |---|---|---| | +0x00 | u32 LE | `count` — number of sub-files (MAIN.CD: 0x31 = 49) | | +0x04 | u32 LE | zero | | +0x08 + 8*i | u32 LE | sub-file *i* start, **in 2048-byte sectors relative to the start of the .CD file** | | +0x0C + 8*i | u32 LE | sub-file *i* length in bytes (unpadded) | - **The first field of each entry is NOT a disc LBA.** `brave.txt` calls it "LBA", but the code in `brave.c::Extract()` proves it is file-relative (`buffer + entry_sector * 0x800`). Do not add the `.CD` file's own disc LBA when splitting. - The TOC and every sub-file are sector-aligned (0x800), zero-padded, and contiguous; the first sub-file starts at sector 1 (offset 0x800). - MAIN.CD and SC01–SC07.CD use this identical format — one code path in every known tool, no per-file special-casing (brave.c; confirmed by jywjyw's `Conf.java` rebuilding all 8 with the same `CdRebuilder`). ### 2.2 Roles of each .CD file | File | Role | Sub-file count | Provenance | |---|---|---|---| | LIST.CD | Concatenated TOCs of the other 8 `.CD`s (§2.3) — **not itself a normal container** | n/a | jywjyw (JP) + partial local check | | MAIN.CD | Global/resident archive: loader-resident script blob (`FILE_010` PAC entry index 1, type 1), fonts, shared assets | **49** (0x31, verified-local US; same count in JP) | local + jywjyw | | SC01.CD | Chapter 1 scenario archive (per-location PAC chains: scripts/overlays, graphics, VAB audio, SQV music) | 86 (0x56) — **JP value, UNVERIFIED for US** | jywjyw | | SC02.CD | Chapter 2 scenario archive | 45 — reported for US by Auryn (forum 15730), **UNVERIFIED locally** | thread 15730 | | SC03–SC07.CD | Chapter 3–7 scenario archives | **TBD** | — | Sub-files inside a `.CD` are either PAC chains (§3) — identified by the `PAC\0` magic — or non-PAC files, which per `brave.txt` are SQV (MIDI) archives (§5). Anything without either signature is unknown (**TBD**). JP reference sizes (jywjyw `Conf.CD_JAPAN_SIZE`, for cross-version diffing only — the US sizes are in §1.3): MAIN 6,205,440; SC01 20,615,168; SC02 6,580,224; SC03 78,168,064; SC04 20,764,672; SC05 16,502,784; SC06 23,908,352; SC07 12,730,368. ### 2.3 LIST.CD (4,096 bytes = 0x1000) Per `brave.txt`: "contiene las cabeceras de todos los archivos CD" (contains the headers of all the CD archives). The exact structure, per jywjyw's `ListCdWriter.java` + `CdHeader.trimBuild()` (JP repacker source): - LIST.CD = the 8 TOCs of MAIN.CD, SC01.CD … SC07.CD **in that order**, each **trimmed** to exactly `8 + 8*N` bytes (u32 count + u32 zero + N entries — i.e. the TOC without its sector padding), **concatenated back-to-back with no alignment between tables**, written into a fixed 0x1000-byte (2-sector) buffer. - Purpose (jywjyw `note.md`): loaded into RAM at game start so the engine can locate any sub-file in any `.CD` without re-reading each archive's header sector. US RAM cache location **TBD** (memory-map §3.2 `listCd_ramCache`, open question #3). **Verification status:** locally confirmed only that LIST.CD *begins with* an exact copy of MAIN.CD's TOC (first bytes identical to MAIN.CD sector 0). The full trimmed-concatenation layout (SC01–SC07 portions, their order, the no-alignment packing) is from the JP repacker and is **UNVERIFIED against the US disc** — verify by recomputing the 8 trimmed TOCs from the US `.CD` files and diffing against LIST.CD (cheap Phase-2 check). ⚠️ CUE's BRAVE tool does **not** special-case LIST.CD. Because LIST.CD *starts* with a valid TOC, running BRAVE on it parses MAIN.CD's table and then reads garbage sector offsets inside a 4 KB file. Never feed LIST.CD to the extractor; treat it as derived data (regenerable from the other 8 TOCs). --- ## 3. PAC archive format A `.CD` sub-file whose first u32 is `0x00434150` (`"PAC\0"` LE) is a **PAC chain**: a sequence of entries, each with its own 0x800-byte header block immediately followed by its payload, repeated until an entry's last-flag is set. ### 3.1 Entry header (0x800-byte block; fields per brave.c/brave.txt + jywjyw PacHeader.java) | Offset | Type | Meaning | Status | |---|---|---|---| | +0x00 | u32 LE | Magic `0x00434150` = `"PAC\0"` | verified (tool + local) | | +0x04 | u8 | **Type** (0–8), selects payload interpretation (§3.2) | verified | | +0x05 | u8 | **Last-entry flag**: 1 = last entry in chain, 0 = more follow | verified | | +0x06 | u16 | Zero | verified | | +0x08 | u32 LE | **Unknown** — **TBD**. Possibly a decompressed-size hint, checksum, or load parameter. `???` in brave.txt; jywjyw's repacker preserves it verbatim (via `headers.bin`) without interpreting it. Check against the game's loader (memory-map open question #7) | **TBD** | | +0x0C | u32 LE | **Length including this 0x800 header** — payload = `len − 0x800`, payload starts at entry+0x800 | verified | | +0x10 … +0x7FF | — | Not interpreted by any known tool. jywjyw preserves the *entire* 0x800 header verbatim, implying it may contain meaningful non-zero bytes | **UNVERIFIED** | ### 3.2 Type table Sources: `brave.txt` (CUE) + jywjyw `note.md`. | Type | Payload | Notes | |---|---|---| | 0 | Graphics | Mostly 32×32 image blocks (8bpp = 64×32, 4bpp = 128×32 per jywjyw) | | 1 | Graphics / **uncompressed script** | The memory-resident MAIN.CD `FILE_010` entry "1.1" and the shop-dialog "2.1" scripts are type 1 | | 2 | VAB header (`.VH`) | PsyQ sound bank header | | 3 | VAB body (`.VB`) | PsyQ sound bank sample data | | 4 | **LZSS-compressed data** (§4) | Location scripts **containing embedded MIPS code** = the game's overlays. The only compressed type | | 5 | — | Unused per CUE ("---") | | 6 | Unknown | **TBD**. jywjyw notes all of a type-6 entry's data fits within its first 0x800 bytes | | 7 | Unknown | **TBD** | | 8 | Graphics | | Only type 4 is compressed; everything else is stored raw. ("All .CD files are LZSS-compressed" — an early planning claim — is **wrong**; compression is a per-PAC-entry type flag, with no magic and no size heuristic.) ### 3.3 Chaining (brave.c::UnPAC, verbatim semantics) ``` pos = 0 do: type = buf[pos+0x04]; last = buf[pos+0x05] len = u32le(buf + pos + 0x0C) # includes the 0x800 header payload = buf[pos+0x800 : pos+len] # len − 0x800 bytes pos += (len + 0x7FF) & ~0x7FF # next entry at sector alignment while not last ``` Entries are sector-aligned, zero-padded between payload end and the next 0x800 boundary. --- ## 4. LZSS variant (PAC type-4 payloads) — CRITICAL SECTION Two authoritative implementations exist and they **disagree on termination** (§4.4): the game's own MIPS decompressor (US EXE **0x80018730**, byte-verified against the AiroRom listing — memory-map §2.1) and CUE's `brave.c::Decode()` (tool reference). Everything below is reconciled from both plus Vehek's findings in romhacking thread 15730. ### 4.1 Parameters | Parameter | Value | Notes | |---|---|---| | `POS_BITS` | 10 | Position field width | | `N` (ring size) | 1024 bytes (`1 << 10`) | Game keeps the ring in **scratchpad 0x1F800000** (memory-map §3.1) | | `THRESHOLD` | 1 (CUE's naming) | jywjyw's encoder calls it 2 — naming difference only; both agree real match length = 6-bit field + 2 | | Max match `F` | 65 bytes (`(1 << 6) + 1` + the loop's `<=`) | Copy lengths range **2..65** | | Ring initial state | Zero-filled, write index `r = 0` | Encoder-side origin is index **1** (§4.3, §4.6) | | Decompressed size | **No size field anywhere** | CUE grows the output buffer dynamically (start 128 KB, +64 KB steps); the game decodes until the terminator | ### 4.2 Flag engine (LSB-first with 0xFF00 sentinel) From `brave.c`, verbatim: ```c if (!((flags >>= 1) & 0xFF00)) flags = 0xFF00 | encoded[enc_size++]; if (flags & 1) { /* literal */ } else { /* match */ } ``` - A flag byte is loaded into the low 8 bits with sentinel `0xFF00` ORed above it; each step right-shifts once and tests bit 0. After 8 shifts the sentinel is exhausted (`& 0xFF00 == 0`) and the next flag byte is fetched. Net effect: **flag bits are consumed LSB-first**, one flag byte governs the next 8 tokens. - The game's MIPS routine implements the same order with a mask walking 0x01→0x80 against a held token byte (AiroRom listing) — equivalent semantics, verified. - Flag bit **1 = literal**, **0 = match reference**. ### 4.3 Literal and match paths **Literal (flag bit = 1):** read 1 input byte → append to output **and** write to `ring[r]`; `r = (r + 1) & 0x3FF`. **Match (flag bit = 0):** read u16 **little-endian** `code`, then: ``` pos = code & 0x3FF // low 10 bits — ABSOLUTE ring index (NOT a relative offset!) len = (code >> 10) + 1 // THRESHOLD = 1 for i in 0 ..= len: // len+1 iterations => 2..65 output bytes b = ring[(pos - 1 + i) & 0x3FF] // note the −1 bias output(b); ring[r] = b; r = (r + 1) & 0x3FF ``` - **Absolute index, not distance:** the 10-bit `pos` addresses the ring directly. Generic LZSS tools (relative-distance decoders) will NOT work on this format. - **The −1 bias:** stored positions are ring-index+1, because the game's *encoder* starts writing at ring index 1 (Vehek: "the starting position in the buffer appears to be 1"). An off-by-one here corrupts every decompressed overlay. Validate any new implementation by diffing a decompressed type-4 blob against a live emulator RAM dump at the overlay load address (memory-map §4, open question #1). - The ring write pointer `r` advances on **every** output byte (literals and match bytes alike) and is independent of `pos`. ### 4.4 Termination — GAME vs TOOL discrepancy (load-bearing) | Implementation | Stop condition | Consequence | |---|---|---| | **Game** (MIPS @ 0x80018730, US-verified; Vehek Aug 2014) | A match code whose **low 10 bits == 0** (`pos == 0`) terminates the stream. Streams typically end with bytes `00 00`. The PAC header length field has **no effect** on the game's decompressed size (Vehek-tested) | `pos == 0` is reserved ⇒ legal references are `pos` 1..1023, i.e. biased start indices 0..1022 | | **CUE's brave.c** | No terminator check; stops when the input length (`PAC len − 0x800`) is consumed | Trusts the header length; never sees `pos == 0` specially | **Project rule:** our extractor must implement **game semantics — stop on `pos == 0`** — with a **length sanity cross-check**: after hitting the terminator, verify input consumption is consistent with the PAC length field (`len − 0x800`, allowing trailing padding), and warn on any stream that exhausts its input without a terminator or overruns it. Divergence between the two conditions on real game data has not been observed but has also not been exhaustively tested — treat any warning as a format discovery, not noise (**UNVERIFIED** whether all retail streams satisfy both conditions simultaneously). Additional tool caveat: `brave.c`'s `ring[]` is a global that is never re-zeroed between files (only `r` resets), so CUE's tool is not a strict oracle for streams that reference ring positions not yet written; the game expects a zero-filled ring at stream start. ### 4.5 Game-side streaming state machine (resumable, sector-fed) The game's decompressor consumes input in **0x800-byte CD-sector chunks** and is a **resumable 5-state machine** so decompression can suspend mid-token at a sector boundary. All addresses US-verified; full ledger in memory-map §2.1/§3.1: | Item | Address (US) | |---|---| | Decoder entry | 0x80018730 (`LzssDecodeSector`); returns at 0x80018910 | | State jump table | 0x80072A30 (`00000000 FFFFFFFF` then 5 state pointers) | | State variable | 0x800C7D24 (runtime BSS) | | Persistent state block | 0x800747A0 mask, 0x800747A4 token byte, 0x800747AC output ptr, 0x800747B0 ring index, 0x800747B4 partial 16-bit match code | | Ring buffer | 0x1F800000 (scratchpad, 1024 B) | | Sector staging buffer | JP 0x80078BD0–0x800793D0 — **JP-only, US equivalent TBD** (memory-map §3.2) | States: 0 finish, 1 init token ring, 2 check token bit, 3 process match, 4 next token bit. The partial-code slot exists precisely because a 16-bit match code can straddle a sector boundary. ### 4.6 Recompression — NOT byte-stable (encoder constraints) - LZSS parsing is ambiguous (literal-vs-match and match-length choices); jywjyw's working binary-search-tree encoder explicitly notes "recompressed 0.4 script not equals original". **No recompressor is expected to reproduce Square's original bitstream.** - ⇒ **Compressed type-4 payloads are opaque binary for build verification.** Only the *decompressed* overlays can be matched; the matching build treats compressed blobs as binary data. Repacking/injection is a **Gen3 parking-lot concern**. - Any future encoder MUST: (a) **never emit a `pos == 0` reference mid-stream** — stock Okumura-style LZSS output does, and the game treats it as end-of-stream (Vehek's failed 2013 attempt); (b) terminate the stream with a `pos == 0` pair; (c) start its ring origin at index 1 (jywjyw's `Compresser.java` does exactly this — it also documents the encoded byte order: byte0 = `pos & 0xFF`, byte1 = `(len-2) << 2 | pos >> 8`). --- ## 5. SQV format (sequenced music archives) `.CD` sub-files that are not PAC chains; signature `".sqv"`. Source: `brave.txt` (CUE) — **not yet locally re-verified against extracted US files (UNVERIFIED)**. | Item | Spec | |---|---| | Magic | 4 bytes `".sqv"` at +0x00 | | Header u32s | Up to **3** u32 offsets to `MThd` (Standard MIDI File) sub-files. Each value is an offset **to which 4 must be added** (+4 bias); a value of **0 means the archive holds a single MThd** (no further offsets) | | Header alignment | 16 bytes, zero-padded | | Member alignment | 4 bytes, zero-padded | | VAB | A VAB file at **fixed offset 0x7000**, after the MThd data | Pairs with PAC types 2/3 (VAB header/body) for the game's sound banks; whether the driver is stock PsyQ SEQ/VAB or Square-custom is an open research question (memory-map context). --- ## 6. Extractor implementation notes (Phase 2 pipeline) 1. **Naming collision in CUE's tool — do not copy.** `brave.c` saves sub-files as `.dir/FILE_%03d` (fine) but names PAC chain entries `%03d` **by type byte** (`sprintf(name, "%s.dir/%03d", filename, num)` where `num` = type). Two same-type entries in one chain **silently overwrite each other** on extraction. **Our extractor uses `{index}.{type}` naming** (jywjyw's `CdSplitter` convention: `0.4`, `1.1`, `2.1` …) so every entry survives and the type remains visible. 2. **Decode rule:** decompress **only** type-4 entries, using game termination semantics with the length cross-check (§4.4). Save all other types raw. Save non-PAC sub-files as-is (SQV etc.). 3. **Never run the pipeline on LIST.CD** (§2.3); instead verify LIST.CD by *regenerating* the trimmed-TOC concatenation from the 8 archives and diffing. 4. **Round-trip / repack reference (Gen3):** `github.com/jywjyw/bravefencer-hack` (Java/Maven, 2018) — `CdHeader.java` (TOC rebuild: count, entries from sector 1, `addr += paddedLen/0x800`), `PacHeader.java` (full 0x800 header incl. unknown bytes), `hack/Compresser.java` (game-compatible LZSS encoder; LGPL 2.1 header, adapted from JSword), `hack/CdRebuilder.java` (rebuilds all 8 .CDs, preserves original PAC headers via `headers.bin`, recomputes length fields, pads to 0x800, warns if a rebuilt .CD outgrows the original), `hack/ListCdWriter.java` (LIST.CD regeneration), `dump/Uncompresser.java` + `dump/CdSplitter.java` (decoder/splitter with the `{index}.{type}` naming). Its `doc/note.md` is the best third-party memory/format document (JP-based). 5. **Cross-validation target:** the pipeline's raw sub-file and decompressed type-4 outputs must be byte-identical to `tools/brave-CUE/brave.exe` outputs *where CUE's tool is correct* (i.e. modulo the naming collision and modulo any stream where the termination semantics diverge — log and inspect those). 6. **Licensing:** `brave.c` is GPLv3 (study/port is fine; note license if vendoring); `Compresser.java` carries LGPL 2.1. ## 7. Out of scope (for now) | Format | Status | |---|---| | `.STR` (12 files) | Standard PS1 STR movie streams (Mode 2 Form 2 interleaved video/XA). No game-specific container suspected (**UNVERIFIED** — assumed standard). Not part of the extraction/matching pipeline | | `.DA` (3 files) | CD-DA track handles (§1.4) — no track-1 payload; nothing to extract | ## 8. Open questions specific to formats | # | Question | Attack | |---|---|---| | F1 | PAC types 6/7 semantics (CUE "???", jywjyw "??") | Ghidra: PAC-header parser in the loader | | F2 | PAC header +0x08 u32 meaning (decompressed-size hint? checksum?) | Compare field values against measured decompressed sizes of type-4 entries; then loader RE | | F3 | PAC header bytes +0x10..+0x7FF — meaningful content? | Hex-survey across all extracted headers (jywjyw preserves them for a reason) | | F4 | LIST.CD full layout on the US disc (only the MAIN.CD-TOC prefix is locally verified) | Regenerate trimmed TOCs from US archives and diff (§2.3) | | F5 | US sub-file counts for SC01, SC03–SC07 (SC01=86 is JP; SC02=45 is unverified US hearsay) | Read each US TOC during Phase-2 extraction — trivial | | F6 | Do any retail type-4 streams violate the length/terminator cross-check? | Extractor warning log over the full disc | | F7 | Are `.sqv` details (3-offset cap, +4 bias, VAB at 0x7000) correct for US files? | Inspect extracted SQV sub-files | | F8 | Where are LOGOA/LOGOB.STR and the .DA files referenced, if not in `CdPathTable`? | memory-map open question #14 |