Files
BFM-decomp/docs/gen3-parking-lot.md
T
Drew T 808ec6ff7e feat(phase-25): T5b batch-1 COMPLETE — continuation +541 fns (fleet 72.02->72.18%)
- batch-1 cheap tier (83) now 100% drafted: the 29-continuation recovered via
  resumeFromRunId (9 session-cap failures re-run, 20 cached-replay, 0 errors).
- 22/29 -O2 match_one-MATCH; gate banked 8 clean (main 3, _a 2, _after 3) +
  family_sweep +533 siblings across 133 overlays (5 exemplars, ~0 tokens).
- R22 clean-fleet 136/136; dedup-check 1813/0; 0 NON_MATCHING (G4).
- backlog: func_801457A4 (MATCH but -O0-only -> batch-3), func_80135004/8013AD38
  (need the T7 caller-decl reconcile), func_80168828 (close=8, permuter fuel).
- frontier map now 93 exemplars measured (.run/t5_frontier.jsonl).
- docs/gen3-parking-lot.md: captured the Gen3 native-port recomp architecture
  (PsyQ-SDK HLE boundary on Vulkan; recomp-front-end + decomp-incrementally hybrid).
- session totals: fleet 71.36 -> 72.18% (+2820 fns). Cheap tier done; giants next (hold).
2026-07-09 15:47:30 -06:00

11 KiB
Raw Blame History

Gen3 Parking Lot — asset export / native rebuild (speculative; NOT in scope)

