diff --git a/AGENTS.md b/AGENTS.md index 8e6fe8b5..f5b4516f 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -24,7 +24,7 @@ | `port/src/` | Shims: `libultra.c` (OS API), `gesched.c` (scheduler), `n64stubs.c` (boot/TLB/FPU/rmon), `random.c` (PRNG ported verbatim from `random.s`), `ucode.c` (microcode segment markers), `main.c`, `video.c`, … | | `port/fast3d/` | Software RSP (adapted from the PD port). The main Phase 2 work. | | `rsp/graphics/gmain.s` | GE's RSP ucode — ground truth for GBI/CC/RM. | -| `PD_PORT_CHECKOUT` | Perfect Dark PC port checkout — ground truth for the port layer. Its `port/fast3d/` is the starting point for ours. | +| `PD_PORT_CHECKOUT` | Perfect Dark PC port checkout — **standing reference for this project**: consult it whenever a remaining work item has a PD analogue (same Rare engine family). Port-layer ground truth (`port/fast3d/`, crash/system/video), plus copy candidates: `port/src/preprocess/` (N64→PC asset conversion at load; `filemodel.c` is the D43 near-analogue, same vma 0x5000000) and `mixer.c`/`input.c`/`fs.c` (Phase 3/4 — our stubs were scaffolded to be replaced by these). Caveats: port-layer files only; same family ≠ identical format — validate per field. Full audit: `docs/PCPortResearch.md` §2.4. | ## Build @@ -48,6 +48,6 @@ Run `/linkcheck` for this sweep. Record new findings in `docs/PCPortResearch.md` - **Compile+link milestone:** all ~235 TUs compile and `ge007.x86_64.exe` links clean (`ninja ge007 -k 0`, 236/236). Findings D7–D17 in `docs/PCPortResearch.md` §11; asset stubs in `port/src/assetstubs.c`, fast3d no-ops in `port/src/gfxstub.c` (delete when real fast3d lands). - **Phase 1 (boot to window):** done — ROM loads/validates/maps at cart base 0x10000000 (`romdata.c`), SDL2 window + GL clear loop (`video.c`), absolute asset symbols from `scripts/gen_romassets.py` → `port/src/romassets_.s` (findings D18–D23). - **Phase 1.5 (boot to first frame):** DONE — boot chain complete AND real frames render (`[NOTE] frame N rendered in … us`). D34–D42 closed the blockers on top of D31–D33: ANIM_DATA lvalues, music seq-table ABI+endianness, music-heap sizing, libaudio bank-tree re-layout, implicit-declaration prototype shim (D38), Globalimagetable rebasing + true segment size (D39), ModelHitEntry pool sizing (D40), cross-thread GL context release (D41), rsp task-settings toggle (D42). ROM dump verified against the No-Intro good dump (byte-identical, header CRCs re-checked — `tools_pc/romverify.c`). -- **Phase 2 (rendering):** IN PROGRESS — fast3d integrated + first frames render; current blocker is **D43** (model-file loading ABI mismatch: N64-layout ROM model files read as 8-byte-pointer PC structs → D37-style re-layout in `port/src/romdata.c`). Recurring sub-task: ABI/layout fixes per the D32 procedure (§H) as more asset types are first touched. -- **Phase 3 (audio + input):** libaudio → SDL; decide the ASP strategy (C2: `aspMain*` dummies in `ucode.c`). -- **Phase 4 (saves + polish):** file-backed EEPROM; PFS/motor already no-op stubs. +- **Phase 2 (rendering):** IN PROGRESS — fast3d integrated + first frames render; current blocker is **D43** (model-file loading ABI mismatch: N64-layout ROM model files read as 8-byte-pointer PC structs → D37-style re-layout in `port/src/romdata.c`; a Rare-validated near-analogue exists in the PD port's `preprocess/filemodel.c` — §2.4). Recurring sub-task: ABI/layout fixes per the D32 procedure (§H) as more asset types are first touched (PD's `preprocess/` module already solves most of them). +- **Phase 3 (audio + input):** libaudio → SDL — largely copy-and-adapt from PD (`mixer.c` 722 lines vs our 31-line stub; `input.c` SDL_GameController backend; §2.4); decide the ASP strategy (C2: `aspMain*` dummies in `ucode.c`). +- **Phase 4 (saves + polish):** file-backed EEPROM — build on PD's `fs.c` (file-backed save dir); PFS/motor already no-op stubs. diff --git a/docs/HANDOFF.md b/docs/HANDOFF.md index aa11eb08..265e2a26 100644 --- a/docs/HANDOFF.md +++ b/docs/HANDOFF.md @@ -21,8 +21,9 @@ Commit message style: `PC port: — `. ## Read first (authoritative, in order) 1. `AGENTS.md` — non-negotiables, critical files, build, verification ritual, phase status. 2. `docs/PCPortResearch.md` §H (handoff + plan), then **§F/D44** (the quick win), - **D43** (the open model-file task), **D37** (the libaudio bank-tree re-layout — - the pattern to copy), D39–D42 for the first-render fixes, D31–D38 for background. + **D43** (the open model-file task), **§2.4** (PD-port copy-candidate audit — + read before starting D43 or Phase 3/4), **D37** (the libaudio bank-tree + re-layout pattern), D39–D42 for the first-render fixes, D31–D38 for background. ## Non-negotiables (the ones that bite) - N64 build files (`Makefile`, `tools/`, `rsp/`, `ld/`) — never touch. @@ -76,7 +77,13 @@ Commit message style: `PC port: — `. 2. Understand the file format from `bondtypes.h` (`ModelFileHeader`, `ModelNode`, `ModelRoData` union + per-opcode records) and `bondconstants.h` (`MODELNODE_OPCODE_*`). N64 layout: header 24 B, node 20 B, all pointers 4 bytes. -3. Implement a D37-style two-pass re-layout in `port/src/romdata.c`: walk the node tree + **Do NOT design the re-layout from scratch:** PD port's + `port/src/preprocess/filemodel.c` (1,114 lines) is a Rare-validated near-analogue — + same `PROMOTE` idiom, **same vma 0x5000000**. Read it first; adapt its node walk / + placement / record handling. GE's `ModelRoData` union + opcodes differ (PD has + stargunfire/headspot/gundl types), so validate every GE record type per-field against + bondtypes.h + ROM ground truth. +3. Implement the two-pass re-layout in `port/src/romdata.c`: walk the node tree (Child/Next/Parent links), place every sub-struct once (8-aligned) in a compact image, rewrite each embedded pointer as a zero-extended offset from the image start, so the existing `PROMOTE` rebase (`diff = fileramaddr - vma`, vma = 0x5000000) works @@ -85,7 +92,10 @@ Commit message style: `PC port: — `. 4. Validate one small model against ROM ground truth before generalizing (opcode sequence, node links, a few Data records). gdb works; or write the optional `tools_pc/romdump.py` inspector (below) and validate statically. -5. Expect follow-on faults in other asset types (BG/stan/propobj tables) — same procedure. +5. Expect follow-on faults in other asset types (BG/stan/propobj tables) — same + procedure; note PD's `preprocess/` module already solves most of them + (`filebg.c`, `filetiles.c`, `filepads.c`, `filesetup.c`, …) — see the PD + reference section below. ## Dev-loop speedups (build these when they pay off, not before) Priority order from the last session's measurements: @@ -105,6 +115,23 @@ Priority order from the last session's measurements: scriptable, and enables screenshot diffs against an emulator (Mupen64Plus with the verified No-Intro dump as ground truth). +## PD port as standing reference (always available) +`PD_PORT_CHECKOUT` is the Perfect Dark PC port — same Rare engine +family, and the **standing reference/guidance source** for this project (noted in +AGENTS.md). Consult it whenever a remaining work item has a PD analogue: +- **Phase 2 asset conversion:** `port/src/preprocess/` (~4,100 lines) — per-ROM-segment + N64→PC layout converters hooked from their `romdata.c`. `filemodel.c` = D43 near-analogue + (same vma 0x5000000); `filebg.c` (expected follow-on), `segaudio.c` (cross-check for our + D37 bank-tree fixup), `filelang.c`, `filetiles.c`, `filepads.c`, `filesetup.c`, `gbi.c`, + `segfonts.c`. +- **Phase 3/4 (copy-and-adapt; our stubs were scaffolded for this):** `port/src/mixer.c` + (722 lines vs our 31 — libaudio→SDL mixer), `input.c` (1,551 vs 48 — SDL_GameController + backend with hotplug/rumble/keyboard; remap buttons to GE's scheme), `fs.c` (294 vs 81 — + file-backed save dir for Phase 4 EEPROM/PFS). +- **Caveats:** copy port-layer files only (their game code was modified for PC); same + family ≠ identical format — validate every conversion per-field against GE headers + ROM + ground truth. Full audit: `docs/PCPortResearch.md` §2.4. + ## Standing procedure (you will hit more of these) Every ROM-serialized struct with a pointer field faults the same way once you reach more asset loading. The D32 fix procedure (doc §H): at the fault, `ptype /o `; if a @@ -149,5 +176,7 @@ Checkpoint A: D44 committed — crash log shows a real backtrace for the D43 fau Then the milestone proper: stage objects load without faulting and the rendered scene contains actual model geometry (not just the clear color / background), stable under a 30 s+ soak. After that, Phase 2 makes it look right (fast3d CC/RM correctness vs -`gmain.s`) and Phase 3 adds sound (libaudio → SDL). Commit + push to `origin/master` at -each working checkpoint using the message style `PC port: — `. +`gmain.s` — GE-specific, no PD analogue) and Phase 3 adds sound + input, largely +copy-and-adapt from the PD port (see "PD port as standing reference" above). Commit + +push to `origin/master` at each working checkpoint using the message style +`PC port: — `. diff --git a/docs/PCPortResearch.md b/docs/PCPortResearch.md index d8dd1706..5ec532bb 100644 --- a/docs/PCPortResearch.md +++ b/docs/PCPortResearch.md @@ -133,6 +133,50 @@ The PC build is **CMake** (the N64 build stays Make + IDO). Dependencies: It deliberately **excludes** the N64 I/O drivers (`src/lib/ultra/io/*.c`), the boot/TLB assembly, and the RSP microcode (`.s` under `rsp/`). +### 2.4 PD-port copy-candidate audit (session 2026-08-22) + +The PD port (`PD_PORT_CHECKOUT`) is a **standing reference** +for this project (see AGENTS.md): same Rare "Indy"/Bond engine family, and its +port layer solves the same problem classes we are currently hitting. Audited +against our remaining work items: + +**Direct copy candidates (Phase 3/4 — our stubs were scaffolded to be replaced +by these):** + +| PD file | Lines | Replaces our | Notes | +|---|---|---|---| +| `port/src/mixer.c` | 722 | 31-line stub | libaudio→SDL final mixer; same RareAL family. Adapt to GE's `audi.c` wiring (sample rate/buffering) | +| `port/src/input.c` | 1551 | 48-line stub | SDL_GameController backend: hotplug, rumble, keyboard fallback. Remap the button table to GE's scheme (C-buttons/Z-trig differ from PD) | +| `port/src/fs.c` | 294 | 81 lines | File-backed save dir (`--savedir`, `$S` path expansion) — foundation for Phase 4 EEPROM/PFS file backing (ours are no-op stubs today) | +| `port/src/audio.c` | 75 | 70 lines | Near-identical; diff when Phase 3 starts | + +**Reference implementations (Phase 2 — the D32/D37/D43 class).** PD's port +layer has a `port/src/preprocess/` module (~4,125 lines): a per-ROM-segment +`preprocessfunc` table in their `romdata.c` that converts N64-layout asset data +to PC-native layout *at load time* — a systematic solution to the problem class +we have been fixing ad-hoc per asset type. +- `filemodel.c` (1,114) — the direct **D43** analogue: same `PROMOTE` idiom, + **same vma 0x5000000**. NOT a drop-in copy (GE's `ModelRoData` union + opcode + set differ — PD has stargunfire/headspot/gundl node types), but the node-walk + order, placement/alignment choices, and record handling are Rare-validated + against near-identical data: adapt rather than design from scratch. +- `filebg.c` (524) — BG tables (an expected D43 follow-on fault). +- `segaudio.c` (362) — audio bank-tree conversion; cross-check for our D37 + `romdataFixupAudioBank`. +- `filelang.c`, `filetiles.c`, `filepads.c`, `filesetup.c`, `gbi.c`, + `segfonts.c` — the remaining asset types we will hit. + +**Not copyable:** GE-specific `ModelRoData` record map; fast3d CC/RM + correctness (GE's custom GBI modes vs `gmain.s` do not exist in PD); PD extras +(`config.c`, `optionsmenu.c`, `mpsetups.c`, `mod.c`) — options menu, MP setup, +MOD player; not part of 1:1 fidelity. + +**Caveats:** copy **port-layer files only** — their decomp was modified for PC +(e.g. `types.h` keeps N64 offset comments while using real pointers); our +non-negotiables are unchanged. Same family ≠ identical format: validate every +copied conversion per-field against GE headers + ROM ground truth (the standing +D32 procedure). + --- ## 3. GoldenEye decomp: what we're porting @@ -1236,7 +1280,9 @@ each): `load_object_fill_header()` derives RootNode from `numtextures` at the wrong offset, and the `PROMOTE` rebase (`(u32)var + diff`) then operates on misaligned fields. Needs a D37-style re-layout of the model file image (header + switches array + texture table + node tree + -ModelRoData records). Phase-2 project; see §H. +ModelRoData records). Phase-2 project; see §H. Reference implementation: +PD port's `port/src/preprocess/filemodel.c` — same PROMOTE idiom, same vma +0x5000000 (§2.4); adapt + per-field validation, not a drop-in copy. **D44 (OPEN)** Crash-handler Phase 2 backtrace self-faults before printing. `crashStackTraceSym()` in `port/src/crash.c` calls @@ -1335,10 +1381,14 @@ array, texture table, ModelNode tree (walked via Child/Next/Parent), and the per-opcode ModelRoData records — with every embedded pointer rewritten as a zero-extended offset from the image start so the existing `PROMOTE`/`diff = fileramaddr - vma` rebase works unmodified. Map the record -types first (bondtypes.h `ModelRoData` union, opcodes in bondconstants.h); -validate one small model against ROM ground truth under gdb before generalizing. -Expect more D32-class ABI fixes as other per-level asset types are first -touched — standing sub-task, not a one-off. +types (bondtypes.h `ModelRoData` union, opcodes in bondconstants.h) — but do +NOT design from scratch: PD port's `port/src/preprocess/filemodel.c` (§2.4) is +a Rare-validated near-analogue (same PROMOTE idiom, same vma 0x5000000); adapt +its node walk / placement / record handling and validate every GE record type +per-field against bondtypes.h + ROM ground truth. Expect more D32-class ABI +fixes as other per-level asset types are first touched — standing sub-task, +not a one-off; PD's `preprocess/` module already solves most of them +(`filebg.c`, `filetiles.c`, …). **Dev-loop measurements + speedups (session of 2026-08-22).** Measured iteration costs on the dev box (16 cores): full clean rebuild ≈ 5 s wall @@ -1361,7 +1411,9 @@ an emulator (Mupen64Plus with the verified No-Intro dump). NOT worth it **Audio (Phase 3 prep).** Bank trees are correctly laid out at boot and the libaudio manager + audio thread run without crashing; actual sound output still needs an SDL backend for `alDriver`, and the ASP strategy question (C2) remains -open. No work required until rendering is stable. +open. No work required until rendering is stable. The SDL backend is largely +copy-and-adapt from PD: `port/src/mixer.c` (722 lines vs our 31-line stub, +which was scaffolded for it) plus `input.c`/`fs.c` for Phase 3/4 — see §2.4. **D32 repeatable fix procedure** (apply to any ROM-serialized struct that faults on a pointer-field read):