PC port: document PD-port copy-candidate audit (§2.4) — standing reference, D43 near-analogue, Phase 3/4 copy targets

This commit is contained in:
James Dansereau committed 2026-08-22 08:17:34 -04:00
1 parent 95b6e41a0c
commit 89653cdcbb
3 files changed
+97 -16

No files matched your search

+4 -4
View File
@@ -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_<r>.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.
+35 -6
View File
@@ -21,8 +21,9 @@ Commit message style: `PC port: <phase> — <what>`.
## 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: <phase> — <what>`.
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: <phase> — <what>`.
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 <Struct>`; 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: <phase> — <what>`.
`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: <phase> — <what>`.
+58 -6
View File
@@ -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):