The evolvable docs/-layer home for Gen3 ideas (PROJECT_CONTEXT.md is static/permanent — P1 — so its "Gen3" + "Parking Lot" sections can't be appended to; this file is where Gen3 speculation and its supporting findings accumulate). Gen3 is PARKED per the roadmap: do not start until Gen2 is substantially matched. Recorded 2026-07-01 at Drew's request, after a "could we export the town mesh / Musashi model and rebuild it in Unity?" discussion + a read-only survey of the current state.

The idea

Export BFM's 3D assets — location/town meshes + textures, and character models (Musashi et al.) — and rebuild / inspect them in Unity (or any modern engine). This is enabled by the code decomp but is a separate axis from it (the decomp byte-matches CODE; this interprets DATA). None of the asset-export tooling exists yet.

Current state — what we ALREADY have (VERIFIED 2026-07-01, read-only survey)

Rendering = stock PsyQ libgpu (understood). The game builds primitives with SetPolyF3/F4/FT3/FT4/ G3/G4/GT3/GT4, manages ordering tables (AddPrim/CatPrim/NextPrim/TermPrim/DrawOTag/DrawSync), all byte-matched/linked (Phase 7/8). So on-screen geometry is standard PS1 polys (flat/gouraud, textured/untextured tris & quads) with vertices/colors/UVs/tpage/clut.

Model data = the libgs TMD path IS used (the key positive finding). Verified by real jal call sites in the disassembly — not grep-count speculation (R14):

  • jal GsMapModelingData (maps/relocates a TMD in RAM) in the EXE model subsystem — func_8001C214 / C320 / C4A4 / C5B8 / C810 / C924 / C97C clustered at ~0x8001C2xx–0x8001C9xx — and in ≥1 overlay (ov_SC02_005/func_801871E4).
  • jal GsLinkObject5 (link a TMD → a texture-mapped GsDOBJ) in func_8001C320 / C5B8.
  • ⇒ BFM's model data is TMD (or GsMapModelingData-compatible) — a documented Sony format. This makes static geometry substantially more exportable than a fully custom format would.

Animation / scene mgmt likely CUSTOM. No call sites found for GsSortObject*, GsInitCoordinate2, or GsGetLws (the libgs LWS/TOD animation + auto-sort path). Read: the game uses libgs for model data but a custom sort/draw loop and custom animation on top. ⇒ static town geometry (TMD) is the easy case; animated characters (Musashi) are harder (bespoke skeleton + animation format to RE).

Textures — the tractable half. The upload/handling path is named (LoadTPage/LoadClut/GetTPage/ GetClut/SetDrawTPage/DumpTPage/DumpClut). PS1 textures live in VRAM (1024×512 16-bit; texture pages + CLUTs), so a VRAM dump → texpage/CLUT decode → PNG rip needs no full format RE, and PCSX-Redux is already wired (Phase 3). The type-0 PAC entries (301 extracted .0 "graphics" blobs, each with a +0x08 dimension/format param — see formats.md) are the prime TIM-candidate source.

Data is all extracted; WHERE a town's geometry lives is TBD. Every raw byte is on disk (extracted/retail/SCxx.CD.dir/...). Candidate geometry/texture blobs: type-0 (301, "graphics"), type-7 (139, uninterpreted .7), and possibly the type-4 overlay data tails. Which blob holds a given town's TMDs is unknown — trace it from a GsMapModelingData caller's source pointer.

Tooling: NONE. No TIM/TMD/mesh decoder or exporter in tools/; formats.md has zero geometry coverage (only disc-sector "geometry"). This is greenfield.

Difficulty read

Target Difficulty Why
Town textures Low VRAM rip (no format RE); type-0 "graphics" blobs; harness exists
Town static mesh Medium TMD is documented; locate the blob + confirm stock-TMD vs a Square tweak, then decode
Musashi character model High Skinned + custom animation (no libgs LWS) — bespoke skeleton/anim format on top of the mesh
Unity rebuild (static) Low–Med Import glTF/OBJ + PNG; handle fixed-point→float, handedness, unlit/vertex-color + affine→perspective
Unity rebuild (animated) High Needs the recovered rig + anim export

Decisive next probes (in order, WHEN we pick this up)

  1. VRAM texture rip first (quick, high-reward): PCSX-Redux VRAM dump at a town → decode texpages/CLUTs → PNG.
  2. Trace a GsMapModelingData caller (Ghidra) → find which loaded blob/offset the TMD is read from → locate town geometry on disc (map the type-0/7/overlay-tail question).
  3. Confirm stock-TMD vs Square variant: parse a candidate blob as TMD (header id 0x41, flags, primitive list); a stock TMD decodes with existing PS1 tooling.
  4. Write a TMD→glTF/OBJ exporter + TIM→PNG converter (tools/ — leverage existing PS1 TMD/TIM knowledge).
  5. Unity import + the PS1-ism handling above.
  6. Animated (Musashi): RE the custom skeleton + animation format (separate, harder sub-project).
  • Shiftable build; asset repack (LZSS recompression becomes real here — a standing Gen3 problem).
  • Native recomp / PC port (the original psxrecomp ambition, properly sequenced).
  • Randomizer-grade tooling; JP/proto as extra splat versions; text/translation tooling; decomp.me preset; frogress/decomp.dev dashboards; the public flip (Phase 14, deferred to Gen3+).

Honest caveat

This is speculation + a survey, not committed RE. The load-bearing new fact — BFM uses libgs TMD model data — is byte-verified (real jal sites). Everything downstream (which blob, stock-vs-variant, the exporter, Musashi's anim) is unstarted and belongs to a future Gen3 effort. The decomp is the enabler: the matched renderer + the located call sites are the format spec.


Native PC port — the recomp architecture (PsyQ→Vulkan HLE boundary)

Spitball with Drew, 2026-07-08 (during a T5b wait). The fleshed-out "native recomp / PC port" parking-lot item. PARKED — Gen3. Not committed; captured while fresh (R30). The key architectural insight is the PsyQ-SDK HLE boundary, which is what makes a PS1 port tractable rather than "write another emulator."

The idea

A native PC build of BFM via a static-recomp front-end for the game's MIPS code + a PsyQ-SDK HLE layer on modern backends (Vulkan for the GPU, an audio lib for the SPU, native FS for the CD). Distinct from the asset-export axis above (that interprets DATA; this makes the CODE run natively).

The key insight — HLE the SDK API, not the hardware

A naïve PS1 recomp is hard because you end up re-emulating the GPU at the command/register level (the emulator problem). But BFM never touches GPU registers — it goes through the PsyQ SDK (libgpu GsSortObject/DrawOTag, libgte RotTransPers, libspu, libcd). The SDK is a finite, documented API, and we've already isolated it byte-exact (Phase 7/8 linked libgpu/libgte/libspu/libcd from the real PsyQ 4.0 objects; we know precisely which addresses are SDK vs game code). So reimplement the API, not the hardware — Wine/Proton-shaped, not emulator-shaped.

Our decomp already draws the exact boundary: every function is partitioned "game code (matched C)" vs "PsyQ library (linked object)." That split IS the "translate vs HLE" partition — Phase 7/8 hands the port its function partition for free. So: game code → static-recomp (or use our decomp'd C where it exists); PsyQ SDK → replace with the Vulkan/audio/FS HLE layer (don't translate it).

Three architectures (and the winning hybrid)

  • A — full static recomp (N64Recomp-style): translate ALL MIPS + emulate the GPU at command level. Hard; re-does emulator work.
  • B — PsyQ-HLE port (this idea): game code recomp'd/decomp'd, boundary cut at the SDK, HLE'd on Vulkan. The sweet spot.
  • C — decomp port: compile our matched C native + a PsyQ shim — converges with B (both need a PsyQ reimplementation).
  • Hybrid (the winning move): a recomp front-end boots a native build far sooner than a 100% decomp, then the decomp incrementally replaces recomp'd functions with readable C (recomp-now, decomp-forever). This is the N64 world's proven pattern.

Hard parts (honest)

  • Overlays — the #1 PS1 gotcha, and BFM is overlay-heavy (134 overlays streamed to 0x80128158). Static recomp hates "same address = different code over time." N64Recomp handles it by recompiling each overlay separately + dispatching on which is loaded — and we've already mapped every overlay boundary + the loader state machine (Phase 3), so this is derisked for us specifically. This is the biggest reason our recomp is more feasible than a cold one.
  • GPU HLE (biggest single chunk): walk the OT in DrawOTag, translate each primitive (POLY_FT4, …) to Vulkan. Choose fidelity — replicate PS1 quirks (affine warp, OT painter's-algo, 15-bit dither) or "fix" them (perspective-correct, hi-res). DuckStation's hardware renderer = a reference for the mapping.
  • GTE: mostly free via libgte HLE, except BFM uses inline GTE macros (gte_rtps → raw cop2; why our import auto-detects GTEMAC + gte_macros.inc) — those cop2 ops land in recomp'd game code → still need a small software GTE (fully documented, a few hundred lines).
  • SPU + CD: HLE libspu (SEQ/VAB) + libcd → read our extracted assets. The CD loader is already RE'd (Phase 3), so the streaming state machine maps to a file/event model.

The gap + the reusability sleeper

  • No mature PS1→PC static recompiler exists the way N64Recomp does (the PS1 world went the emulator + per-game-decomp routes). Drew's own psxrecomp (rocky v1–v3 post-mortem) is the closest prior art. ⚠️ Verify via web before committing (X2/R17) — check for recent experimental MIPS-R3000/PS1 static-BT efforts or anyone extending N64Recomp's RabbitizerLib/N64ModernRuntime toward R3000.
  • Reusability: because ~every PS1 game used PsyQ, a PsyQ-HLE + MIPS-R3000-recomp toolkit generalizes to the whole PsyQ library — "N64Recomp for PS1," a genuinely new community tool + a Gen2-public-flip hook.

Sequencing

Gen3, parked — do NOT fork Gen2 focus. This capture is the architecture decision to revisit at Gen3: port architecture = PsyQ-SDK HLE boundary on Vulkan; recomp front-end for early boot, decomp replaces incrementally; overlays per-N64Recomp; reuses the Phase-7/8 SDK partition + the Phase-3 overlay/loader map.

Also worth noting — C++ vs C# (from the same discussion)

  • C++ = natural port target: closest to the decomp's C (structs/unions/pointer-math/manual-memory carry over ~mechanically), best perf, direct graphics/audio interop. Port native first, modernize incrementally.
  • C# = a reimplementation character (pointer arithmetic/unions fight the managed model) using the decomp as an executable spec/oracle — the Unity-remake route. Valid, more freedom, much more work.
  • The decomp enables both; C++ is pragmatic, C# is the "remake."