From d7b9371afbd14d7cb87281931b2fd28155725a22 Mon Sep 17 00:00:00 2001 From: James Dansereau <106631954+jkdansereau@users.noreply.github.com> Date: Tue, 6 Oct 2026 18:35:44 -0400 Subject: [PATCH] Docs: public copy, site presentation, release-gate review (D533, D534) Public-copy language pass, homepage restructure and Documentation rename, 404 page, dead-anchor and permalink fixes, release-gate sign-off, and the pre-release review fixes of the 2026-10-05 batch (probe-doc drift, build-path hygiene, verify.sh and capture safety). Refs: D533 D534 --- .github/SECURITY.md | 2 +- .github/release-notes.md | 67 ++-- AGENTS.md | 38 ++- README.md | 21 +- build-pc.sh | 4 +- docs/404.html | 20 ++ docs/ROADMAP.md | 33 +- docs/_config.yml | 28 +- docs/_includes/head-custom.html | 133 +++++++- docs/_layouts/default.html | 31 +- docs/building.md | 5 +- docs/dev/GE-ENV-PROBES.md | 1 + docs/dev/HANDOFF-ARCHIVE.md | 217 ++++++++++++- docs/dev/agentic-development.md | 23 +- docs/dev/findings-index.csv | 14 +- docs/dev/findings.md | 121 +++++-- docs/{under-the-hood.md => documentation.md} | 36 +-- docs/fidelity.md | 72 +++++ docs/framerate-and-pacing.md | 2 + docs/index.md | 322 +++++++++---------- docs/input-and-aim.md | 2 + docs/internals.md | 2 +- docs/porting-notes.md | 6 +- docs/saves.md | 2 + docs/security-and-fidelity-status.md | 115 ------- docs/security.md | 87 +++++ docs/the-asset-pipeline.md | 2 + docs/the-software-rsp.md | 2 + port/fast3d/gfx_pc.cpp | 4 +- tools_pc/audiodebug.ps1 | 2 +- tools_pc/capture_p7.sh | 6 +- tools_pc/golden/README.md | 8 +- tools_pc/verify.sh | 22 +- 33 files changed, 982 insertions(+), 468 deletions(-) create mode 100644 docs/404.html rename docs/{under-the-hood.md => documentation.md} (50%) create mode 100644 docs/fidelity.md delete mode 100644 docs/security-and-fidelity-status.md create mode 100644 docs/security.md diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 192f0203..6e6b6d2e 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -12,7 +12,7 @@ surface is: A full walkthrough of what a release actually installs on your machine, and what a from-source build pulls in, is in -[`docs/security-and-fidelity-status.md`](../docs/security-and-fidelity-status.md). +[`docs/security.md`](../docs/security.md). ### Why the binaries are unsigned diff --git a/.github/release-notes.md b/.github/release-notes.md index 5a87b3ee..f9562a6a 100644 --- a/.github/release-notes.md +++ b/.github/release-notes.md @@ -16,12 +16,12 @@ Region: **NTSC-U (US) only**. See [Known issues](#known-issues). build was played on (e.g. "Played on Windows and Steam Deck: campaign spot-checks, 2-4P split-screen, a controller-only session"). --> -The headline is **the settings interface, rebuilt**. Both options screens are -merged into one, laid out like the Perfect Dark PC port's menu, with plain-English -wording, real-unit sliders and a one-line description under whichever option you -select. Alongside it: the game's own N64 control styles as a per-seat preset, -emulator save files that load directly, a scalable HUD overlay, and a round of -fidelity fixes checked frame-by-frame against the N64 game. +The headline is **one real options system**: both options +screens merged into a single menu, laid out like the Perfect Dark PC port's, +with the game's own N64 control styles as a per-seat preset, emulator save +files that load directly, and a scalable HUD overlay. A round of fidelity +fixes, checked frame-by-frame against the N64 game, rounds out the feature +list. --- @@ -37,15 +37,6 @@ fidelity fixes checked frame-by-frame against the N64 game. - **Display mode** is a dropdown; **Backspace** goes back, matching the rest of the game's menus (D504 follow-up). -### Menu wording, units and help text (D505–D507) - -- 27 maintainer-approved wording edits across both menus: option names say what - they do, not what the internal variable is called (D505). -- Sliders carry **real units** — percent, frames, seconds, pixels — and their - values are standardised across the two menus (D506). -- Selecting an option shows a **one-line description** of it in the same menu - (D507). - ### Control styles: the game's own N64 layouts (D513, D516, D518) - **Control style** offers **Ext** (this port's scheme, unchanged) and @@ -79,18 +70,6 @@ fidelity fixes checked frame-by-frame against the N64 game. **crosshair colour by health** — the four settings the Perfect Dark port has that this port was missing. -### Overlay controls are antialiased (D520) - -- Slider wedges, slider markers and dropdown arrows no longer step in whole - canvas pixels (which grew visibly at large window sizes); they are drawn - smooth at every size. - -### Watch menu keeps its own settings (D349, corrected) - -- Closing the in-level **Watch** menu now saves the watch's own screen size and - ratio, and they no longer fight the front-end **Aspect ratio** setting. The - earlier note in the findings log was corrected. - --- ## Fidelity and stability fixes @@ -101,13 +80,19 @@ fidelity fixes checked frame-by-frame against the N64 game. - **Aspect ratio:** forced ratios now letterbox/pillarbox without re-cropping the play area (D508); the rare gun/hand model glitch after changing ratio mid-level is largely fixed, a rare remainder is logged (D509). +- **Watch menu:** closing the in-level **Watch** menu now saves the watch's own + screen size and ratio, and they no longer fight the front-end **Aspect ratio** + setting; the earlier note in the findings log was corrected (D349). +- **Overlay controls:** slider wedges, slider markers and dropdown arrows no + longer step in whole canvas pixels (which grew visibly at large window sizes); + they are drawn smooth at every size (D520). - **FPS counter:** holding left/right on a Bond settings row no longer drops the FPS counter (D517). - **Build:** a fresh build directory no longer produces an untagged executable name (D515). - **Tooling:** the asset converter's argument quoting and sidecar size check are - tightened; the CI cache actions no longer carry stale ROM paths. The golden-frame - tooling is now non-destructive and drift-free: a verification sweep used to + tightened; the CI cache actions no longer carry stale ROM paths. The + reference-frame tooling is now non-destructive and drift-free: a verification sweep used to overwrite your own `data/ge007.ini` and leave the previous level's save file behind, and now pins both idempotently (your ini and save are restored whatever the run does); the capture helper reads its frame windows out of the gate instead @@ -124,6 +109,16 @@ fidelity fixes checked frame-by-frame against the N64 game. --- +## The small stuff + +27 menu-wording edits across both menus (option names say what they do, not +what the internal variable is called, D505); sliders carrying **real units** — +percent, frames, seconds, pixels — standardised across the two menus (D506); +a **one-line description** of the selected option in the same menu (D507); +and menu spelling standardised to one convention across both menus. + +--- + ## Known issues | Issue | Impact | Workaround | @@ -148,10 +143,16 @@ follow-ups. sentence "these were merged and review-verified but not playtested live" in the body; never imply a full playtest. --> -**Golden-gate state for this release:** both platforms carry the full 21-level -reference-frame set (63 frames each) at the same stems, and each platform's gate -is green against its own goldens. The cross-platform spread (0.348-3.983% of -pixels over tol 2 on the 20 non-Cuba levels, structural tier 21/21 clean) is +**Reference-frame gate for this release:** both platforms carry the full 21-level +reference-frame set (63 frames each) at the same stems. The Windows gate is green +against its own goldens (21/21, three consecutive clean sweeps); on Linux the +crash gate is green and the pixel verdict is still owed (see below). The recipe pins the run for you: the save file's +CONTENT is pinned (the gate installs its own canonical save per level, the same +way it pins the display ini — D529) and the PRNG seed is hard-pinned (a stray +`GE_RSEED` in your environment warns and is ignored rather than re-seeding the +gate — D532), so a gate run and a capture run see the same starting state. The +cross-platform spread measured on the shipped pairs (0.380-5.120% of pixels over +tol 2 on the 20 non-Cuba levels, structural tier 21/21 clean) is informational only — it is not a cross-platform pixel-parity claim, and the linux *pixel* gate did not run on the capture box (its GL probe fails closed there; the linux crash gate did). The Dam ending cutscene has not been checked diff --git a/AGENTS.md b/AGENTS.md index 40fca429..a7f6d5ff 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,6 +9,33 @@ - **Current status:** the README "Status" section (`docs/dev/LEVEL-STATUS.md` is a historical per-level sweep record). Current task + environment: `docs/HANDOFF.md` (a rolling local working file — may be absent in a fresh clone; fall back to the README "Status" section). - **Dispatching subagents?** `docs/dev-process.md` — task budgets/deadlines, file partitioning, pre-flight, the standard brief template. Every investigation subagent reads `docs/porting-notes.md` first and appends to it. +## Public site pages (`docs/`) — presentation layer, not a source of truth + +The repo's homepage and sub-pages are published by GitHub Pages; the local +preview is `node scratch/build_preview.mjs site` → `scratch/_site/` (served +with `node scratch/serve_preview.mjs 8777`). The site is a **presentation +layer over the reference docs, kept in a separate area, and allowed to go +stale** — never let it poison a work item: + +- **Pure site pages** — `docs/index.md`, `documentation.md`, + `the-software-rsp.md`, `the-asset-pipeline.md`, `framerate-and-pacing.md`, + `input-and-aim.md`, `saves.md`, `security.md`, `fidelity.md` — + summarise the ROADMAP / README / finding log / release notes. **Do not + consult them as work information, cross-check against them, or treat a + mismatch with `docs/ROADMAP.md` or the finding log as a bug** (that is + the expected, independent-staleness case). Update them only when the task + says so, and source every fact from the ROADMAP / README / finding log — + never the other way round. The footer "last updated" date in + `docs/_layouts/default.html` is the same class of site-only detail. +- **Shared files** — `docs/internals.md`, `porting-notes.md`, + `building.md`, `dev-process.md`, `dev/agentic-development.md` are + authoritative working docs that the site publishes verbatim; edit them + normally as part of dev work and the site picks up the change for free. + +Keep the split intact: no working notes migrate into pure site pages, and +never edit a shared doc merely to make the site read better — if the site is +stale, fix the site page (or the fact upstream, as its own task). + ## Non-negotiables 1. **N64 build untouched.** `Makefile`, `tools/`, `rsp/`, `ld/` belong to the N64 build. Never modify them for the PC port. @@ -45,8 +72,10 @@ mostly agent-authored. documentary and small (bug shots, previews — downscaled), and never commit bulk ROM-derived captures: capture output dirs are gitignored (`tools_pc/sweep-captures/cap-*/`, `tools_pc/sweep-captures/**/*.ppm`); the - only bulk imagery in the tree is `tools_pc/golden/*.png`, which exists - because `verify.sh` consumes it. Downloads ship no game content at all. + only bulk imagery in the tree is `tools_pc/golden/**/*.png` + (63 frames × 2 platforms, 126 total, nested per level at + `tools_pc/golden//{win,linux}/`), which exists because `verify.sh` + consumes it. Downloads ship no game content at all. 5. **Batch, don't spam.** Accumulate approved doc/tree fixes across a session into ONE commit; no per-item branches. 6. **Release gate.** Before pushing a release: the PII scrub and legal/copyright @@ -83,8 +112,9 @@ Needs CMake + SDL2 + zlib + OpenGL, and must run from the MSYS2 MINGW64 shell 1. **`Cannot create temporary file in C:\Windows\: Permission denied`** at the link step. The PE toolchain (ninja → cmd → gcc/ld) needs a writable TMP/TEMP; the msys→native env conversion drops it in non-login shells - (agent harnesses; the `C:/msys64` tree here was built for `D:/M/msys64`, - so its conversion is unreliable). `build-pc.sh` now **self-heals**: it + (agent harnesses; the `C:/msys64` tree here was built for a relocated MSYS2 + root — `` in the placeholder legend — so its path conversion is + unreliable). `build-pc.sh` now **self-heals**: it probes a native child's TMP (via a file — piped `cmd.exe` stdout is unreliable under msys console emulation) and, if broken, re-runs cmake+build under PowerShell with `TMP`/`TEMP` set natively (the diff --git a/README.md b/README.md index 108b532f..dfcb658f 100644 --- a/README.md +++ b/README.md @@ -48,14 +48,17 @@ project is as much a study of that process as it is a port. See ## News -- **2026-10-05** — **v0.5.0**: one options menu laid out like the Perfect Dark - port's, with plain-English wording, real-unit sliders and a one-line description - per option; the game's own N64 control styles as a per-seat preset; emulator save - files that load directly; a scalable HUD overlay; fullscreen mode, window centring - and crosshair settings; antialiased overlay controls; and a fidelity round checked - frame-by-frame against the N64 game (fog, aspect-ratio letterboxing, the Watch - menu's own settings). The reference-frame gate was re-based on both platforms - (21 levels, 63 frames each). [Release notes](https://github.com/jkdansereau/goldeneye-pc-port/releases/tag/v0.5.0). +- **2026-10-05** — **v0.5.0**: one real options system: a + single menu (laid out like the Perfect Dark port's) covering every setting, + the game's own N64 control styles as a per-seat preset, and your emulator + saves loading directly; a scalable HUD overlay; fullscreen, window-centring + and crosshair settings; and a fidelity round checked frame-by-frame against + the N64 game (fog, aspect-ratio letterboxing, the Watch menu's own + settings). The reference-frame gate was re-based on both platforms (21 + levels, 63 frames each). Plus the small stuff: a menu wording, units and + help-text pass, standardised menu spelling, and antialiased overlay + controls. + [Release notes](https://github.com/jkdansereau/goldeneye-pc-port/releases/tag/v0.5.0). - **v0.4.1**: 2–4 player split-screen multiplayer, controller presets and rebinding, PlayStation/Nintendo button names, master volume and audio device selection, a steady 60 fps on low-end GPUs, and fidelity fixes @@ -239,7 +242,7 @@ only in the release packages, and no macOS/ARM builds yet. What a release installs (no networking, no telemetry, no ROM or game assets) and how faithfully the port tracks the original game's logic: -[Security & fidelity status](docs/security-and-fidelity-status.md). +[Security](docs/security.md) and [fidelity](docs/fidelity.md) status. The binaries are not code-signed; from v0.5.0 on each release artifact also carries a GitHub build-provenance attestation you can check with `gh attestation verify --repo jkdansereau/goldeneye-pc-port` diff --git a/build-pc.sh b/build-pc.sh index b5c149cc..5172aced 100644 --- a/build-pc.sh +++ b/build-pc.sh @@ -45,7 +45,7 @@ BUILD_DIR="${BUILD_DIR:-build-pc}" # MSYS2 install). Whatever `cmake`/`gcc` happen to be first on PATH is not # trustworthy: Git Bash's /mingw64 has no toolchain, and a pip-installed # cmake in a Python Scripts dir can shadow the MSYS2 one. GE_MSYS2_ROOT -# (e.g. D:\M\msys64) overrides the search. +# (e.g. C:\msys64) overrides the search. _ge_find_mingw_bin() { local c root root="${GE_MSYS2_ROOT:-}" @@ -67,7 +67,7 @@ _ge_find_mingw_bin() { if [ -n "${MSYSTEM:-}" ] && [ -z "${GE_PC_BUILD_VIA_NATIVE:-}" ]; then _mingw_bin="$(_ge_find_mingw_bin)" || { echo "ERROR: could not find the MSYS2 MinGW64 toolchain (mingw64/bin with cmake.exe + gcc.exe)." >&2 - echo "Install it (see docs/building.md) or point GE_MSYS2_ROOT at the MSYS2 root, e.g. GE_MSYS2_ROOT='D:\M\msys64'." >&2 + echo "Install it (see docs/building.md) or point GE_MSYS2_ROOT at the MSYS2 root, e.g. GE_MSYS2_ROOT='C:\msys64'." >&2 exit 1 } # Failure mode 3 (AGENTS.md): the toolchain's DLL dir must be on PATH, diff --git a/docs/404.html b/docs/404.html new file mode 100644 index 00000000..c8b3f422 --- /dev/null +++ b/docs/404.html @@ -0,0 +1,20 @@ +--- +title: 404 +description: Page not found. +layout: default +permalink: /404.html +--- + +## 404 + +This page doesn't exist. The site's real pages are linked from the +nav: [home]({{ '/' | relative_url }}) · +[documentation]({{ '/documentation' | relative_url }}) · +[building]({{ '/building' | relative_url }}). + +*Note for anyone following a moved link: the old "under the hood" page now +lives at [documentation]({{ '/documentation' | relative_url }}).* + + diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index 99f1d0ec..42ed6205 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -140,15 +140,13 @@ below). | Item | Status | Refs | Notes | |---|---|---|---| | Third-person Bond offset residual | partial; intro puppet explained (2026-10-02) | D173, D292 | Mostly fixed. On the level-start puppet the variance is tick/PRNG-dependent idle timing (D13 class, faithful, no sign-off requested); one 1-frame first-tick Y transient is untraced. If the residual is seen elsewhere (cutscene body model), the maintainer should note where. | -| Dam ending cutscene: Bond placement; railing transparency | partial (railing/grate transparency confirmed faithful 2026-10-07; Bond-placement re-check pending) | D173, D292, D526; maintainer 2026-10-02 | **Railing/grate transparency CLOSED as faithful (D526):** the 2D edge-railing (grate) texture in the Dam ending shows "odd" transparency between its two anchors, confirmed identical on 1964/GEPD (port == N64) → the original 2D texture, not a port bug (D306/D291 class). **Remaining: re-check Bond placement at the end of Dam** (the D173 level-start probe found no defect; the last place a Bond offset was seen). | +| Dam ending cutscene: Bond placement; railing transparency | partial (railing/grate transparency confirmed faithful 2026-10-05; Bond-placement re-check pending) | D173, D292, D526; maintainer 2026-10-02 | **Railing/grate transparency CLOSED as faithful (D526):** the 2D edge-railing (grate) texture in the Dam ending shows "odd" transparency between its two anchors, confirmed identical on 1964/GEPD (port == N64) → the original 2D texture, not a port bug (D306/D291 class). **Remaining: re-check Bond placement at the end of Dam** (the D173 level-start probe found no defect; the last place a Bond offset was seen). | | Title-screen attract demo runs too fast; F10 box rescales while it plays | backlogged (after v0.5.0, maintainer 2026-10-04) | D87 (ramrom file struct); timing: D13, D248, D427, D451, D486 | **Reported 2026-10-04 (maintainer live pass):** the attract segment plays visibly too fast; not investigated. Lead: it is a ramrom demo replay whose recordings carry a per-frame tick count, which the port's 60 fps / 1-tick pacing may override. Rule out PC causes, then compare with 1964. Same pass: the F10 box briefly rescaled during the demo (not reproduced; candidate fix = overlay reads fast3d's native viewport, parked locally). | -| Dam: first-tower guard walks a small circle (glass collision polygon) | done | D490 | **Fixed 2026-10-02, merged into `release/v0.4.1` 2026-10-03:** a stack overflow in `sub_GAME_7F03ECC0` (flat-bbox objects, i.e. glass) corrupted the collision polygon; `rem[8]` under `AVOID_UB`. Headless probe: the guard now walks his whole patrol loop. Maintainer by-eye check in Dam passed 2026-10-02. The porting-notes D24 audit ran 2026-10-03 and found D491 (below); no other overflow of this kind. | -| Struct reads across adjacent globals: portal bbox seeds put props in extra rooms; casing spin seeded from trigger state | done | D491 | **Fixed 2026-10-03, merged into `release/v0.4.1`:** `bg.c` / `gunfire.c` read three separate scalar globals as one coord3d; mingw reverses their order. `#ifdef PORT` reads the named globals. Builds clean, 900-frame `-level_09` smoke clean. Nothing by eye is expected. Delete this row once released. | | Sniper rifle: zoom level and speed vs 1964 | verify | #136, D484, D485, D494 | Crouch: **D494** (2026-10-03) makes the PC crouch key crouch/stand freely with the sniper, as GEPD does (superseding D483's freeze); maintainer-verified. Zoom level (D484, 20 -> 7 degrees) and speed (D485, tick-scaled, Rule-2) fixed 2026-10-02. **Owed:** 1964 A/B of zoom level and speed; close #136 after it. | | Crash on save slots 1/3/4, plus a host BSOD (fltmgr.sys) | decided | #94 | Needs a repro. Two maintainer retest requests (v0.3.0, v0.4.0) got no reply. Likely D299/D301 (save-slot crash, fixed) and D344 (BSOD on exit, fixed). **Decided 2026-10-01: close as stale when the release ships**, pointing at D299/D301/D344; no GitHub activity before then. | | Authored mip chains not sampled for explicit-LOD textures | post-1.0 | D323 | Distant textures use driver mips instead of Rare's hand-authored chain (heaviest on Statue/Aztec). Parked as a fidelity enhancement. **Decided 2026-10-01:** after 1.0, together with the LOD-fraction approximation as one "real RDP LOD" project. | | Approximations in the software RSP | post-1.0 | `gfx_pc.cpp` `G_CCMUX_LOD_FRACTION`; D323 | The LOD-fraction CC input is an eyeballed approximation, bundled with D323 as one "real RDP LOD" project after 1.0 (2026-09-30 triage). **WrapFix settled 2026-10-02:** the maintainer checked the Depot building ceilings against 1964 with the default `Video.WrapFix = 0` and they look right, so the default stays off (the opt-in knob remains; RC3/D167). | -| Latent 64-bit ABI hazards | partial | D441, #107, #108; AUDIT-M6 `struct player`/`struct hand` raw offsets (porting-notes §A1) | **State (2026-10-03, D441):** complete census (local `scratch/d441/`; many compiler hits are intentional `(s32)&ANIM_DATA_*` offsets, D34); 17 files widened to `uintptr_t` under `#ifdef PORT`; 35 sign-extension sites fixed; CI ratchet `tools_pc/abi_ratchet.py` (baseline 389); arena windows centralised (`port/include/portaddr.h`); tier-1 high-arena build (`GE_HIGHARENA_BASE=0x90000000`) runs the menu and `-level_09` pixel-identical to default. D478: nothing live on the current layout; the rest is harmless today but would bite on macOS/ARM/ASLR. **Sweep (2026-10-05):** tier-1 all-level sweep done — 21/21 PASS, 0 crashes, worst over-tol 2.38 % Jungle / 1.38 % Surface 2 (both under their 3 % limit), all 1 %-limit levels <= 0.24 %; no fix batches, ratchet unchanged (389); recipe + caveats in the D441 findings entry. **Owed:** tier 2 (above 4 GiB; u32 slots widened); store-to-u32 vs arithmetic-only tag per site (`portHostToN64` under macOS port_addr); `mema.c` widening (D453); `initanitable.c` heap slots; GE_DETERM wall-clock timers (D117 follow-up, blocks using determinism mode for frame gates). **#108:** widening the `sndPlaySfx` bound disables the guard everywhere; prefer a range check. Detail: findings D441, D457, D478. | +| Latent 64-bit ABI hazards | partial | D441, #107, #108; AUDIT-M6 `struct player`/`struct hand` raw offsets (porting-notes §A1) | **State (2026-10-03, D441):** complete census (local `scratch/d441/`; many compiler hits are intentional `(s32)&ANIM_DATA_*` offsets, D34); 17 files widened to `uintptr_t` under `#ifdef PORT`; 35 sign-extension sites fixed; CI ratchet `tools_pc/abi_ratchet.py` (baseline 389); arena windows centralised (`port/include/portaddr.h`); tier-1 high-arena build (`GE_HIGHARENA_BASE=0x90000000`) runs the menu and `-level_09` pixel-identical to default. D478: nothing live on the current layout; the rest is harmless today but would bite on macOS/ARM/ASLR. **Sweep (2026-10-05):** tier-1 all-level sweep done, 21/21 PASS, no fix batches, ratchet unchanged (389) — recipe + caveats in the D441 entry. **Owed:** tier 2 (above 4 GiB; u32 slots widened); store-to-u32 vs arithmetic-only tag per site (`portHostToN64` under macOS port_addr); `mema.c` widening (D453); `initanitable.c` heap slots; GE_DETERM wall-clock timers (D117 follow-up, blocks using determinism mode for frame gates). **#108:** widening the `sndPlaySfx` bound disables the guard everywhere; prefer a range check. Detail: findings D441, D457, D478. | ## 3. Verification debt (fixed; a live check is owed) @@ -179,11 +177,9 @@ A single campaign pass with these on the checklist would clear most of them. | macOS (Intel + Apple Silicon) / ARM64 Linux | open (target 0.9.0/1.0, maintainer 2026-10-02) | PRs #88, #95; #101 | Outside drafts, parked for `main` until after 1.0. **Address model decided (2026-09-30): port_addr (#95)**, a `portN64ToHost`/`PORT_N64PTR` chokepoint with a `PORT_ADDR_STRICT` validator. The `macos` integration branch is on origin with #88 (merged), #132 and #95 (retargeted, open); nothing merges to `main` before the post-1.0 pass. **Open against `macos`:** #131 (self-hosted sweep scaffold, never run on a Mac; needs #95 first). **On the next rebase onto `main`:** take #131's `selfhosted.yml`; drop #95's `crouched_rifle` hunk (our #120/D445 is correct); relabel #95's D29x/D405-D421 labels. Needs the §2 ABI-hazard sweep; the maintainer tests firsthand before merging. | | Platform parity gate (Linux co-equal with Windows) | open | — | **Decided 2026-09-30:** yes. Every released platform carries the same gate: a 21-level sweep plus a campaign pass. Needs the golden baselines on both platforms first (§7). The last known Linux crash (D190) was closed as not-a-bug. | | Low-end GPUs (~30 fps on Celeron/HD 400) | fixed, verify on N3060 class | D339, D481, D482, #92 | **D481 (2026-10-02):** fast3d ran inline on the scheduler thread, delaying retrace delivery so the tick gate skipped frames; with the render worker a Lenovo X220 (HD 3000) went 44-46 → 60.0 fps. **D482:** Atom/Celeron-class GPUs (HD 400-605, UHD 600/605, software renderers) get DrawDistance/LodDistance 100 and MSAA 1 once, where still at defaults. **D473:** uncached per-batch getenv fixed. Direction: real optimizations, not a preset. **Next only if N3060-class hardware still drops:** one `GE_PERFSTAT=1` run there (`gpu=` vs `run=`), then a lighter renderer-keyed default (GPU) or fast3d per-batch cuts (CPU; local `PERF-HOTSPOTS.md`). | -| Statue/Cradle hitching (maintainer, 2026-09-30) | parked (not player-visible, 2026-10-02) | `GE_PERFSTAT` spike counters (2026-10-01) | **Diagnosed (D475, D477, D480):** no sustained load. The ~33 ms frames are the game's frame limiter skipping a retrace after a slightly late gfx task (`boss.c`; D13-class timing). CPU bursts were CI-palette re-imports (ruled out, D477; CI4 D476) and in-play shader compiles (fixed by D480). **Parked 2026-10-02:** a maintainer play session with the timeline probe (`probe/hitch-timeline`, never merge) felt no hitching. Reopen only if a visible hitch is reported (lever: pre-warm at level load). | +| Statue/Cradle hitching (maintainer, 2026-09-30) | parked (not player-visible, 2026-10-02) | `GE_PERFSTAT` spike counters | **Diagnosed (D475, D477, D480):** no sustained load — the ~33 ms frames are the frame limiter skipping a retrace after a late gfx task (`boss.c`, D13-class timing); CPU bursts were CI-palette re-imports (ruled out, D477) and in-play shader compiles (fixed, D480). **Parked 2026-10-02:** a play session with the timeline probe felt no hitching. Reopen only if a visible hitch is reported (lever: pre-warm at level load). | | Steam Deck preset skipped when first launched from Desktop Mode | verify | D283 | Fixed on `release/v0.4.1`: the Deck is detected by DMI hardware id (Valve Jupiter/Galileo; `STEAMOS` as a second signal) and the preset applies once per ini, only while Fullscreen/Maximized are untouched. Only a Windows build and a Linux `-fsyntax-only` check ran. **Owed: one run on a real Deck** (Desktop-Mode-created ini, then Game Mode; expect fullscreen 1280x800 and the log line `video: Steam Deck (DMI Valve ...)`). The Known-issues row goes when this ships. | -| Windows 7 | open | #98 | **Re-checked 2026-10-02: likely feasible around 1.0.** The exe imports no Windows 8+ functions and the bundled SDL2 2.32.10 still supports Windows 7. The real blocker is the first-run converter `ge007-convert.exe` (PyInstaller, Python 3.9+): the native C converter (#6 Part B) removes it, or a separate Python 3.8 build. Work: one Windows 7 VM test, then manual per-release checks (CI cannot test it). Supersedes the earlier 'won't-fix' call. || Linux golden baselines (P7) + two residual render bands | closed (2026-10-05 re-round) | D521/D525 | 21-level `/linux/` set captured 2026-10-04 on a real-GL box (Mesa / Intel HD 3000), then **re-rounded 2026-10-05 (P7b, D525)**: all 21 levels, 63 frames, at the D522 windows with the save pinned present (the 2026-10-04 set had `data/ge007.eep` cleared, and D523 measures that save-file presence moves ~every level). Cross-platform framediff vs `win/` at the same stems: structural **21/21 clean** (worst cell mean 4.43, phash <= 6 except cradle at 40), exact **0.348-3.983%** over tol 2 on the 20 non-Cuba levels. **Both residual bands closed:** Archives 19.956-24.554% (cell means to 50.8) is now 3.590-3.859% (worst cell 2.31, phash 1) and Streets 1200's 16.0-148.4 cell-mean band is now 3.616-3.983% (worst cell 4.43, phash 0) — save state, not a renderer. Dam's 900 flyby offset is out of the set (Dam sits at 1500-1700, cross-platform 2.964-3.443%). Caveat: the box's own sweep is crash-detect only (`glxinfo -B` fails there, rc 255, so `verify.sh`'s probe fails closed); the cross-platform numbers are computed on Windows. | - - +| Windows 7 | open | #98 | **Re-checked 2026-10-02: likely feasible around 1.0.** The exe imports no Windows 8+ functions and the bundled SDL2 2.32.10 still supports Windows 7. The real blocker is the first-run converter `ge007-convert.exe` (PyInstaller, Python 3.9+): the native C converter (#6 Part B) removes it, or a separate Python 3.8 build. Work: one Windows 7 VM test, then manual per-release checks (CI cannot test it). Supersedes the earlier 'won't-fix' call. | ## 5. Modern-remaster layer (port features beyond the N64) @@ -208,9 +204,6 @@ Background research lives in the maintainer's local notes (gitignored): | Item | Status | Refs | Notes | |---|---|---|---| -| `All unlocked` rework: RAM override instead of save patching | done | D442, D387, D259 | **Landed 2026-09-30 (D442):** query-time hooks in `file2.c`, RULE-2-SIGNOFF recorded; the EEPROM patch and D387 merge are removed. **Verified 2026-10-03 (headless):** ON = all 20 levels and cheats unlocked; OFF = only real progress; `ge007.eep` byte-identical. Delete this row once released. | -| Settings units and values cleanup | done | D357, D443 | **Landed 2026-09-30 (D443):** mouse ×, whole-% deadzone/volume/vibration, FOV in horizontal degrees, MSAA 16×; verified 2026-10-03 (both UIs show the same values). FPS presets above 60 wait for the frame-rate-above-60 work. Delete this row once released. | -| Restart button in the settings menus | done | D443 | **Landed 2026-09-30 (D443):** a "Restart game" row (orderly D344 quit, then relaunch). **Owed:** click it once on Windows (no BSOD); the Linux relaunch path has never been compiled. | | F10 / PC Options menu: PD-style dialog refactor + watch-consolidation review | verify | D504-D512, `MENU-PD-ALIGNMENT-PLAN.md` (maintainer's local notes) | **Built 2026-10-04 (D504); follow-ups merged into `release/v0.4.1`:** D505/D506/D507 (wording, real-unit sliders, descriptions); D508 forced aspect ratios (16:9 / 21:9); D509 2-frame hold after an aspect change (rare gun glitch = known issue); D510 overlay + FPS counter same size front end vs level; D512 `Game.HudScale` scales the F10 overlay. **Owed (live pass, `docs/dev/notes/MENU-LIVE-CHECKLIST.md`, launches A/B/C):** look-and-feel vs PD; mouse, keyboard and pad in a level and on the front end; HudScale 75/100/150; 21:9; display-mode round trip across a restart; split-screen 2P with the overlay open. **Gaps:** background blur (post-1.0, §5c); per-seat control scheme is #139 / D513. Watch consolidation: not cheaply feasible (parked as T5). | | Defaults/presets consolidation: one `Display mode` switch | verify | D504, D440, `MENU-PD-ALIGNMENT-PLAN.md` §5 (local notes) | **Landed 2026-10-04 (D504):** the derived `__DisplayMode` row shows Modern / Original N64 / Custom (no ini key); F10 dropdown + left/right stepping, front end steps too; one reset row per dialog covering exactly its keys; editing a non-bundled key does not flip Display mode. **Decided 2026-10-03: tri-state**, flipping to Custom when the user edits a bundled key. **Owed (live):** display-mode round trip across a restart; read of the regrouped pages and wording (MENU-LIVE-CHECKLIST.md A1/A2/A4). | | Control style Original shows the game's own N64 styles | verify | #139 follow-up, D516, D517 | **Landed 2026-10-04 (D516, merged @ 91595597):** "Controller style" row (Original only; the game's own 1.1-2.4 per seat, saved to the Bond file; front end reads "(in a mission)"); D498 presets hidden in Original. **D517:** hold-repeat on any Bond row dropped fps (EEPROM rewrite per detent); now staged like sliders. Live passes 2026-10-04 (maintainer): single player + split-screen 2P with different styles per seat. **Owed:** the 2.x second-pad pass (real second controller). | @@ -220,9 +213,7 @@ Background research lives in the maintainer's local notes (gitignored): | Gamepad presets: duplicate-binding warning, Xbox stick behaviours | open (after v0.5.0; documented in the README) | D469, D498, D518, D394 | D469 (pad action table, Jinx 1.1 default, Custom per-seat bindings), **D498** (Xbox 1.2 Christmas / 1.3 Frost / 1.4 Elektra) and the per-seat preset (**D518**) are done and maintainer-verified with pads. Not done (maintainer 2026-10-04: skip in game for v0.5.0, document only): a warning when two actions share a button; the Xbox "left stick aims the crosshair" and "D-pad copies the left stick" behaviours (D394). | | Pad button names on a Nintendo pad | verify | D471; local note `PAD-MENU-AUDIT.md` | Audit done 2026-10-01 (no controller-only trap; D472 fixed G1-G3). D471 family button names + player LEDs: **PS5 DualSense verified 2026-10-03.** **Owed:** a Nintendo (Switch) pad check; the maintainer has no such pad, so this waits for one or for a user report. | | Mute and auto-pause on focus loss; PNG screenshots to `screenshots/` | parked | D231, PR #56 (closed unmerged), `docs/dev/notes/parked/0001-QoL-*.patch` (local) | Deprioritised by the maintainer on 2026-09-25. F12 today is the dev PPM dump. | -| FPS tanks while adjusting F10 music volume with a controller | done | D489 | Maintainer, 2026-10-02 (seen on PAL; general, also on v0.4.0). Cause: every pad/arrow slider step saved to the EEPROM (3 joy-poll handshakes + file rewrite on the game thread). Fixed: steps stage, save once on release; merged into `release/v0.4.1` 2026-10-03. Maintainer pad check passed 2026-10-03. Delete this row once released. | | Frame rate above 60 (120/144/uncapped with interpolation) | decision | [`dev/UNLOCKED-FPS-PLAN.md`](dev/UNLOCKED-FPS-PLAN.md) | The menu cap is {30, 60} (the ini accepts 0 = uncapped, but the sim ticks at the VI rate). The plan's Phase 0 is resolved by D248 (the default was a port bug; it is a real 60 fps) and its Phase 1 audit and Phase 2 slice scope are written (landed 2026-09-30), but the header still reads parked. Next step is a standing sim/render-rate measurement, not the old 30 fps investigation. Must stay compatible with netplay determinism. | -| Swapping saves with emulators | done (D492) | D492; `tools_pc/eep_convert.py`; README "Using an emulator save" | **Implemented 2026-10-04:** `tools_pc/eep_convert.py` (`n64-to-pc`, `pc-to-n64`, `verify [--as n64|pc]`, auto-detect), bundled as `tools/eep_convert.py` in both release packages; README section added. **Why (D492):** the port stores checksum words and `options` little-endian, so an unconverted 1964 save fails validation and all five slots are wiped; converting (swap `options`, recompute the checksum pair) was proven in a headless A/B. On-disk format unchanged. Verified byte-identical and round-trip. | | PD Extended Options parity: settings that need new dev work | done (reviewed 2026-10-04; two rows deferred) | `MENU-PARITY-SONNET-PLAN.md` §3 (local notes); pd_port `port/src/optionsmenu.c` @ 32a1cb9 | **Reviewed 2026-10-04 (maintainer decisions; local notes `PD-PARITY-DESIGN-REVIEW.md`, `WATCH-SCREEN-RATIO-DESIGN.md`).** **Done:** D511 fullscreen mode, Center window, crosshair opacity and color by health (live check owed, MENU-LIVE-CHECKLIST.md A4/A5/B3). **Watch Screen size/Ratio:** stay on the watch, no PC rows; the game saves watch settings on watch close (D349 correction) and they don't fight Aspect ratio (README). **Deferred past v0.5.0:** mouse lock mode (Auto/Always/Off; aim lock itself = `Input.AimMode`, D337); per-seat controller device select (needs a stable pad id; MP input layer). **Closed:** edge deadzone = `Input.AimRange` (D338); crosshair sway N/A (GE has none; gun sway is D428); use key reloads, no toggle (Ext keeps D378's GEPD split, Control style Original gives the N64 behaviour); per-seat options/binds (pad binds already per seat, D469/D518; keyboard/mouse is one device); no Detail textures toggle (`Video.DetailBaseTile` is the D236 fix); N/A confirmed: HUD centering (automatic, D335b), glare, overexposure, GE64 muzzle flashes, MP death music, radial menu speed, language filter, crosshair speed. **Other rows:** analog movement/stick scale/crouch → #139, T7; tickrate/framerate limit → frame rate above 60. | | One PC options UI: front-end "PC Options" opens the F10 overlay over the folder graphic | verify | D519, D343, D504, D510, D512 | **Landed 2026-10-04 (D519, branch `feat/one-options-ui`):** the file-select "PC Options" entry opens the F10 overlay over file select; the `MENU_PC_OPTIONS` screen and four old probes are gone; Bond-file rows and the watch chooser are keyed to file select. Headless checks pass (D519). **Owed:** by-eye mouse, keyboard and pad pass on file select; profile chooser + Controller style with a real profile; footer text overlaps the Copy/Erase bar. | | Emulator saves load directly in the game (no tool for import) | verify | D514, D492; `port/src/eepimport.c` called from `geEepromLoad`; D297 `legacycrc.c` same pattern | **Landed 2026-10-04 (D514, merged into `release/v0.4.1`).** Copying a 1964/Project64 `.eep` in as `data/ge007.eep` works: on first launch each region that validates in emulator format and not port format is converted in place (same rules as `eep_convert.py`); regions invalid in both are left to the game's wipe; port saves untouched; the original is backed up to `ge007.eep.emulator.bak`. Export stays with `eep_convert.py`. Headless verify 6/6 byte-identical to the in-game-proven reference. **Owed:** by-eye mission select after importing a real 1964 save. | @@ -277,20 +268,22 @@ and a stage loader. | Item | Status | Refs | Notes | |---|---|---|---| -| Security review follow-ups | done | maintainer's local security review | All items done on `release/v0.4.1`: the Low hardening items, the Medium item (self-hosted workflow input handling), actions pinned by SHA, `pyinstaller` pinned to 6.22.3, and the ROM no longer stored in `actions/cache` by `selfhosted.yml` (staged from the runner's `_rom-seed` dir only). Dependabot bumps (#134, #135) landed as SHA pins. | -| 1964-vs-port comparison harness | partial | `tools_pc/abpad/` | **2026-10-02/03:** one millisecond timeline drives 1964 (virtual XInput pad) and the port (`GE_INPUTSCRIPT_MS`), both windows captured on one clock with side-by-side sheets; verified through intro skip, file select and folder open. Not frame-identical (1964 has no seed pin or frame dump). Menu scripting dropped for `live_ab.ps1` (position-only, one real pad, `GE_PAD_BACKGROUND=1`). **Left:** in-level timelines (e.g. the D486 turret check); the two sides use different save files. | -| Bond seems to move faster on the port than on 1964 (live A/B) | closed (2026-10-04, maintainer) | — | Seen 2026-10-03 in a live one-pad A/B. Closed as an input-mapping difference, not game speed: the GEPD 1964 bundle is set up for mouse and keyboard and the port then forced 1.2 SOLITARE (D513/D516 now offer the game's own styles). A valid future A/B is the port on a pad against a standard emulator with plain pad input. | -| Scripted-playthrough harness | post-1.0 (maintainer 2026-10-04: not a good use of current LLMs; revisit with newer models) | `GE_INPUTSCRIPT`, D117 | Input script + `GE_PCDUMP` + crash-log check for unattended regression runs. **Maintainer concern (2026-10-03):** aiming, PRNG guards and timed events may block open-loop scripts, and runs aren't deterministic (D117). Levers: Invincibility + Invisibility cheats, closed-loop waypoint steering, pinned seed (`GE_RSEED`), coverage routes. Spike: one Dam route x5, measure end-position spread, then go/no-go. | +| 1964-vs-port comparison harness | partial | `tools_pc/abpad/` | One millisecond timeline drives 1964 (virtual XInput pad) and the port (`GE_INPUTSCRIPT_MS`); both windows captured on one clock with side-by-side sheets, verified through intro skip, file select and folder open. Not frame-identical (1964 has no seed pin or frame dump). **Left:** in-level timelines (e.g. the D486 turret check); the two sides use different save files. | +| Scripted-playthrough harness | post-1.0 (maintainer 2026-10-04: not a good use of current LLMs, revisit with newer models) | `GE_INPUTSCRIPT`, D117 | Input script + `GE_PCDUMP` + crash-log check for unattended regression runs. Levers: Invincibility + Invisibility cheats, closed-loop waypoint steering, pinned seed. Spike: one Dam route x5, measure end-position spread, then go/no-go. | | Cheap worker for mechanical, build-checked tasks | open | `delegate-local` MCP; memory of past Qwen issues | Birdman64's fast worker model wrote 44% of its commits while the lead planned and reviewed. Ours: pattern-applied edits (D441 fix batches, scaffolding strips, small tools), each gated by a clean build + a specific check + the lead's diff review, in the worker's own worktree. Local Qwen for edit-only tasks (it fabricates on reading tasks), or a hosted fast model via `delegate_to_provider` (the maintainer's cost call). | -| Golden baselines | partial | `tools_pc/verify.sh`, `tools_pc/golden/README.md`, D117/D522/D529/D531 | **Windows gate GREEN (2026-10-05 re-base):** 21/21 PASS honest sequential. The recipe pins the save CONTENT (D529: `verify.sh` installs the canonical in-tree save `tools_pc/golden/ge007.eep` per level, the same way it pins the ini — D523's "presence" claim was refuted by the D529 A/B) and per-level windows — `900-1500:300` on 14 levels, `1000-1400:200` on Surface 2/Streets/Depot/Cradle/Frigate, `1500-1700:100` on Dam — because the 900 stem sits inside the wall-clock-paced intro flyby (D117; dam measured 1.6–7.2 % run to run). No tol limit raised. **Linux set (D525 → D530 → D531):** the 2026-10-04 round cleared the save, the box re-rounded 2026-10-05 (D530 — but under the box-LOCAL save: `capture_p7.sh`'s per-level re-pin silently overwrote D529's canonical install, the D531 blocker), and D531 (2026-10-07) fixed the tool and re-captured all 21 levels under the canonical eep (47/63 frames moved). **Owed:** the pixel-gated linux verify — the box's WSLg GPU display is session-state-dependent (its 10-05 capture was GPU-backed; 10-07's session reads back black even with `DISPLAY=:0`, so `verify.sh`'s probe fails closed to crash-detect); run `verify.sh sweep` on the box when its GPU display is up and check the four D117 flaky levels' first stems (never-do 6: exclude/widen, don't re-capture). | +| Golden baselines | partial | `tools_pc/verify.sh`, `tools_pc/golden/README.md`, D117/D522/D529/D531 | **Windows gate GREEN (2026-10-05 re-base):** 21/21 PASS honest sequential. The recipe pins the save CONTENT (D529: `verify.sh` installs the canonical in-tree save `tools_pc/golden/ge007.eep` per level, the same way it pins the ini — D523's "presence" claim was refuted by the D529 A/B) and per-level windows — `900-1500:300` on 14 levels, `1000-1400:200` on Surface 2/Streets/Depot/Cradle/Frigate, `1500-1700:100` on Dam — because the 900 stem sits inside the wall-clock-paced intro flyby (D117; dam measured 1.6–7.2 % run to run). No tol limit raised. **Linux set (D525 → D530 → D531):** the 2026-10-04 round cleared the save, the box re-rounded 2026-10-05 (D530 — but under the box-LOCAL save: `capture_p7.sh`'s per-level re-pin silently overwrote D529's canonical install, the D531 blocker), and D531 (2026-10-05) fixed the tool and re-captured all 21 levels under the canonical eep (47/63 frames moved). **Cross-platform spread recomputed 2026-10-05** from the committed pairs (`framediff.py` win-vs-linux at the stems, tol 2): exact **0.380–5.120 %** on the 20 non-Cuba levels (silo's 900 stem is the outlier: cell mean 34.3, phash 10; streets 3.223–4.566), structural **21/21 clean**, max phash 40 (cradle, at threshold); Cuba 1.357–12.663 % is the structural-tier-only level. The D525 figures above predate the D531 linux re-capture. **Owed:** the pixel-gated linux verify — the box's WSLg GPU display is session-state-dependent (its 10-05 capture was GPU-backed; 10-07's session reads back black even with `DISPLAY=:0`, so `verify.sh`'s probe fails closed to crash-detect); run `verify.sh sweep` on the box when its GPU display is up and check the four D117 flaky levels' first stems (never-do 6: exclude/widen, don't re-capture). | | Flicker scan (A/B/A frame alternation) | parked (2026-10-03: tooling, nothing blocked on it; spec in the local investigation report `scratch/birdman64-rows-investigation.md` §4) | `GE_PCDUMP`, `GE_INPUTSCRIPT` | Dump **every** frame of a scripted run at low resolution and flag frame n when it differs from n-1 but matches n-2. For any old-vs-new pixel diff outside the golden gate, run a same-commit control first. Reference: Birdman64 `tools/flicker_scan.py`. | | Release build-provenance attestation | verify | `.github/workflows/ci.yml` release job; `.github/SECURITY.md`; README Status section | **Implemented 2026-10-04:** SHA-pinned `actions/attest-build-provenance` attests `dist/*.zip[.sha256]` + `dist/*.tar.gz[.sha256]` in the tag-only release job (`id-token: write` + `attestations: write` on that job only). Backs SECURITY.md's "unsigned, but reproducible" stance with `gh attestation verify --repo jkdansereau/goldeneye-pc-port`; documented in SECURITY.md and the README. **Owed:** the v0.5.0 tag build is the first real run; confirm the step passes and verification succeeds on a downloaded asset, then delete this row. | | Self-hosted CI runner link failure | open | D244 | Needs a `pacman -Syu` on the runner (maintainer-only access). Release tag builds use GitHub runners, so it doesn't block a release. | | Remove temporary scaffolding before a release | partial | [`dev/GE-ENV-PROBES.md`](dev/GE-ENV-PROBES.md), D302 | Passes 2026-09-30 to 2026-10-03 removed the closed-finding probe suites (registry in `GE-ENV-PROBES.md`, `gen_env_probes.py --check` passing); TEMP probes `GE_TESTSTYLE` / `GE_D516` removed 2026-10-04. **Left:** D104 (kept while D294 is in verify); the M-183/M-185 clamps, **in scope 2026-10-03 (maintainer)**: re-verify all scripted-camera cutscenes with the clamps compiled out (intro flyover, per-level endings incl. the Dam abseil), then delete both blocks in `model.c`. | -| P12 U5 review of `tools_pc/**` (release-diff, B10) | partial | `docs/dev/notes/RELEASE-BLOCKERS-v050.md` §F, D531 | **2026-10-07 (local session):** U5 reviewed (the unit that owed the P7 merge). **Blocker found + fixed in-tree:** `capture_p7.sh`'s per-level re-pin restored the local save, so the D530 box re-round ran under the box-local save — D531 (fixed + re-captured). **Applied 2026-10-07 as D532 (tooling-only):** (1) `verify.sh`'s `pin_ini_640x480` now fails closed like `pin_eep` (the old `2>/dev/null || true` call sites ran the gate under an unpinned ini on write failure — a spurious-verdict source); (2) `GOLDEN_SEED` hard-pinned with a warning for a differing stray `GE_RSEED` (the old `:-` form let the env var re-seed the gate; capture's `eval grep` still picks up the same line, so capture and gate stay consistent); (3) `audiodebug.ps1`'s dead `-AB`/`-Old` switches removed with a one-line note. **Still open on this row:** the pixel-gated box verify in a WSLg GPU-display window (D531) — crash-detect only until then. Clean: `p7_report.py`, `eep_convert.py`, `abi_ratchet.py`, `docs_budget.py`, `abpad/`, the D258/D416/PII#7 tool diffs, probe/capture deletions (`scan_op12/13.py`, the 433k-line `sweep-captures/` PPM set). | -| Findings log and doc size | partial | `tools_pc/docs_budget.py` | The budget checker exists (per-file and tier-1 token budgets; `--toc FILE`). ROADMAP.md was trimmed under budget 2026-10-04. `findings.md` is still about 2 MB (~500k tokens), so agents must keep using the index. Nothing blocks; trim only if the budget check starts failing. | +| P12 U5 review of `tools_pc/**` (release-diff, B10) | partial | D531/D532/D533 | Reviewed 2026-10-05 (the unit that owed the P7 merge). **Blocker found + fixed (D531):** `capture_p7.sh`'s per-level re-pin restored the local save, so the D530 box re-round ran under the box-local save — fixed + re-captured under the canonical eep (47/63 frames moved). **Should-fixes applied as D532 (tooling-only):** `verify.sh`'s `pin_ini_640x480` fails closed like `pin_eep`; `GOLDEN_SEED` hard-pinned with a warning for a stray `GE_RSEED`; `audiodebug.ps1`'s dead `-AB`/`-Old` switches removed. **Still open:** the pixel-gated box verify in a WSLg GPU-display window (D531) — crash-detect only until then. | +| Findings log and doc size | partial | `tools_pc/docs_budget.py` | Budget checker (per-file and tier-1 token budgets; `--toc FILE`). `findings.md` is still about 2 MB (~500k tokens), so agents must keep using the index. Nothing blocks; trim only when a budget check fails. | | Branch and PR housekeeping | open | — | 30 landed local branches pruned 2026-09-30 (tips in `prune-branches-2026-09-30.txt`, notes archive). **On hold by the maintainer (do not prune yet):** probe-only `d243-verify-local`, `investigate/d243-cutscene-race`, `park/d236-probes-m139`. Local `main` and `release/v0.4.1` are deliberately not pushed. PR #62 (mouse dt-decouple) stays held until mouse feel is fundamentally better. | | Docs site: Web 1.0 phase 2 | decision | — | Phase 1 shipped. Phase 2 (centred serif page) is plan-only. | +| Docs site: press/SEO pass (2026-10-05) | partial | `docs/` site pages | Landed on the pure site pages + presentation layer: real `

` masthead on every page, `color-scheme`/`theme-color`/`max-image-preview`/`rel=me` in the layout, ink-mute >=4.5:1, focus-visible + selection, 15px body, mobile padding; `date`/`modified_time` front matter on the pure pages. Home-page additions (elevator-pitch pull-quote, Overview fact table, press/coverage section) were **removed on review 2026-10-05** — each restated the tagline/intro or duplicated a section below. The five short topic notes (the-software-rsp, the-asset-pipeline, framerate-and-pacing, input-and-aim, saves) were dropped from the published set (the PD port documents the same subjects; files stay in the repo as working material). **Open:** the same front matter on the *shared* docs (`internals.md`, `porting-notes.md`, `building.md`, `dev-process.md`, `dev/agentic-development.md`) is deliberately untouched — authoritative working docs, not edited for site polish. | +| Agentic-development deep dive (doc) | queued — not yet (2026-10-05) | `docs/dev/agentic-development.md` | Write-up owed for the public case study: per-agent commit estimates / work estimates (the manual attribution pass over the session log, incl. the D350-style commit-date divergences), a code-quality audit of the trial models (GPT-6 (Sol) et al.), and the process feedback / findings. Deferred by the maintainer; pick it up when scheduled — nothing here blocks. | +| Docs site: split Security & fidelity -> two pages + v0.5.0 update pass (2026-10-05) | done (local, unpushed) | `docs/security.md`, `docs/fidelity.md` (was `security-and-fidelity-status.md`) | Split on review (the homepage pointer sat near Known issues and read as if there were security *issues*); both pages cross-linked and linked from the hub and index.md's Download section. Update pass 2026-10-05 against the `release/v0.4.1` tree: no network includes/symbols/HTTP literals in compiled `src/`+`port/`; no registry or library-loading calls; converter imports no network module; bundle manifests re-checked (allowlist + ROM/oversized-blob hard-fail); region macros re-compared vs the Makefile (all three regions); library classification re-checked (CMake globs libultra+libultrare audio, gu, `io/vitbl.c` only). Full game-logic audit not re-run; recent `src/game` edits are finding-gated. | +| Docs site: final sanity sweep + anchor fixes (2026-10-05) | done (local, unpushed) | `docs/porting-notes.md` (3 TOC hrefs), `docs/404.html` (permalink) | Audit of the 10-page published set: one h1 per page, no heading-level skips, all 42 in-page anchors resolve, titles/descriptions unique and <=160 chars, og:image/canonical present, JSON-LD parses, no local paths/usernames, served == built. Three `porting-notes.md` TOC hrefs carried a double hyphen where kramdown emits one (headings whose punctuation leaves a single separator: `(32->64):`, `D114/D116):`, `"off":`) — dead on the live GitHub Pages site, corrected at source against the live ids. `docs/404.html` needed `permalink: /404.html` (else canonical/og:url read `/404.html.html`). Preview-builder slug rule fixed to kramdown: strip punctuation, map **each** whitespace char to `-` (so ` / ` -> `--`), no run-collapsing; headings with inline markup now get ids too. | ## Community forks (not ours; reference only) diff --git a/docs/_config.yml b/docs/_config.yml index e3c655bf..52a699d7 100644 --- a/docs/_config.yml +++ b/docs/_config.yml @@ -36,19 +36,29 @@ plugins: # --------------------------------------------------------------------------- # The site publishes a small curated set: -# index.md, under-the-hood.md (the "under the hood" hub), building.md, plus +# index.md, documentation.md (the documentation hub), building.md, plus +# 404.html (themed not-found page), # the four docs grouped behind that hub: internals.md, porting-notes.md, -# dev-process.md, dev/agentic-development.md, the five short topic notes -# grouped under it (the-software-rsp.md, the-asset-pipeline.md, -# framerate-and-pacing.md, input-and-aim.md, saves.md), and -# security-and-fidelity-status.md (linked from index.md's Known issues). -# Everything else under docs/ is working material - the finding log, level -# status, backlogs, plans, session notes, inherited decomp guides. It stays in -# the repo (and on GitHub) but is excluded from the site build so it never -# shows up as a published page or in sitemap.xml. +# dev-process.md, dev/agentic-development.md, and +# security-and-fidelity-status.md (split on review 2026-10-05 into +# security.md + fidelity.md, linked from index.md's Download section and +# the hub). +# The five short topic notes (the-software-rsp.md, the-asset-pipeline.md, +# framerate-and-pacing.md, input-and-aim.md, saves.md) were dropped from the +# site on review 2026-10-05: the Perfect Dark port documents the same +# subjects and nothing here was significantly new; they stay in the repo as +# working material. Everything else under docs/ is working material - the +# finding log, level status, backlogs, plans, session notes, inherited +# decomp guides. It stays in the repo (and on GitHub) but is excluded from +# the site build so it never shows up as a published page or in sitemap.xml. # NOTE: keep this list in sync as dev/ working docs are added; a new unlisted # .md under docs/ is silently published to the site + sitemap.xml. exclude: + - the-software-rsp.md + - the-asset-pipeline.md + - framerate-and-pacing.md + - input-and-aim.md + - saves.md - HANDOFF.md - ROADMAP.md - SetupGuide.md diff --git a/docs/_includes/head-custom.html b/docs/_includes/head-custom.html index 2bef12bf..9b7c82cb 100644 --- a/docs/_includes/head-custom.html +++ b/docs/_includes/head-custom.html @@ -21,7 +21,9 @@ --bg-panel: #121316; /* code / cards / tables */ --ink: #e6e4df; /* warm off-white */ --ink-soft: #a9a7a0; /* secondary text */ - --ink-mute: #70706a; /* captions, footer */ + --ink-mute: #8a8a83; /* captions, footer (>=4.5:1 on --bg-frame: + the old #70706a measured 3.9:1 and failed + WCAG AA on the tagline + legal line) */ --line: #26282c; /* 1px borders */ --line-2: #3a3d44; /* stronger borders */ --accent: #e8a13a; /* GoldenEye gold (amber) */ @@ -40,8 +42,9 @@ margin: 0; color: var(--ink); font-family: var(--sans); - font-size: 14px; - line-height: 1.62; + font-size: 15px; /* 14px read as console chrome, but the + body is prose people actually read */ + line-height: 1.6; /* faint starfield (the old-school "space" tile, softened) */ background-color: var(--bg); background-image: @@ -63,6 +66,14 @@ } a:hover { color: var(--accent-hi); text-decoration-color: currentColor; } + /* keyboard focus + selection: the amber thread again, so a focused + control is visible on the near-black ground (a11y / keyboard users) */ + :focus-visible { + outline: 2px solid var(--accent-hi); + outline-offset: 2px; + } + ::selection { background: var(--accent); color: #0b0b0d; } + /* ---- the centred "page" frame -------------------------------------- */ .frame { max-width: 50rem; @@ -75,11 +86,13 @@ /* ---- banner -------------------------------------------------------- */ .banner { text-align: center; padding: 2.4rem 1rem 1.4rem; } .banner .t { + margin: 0; /* it is an

now (see the layout) */ font-family: var(--mono); font-weight: 700; font-size: 1.7rem; letter-spacing: .05em; - color: var(--ink); + color: var(--accent); /* brand name in goldeneye gold -- the amber + thread (masthead -> nav -> rule -> page title) */ } .banner .tag { color: var(--ink-mute); font-size: .82rem; margin-top: .55rem; } @@ -131,6 +144,23 @@ border-bottom: 1px solid var(--line-2); } .doc-content h2::before { content: "\25aa "; color: var(--accent); } + /* Page-title look: the 2026-09 SEO pass demoted page headings to h2 + (the masthead owns h1; no duplicate page titles) -- see the SEO + conventions note in scratch/pr-seo-revise-prompt.md. The first + content h2 of each page gets the h1 look; presentation only, the + heading levels stay per-convention. (On the home page the masthead + itself is the title, so its first h2 -- Status -- leads as a section.) */ + /* .flat-title (front matter on the home page): the masthead IS the + page title there, so the first h2 must read as a plain section -- + otherwise masthead-h1 + amber first-h2 stack as a double header. */ + .doc-content:not(.flat-title) > h2:first-of-type { + font-size: 1.5rem; + font-weight: 700; + color: var(--accent); + margin: .2rem 0 1rem; + padding-bottom: 0; + border-bottom: 0; + } .doc-content h3 { font-family: var(--mono); font-size: .92rem; @@ -189,21 +219,88 @@ } .doc-content tr:nth-child(2n) td { background: var(--zebra); } + /* ---- console blocks ------------------------------------------------ */ + /* Status / News / Download / Known issues panels (home page). Same + amber-terminal language as the code blocks above, but the content + keeps its semantic list/table markup (SEO + screen readers). + + Site formatting rules (2026-10-05 PR pass + 2026-09 SEO conventions) + — keep these: + - Heading LEVELS follow the SEO pass: the page heading is h2 (the + masthead owns h1; no duplicate page titles), sections are h2, + h3 labels a real sub-topic only (e.g. the current-release line + under Status). The first content h2 gets the amber "page-title" + look via CSS only (.doc-content > h2:first-of-type) -- never by + promoting the markup to h1. + - h2 = the section, outside the panel. + - Directives ("always use the latest release") are .callouts inside + panels, or a .warn line (amber rule, ink text) where no panel + exists; never headings — headings label topics, + never emphasis. + - List intros ("what a player will notice…") stay plain paragraphs. + - Dated list-item leads ("2026-10-05 — v0.5.0") stay leads + inside the
  • , not headings. + - Headings must be single source lines: marked ends an ATX heading at + the line break, so a wrapped "### …" silently becomes a heading plus + a stray paragraph (Jekyll/other renderers differ — don't rely on + multi-line headings at all). */ + .doc-content .cblock { + background: var(--bg-panel); + border: 1px solid var(--line); + padding: 1.1rem 1.3rem .9rem; + margin: 1.6rem 0; + } + + /* Expandable panels (
    , e.g. the release history folded under + Status): same panel language as .cblock; the summary line is the + amber handle. The content is in the DOM either way -- collapsing is + presentation, not hiding from search. */ + .doc-content details { + background: var(--bg-panel); + border: 1px solid var(--line); + padding: .3rem 1.3rem .8rem; + margin: 1.6rem 0; + } + .doc-content summary { + font-family: var(--mono); + font-size: .9rem; + font-weight: 600; + color: var(--accent); + letter-spacing: .03em; + cursor: pointer; + } + .cblock h3 { margin-top: 0; color: var(--ink); } + .cblock p, .cblock ul, details p, details ul { + font-family: var(--mono); + font-size: .88rem; + line-height: 1.6; + } + .cblock li, details li { margin: .5rem 0; } + .cblock li::marker, details li::marker { color: var(--accent); } + .cblock strong, details strong { color: var(--accent); font-weight: 600; } + .cblock code, details code { background: transparent; border: 0; padding: 0; } + .doc-content .callout { + border-left: 3px solid var(--accent); + padding: .15rem 0 .15rem .8rem; + margin: 0 0 1rem; + } + .doc-content .callout p { color: var(--ink-soft); } + + /* Inline directive marker -- "Warning:" leading a sentence, amber like + the rest of the accent thread, no panel around it. */ + .doc-content details .warn { + border-left: 3px solid var(--accent); + padding: .1rem 0 .1rem .7rem; + margin: .2rem 0 .9rem; + font-weight: 600; + } + .cblock table { background: transparent; margin: 1rem 0 .4rem; } + .cblock > p:last-child { margin-bottom: 0; } + /* ---- images -------------------------------------------------------- */ .doc-content img { border: 1px solid var(--line); border-radius: 0; max-width: 100%; - } - - /* ---- details / summary (the "full gallery" collapse) --------------- */ - .doc-content details { - max-width: var(--measure); - border: 1px solid var(--line); - background: var(--bg-panel); - padding: .6rem .9rem; margin: 1.5rem 0; - } - .doc-content summary { - cursor: pointer; color: var(--accent); - font-family: var(--mono); font-size: .85rem; letter-spacing: .03em; + height: auto; /* no squashed stills when a width= is set */ } /* ---- console-viewer carousel (home page media) --------------------- */ @@ -261,7 +358,11 @@ @media (max-width: 40rem) { .frame { margin: 8px; } + .banner { padding: 1.4rem .8rem 1rem; } .banner .t { font-size: 1.35rem; } + .doc-content { padding: 1.1rem 1rem .5rem; } + .doc-content table { font-size: .82rem; } + .doc-content pre { padding: .7rem .8rem; } } diff --git a/docs/_layouts/default.html b/docs/_layouts/default.html index 2bbc8775..79acc088 100644 --- a/docs/_layouts/default.html +++ b/docs/_layouts/default.html @@ -3,26 +3,44 @@ + + + + + + + {%- seo -%} {%- include head-custom.html -%}
    + -
    - in-engine capture + GoldenEye 007 PC port — Dam (v0.4.0 intro attract), in-engine capture
    Dam — v0.4.0 intro attract
    -
    -Full gallery: 12 in-engine captures from a v0.4.0 intro-attract pass (3072×1728 widescreen, Sep 2026) - -

    -

    - In-engine: Dam (v0.4.0 intro attract) -
    Dam
    -
    -
    - In-engine: Runway (v0.4.0 intro attract) -
    Runway
    -
    -
    - In-engine: Surface (v0.4.0 intro attract) -
    Surface
    -
    -
    - In-engine: Frigate (v0.4.0 intro attract) -
    Frigate
    -
    -
    - In-engine: Surface 2 (v0.4.0 intro attract) -
    Surface 2
    -
    -
    - In-engine: Statue (v0.4.0 intro attract) -
    Statue
    -
    -
    - In-engine: Archives (v0.4.0 intro attract) -
    Archives
    -
    -
    - In-engine: Cradle (v0.4.0 intro attract) -
    Cradle
    -
    -
    - In-engine: Aztec (v0.4.0 intro attract) -
    Aztec
    -
    -
    - In-engine: Control (v0.4.0 intro attract) -
    Control
    -
    -
    - In-engine: Streets (v0.4.0 intro attract) -
    Streets
    -
    -
    - In-engine: Jungle (v0.4.0 intro attract) -
    Jungle
    -
    -

    -
    - -More captures may land here as playtesting continues. - ## Play it, or take it apart - **Try it yourself**: grab a bundle above, bring your own ROM, and play the - campaign. It's an early cut for exactly this: if something breaks, an + campaign. It's a pre-1.0 build for exactly this: if something breaks, an [issue](https://github.com/jkdansereau/goldeneye-pc-port/issues) with what you were doing is genuinely useful. - **Read the code**: the game logic in `src/` is unmodified decompilation; @@ -172,80 +155,51 @@ More captures may land here as playtesting continues. asset sidecar. [CONTRIBUTING](https://github.com/jkdansereau/goldeneye-pc-port/blob/main/CONTRIBUTING.md) has the ground rules that keep it faithful to the original game. - **AI disclosure**: development here used AI coding agents — Claude Code - plus a local open-weight model on a single RTX 5090, directed by one person in their - spare time. The project is as much a study of that process as it is a - port, and the write-up is written to let you judge the result for yourself; - it records what happened and takes no position for or against using LLMs - on a project like this. See [the full + plus a local open-weight model on a single RTX 5090, directed by one person + in their spare time. The project is as much a study of that process as it + is a port; the write-up records what happened and takes no position for or + against using LLMs on a project like this. See [the full write-up](dev/agentic-development.md) — the setup, timeline, handoff workflow, and an honest account of what did and didn't work. ## How it works -The R4300 game code's logic and control flow are the decompilation's, and remain the -reference we preserve. The port's changes inside `src/` are few in kind (mostly mechanical 32→64-bit pointer fixes, a couple of approved timing fixes, and the opt-in *All unlocked* hook), each gated behind `#ifdef PORT`. The N64's Reality Signal Processor, the graphics -coprocessor that builds and executes each frame's display list, is emulated in -software: the port interprets the graphics command stream (GBI) the game emits and translates it -to OpenGL, bypassing the RDP entirely. Everything else that would touch N64 -hardware (video, audio, input, timers, save storage) is shimmed in a small -dedicated layer, following the architecture of the Perfect Dark PC port from -the same Rare engine family. Full detail: [Internals](internals.md); the bug +The R4300 game code's logic and control flow are the decompilation's, and +remain the reference we preserve. The port's changes inside `src/` are few in +kind and each is gated behind `#ifdef PORT`: mechanical 32→64-bit +pointer-width fixes, a couple of approved timing fixes, and the opt-in +*All unlocked* and *Skip intro* hooks. The N64's Reality Signal Processor +(RSP), the graphics coprocessor that builds and executes each frame's +display list, is emulated in software: the port interprets the graphics +command stream (GBI) the game emits and translates it to OpenGL, bypassing +the RDP entirely. Everything else that would touch N64 hardware (video, +audio, input, timers, save storage) is shimmed in a small dedicated layer, +following the architecture of the Perfect Dark PC port from the same Rare +engine family. Full detail: [Internals](internals.md); the bug catalogue: [Porting notes](porting-notes.md). -## Known issues - -Known defects and limitations as of v0.5.0, each with its finding-log -reference. The full list, with root causes and fix status: the -[release notes](https://github.com/jkdansereau/goldeneye-pc-port/releases) -and the [finding log](https://github.com/jkdansereau/goldeneye-pc-port/tree/main/docs/dev). - -- **Facility — gas leak during Ourumov's monologue (D318).** If gas leaks, - he can pause for up to ~10 s before the scripted shootout resumes. The - race exists in the N64 original too (where it softlocks permanently); - the port detects it and auto-recovers. -- **F10 options overlay under native widescreen (D335b).** The overlay - stretches with the window instead of pillarboxing like the front-end - menus. Legible, cosmetic only; the F10 *Native widescreen* toggle - restores the old stretched frame throughout. The world/HUD widescreen - rendering itself is correct. -- **Front-end Rareware logo (D75).** A subtle texture-filtering artifact; - the Nintendo logo and legal page are clean. Cosmetic only. -- **`All unlocked` is experimental (D387).** The fresh-install corruption - case is fixed, but saves made while it is on are not guaranteed - recoverable by switching it off — complete a level normally first, and - back up `data/ge007.eep` before enabling it. -- **No macOS or ARM builds yet** (both are on the - [roadmap](https://github.com/jkdansereau/goldeneye-pc-port#roadmap)); - today the release ships Windows, Linux and Steam Deck. Keyboard, mouse - and controller-button rebinding are all supported (defaults in the - [README's Controls section](https://github.com/jkdansereau/goldeneye-pc-port#controls)). - -What the release actually installs, and how faithfully it tracks the N64 -game: [Security & fidelity status](security-and-fidelity-status.md). - ## Documentation -The technical docs live under [under the -hood](under-the-hood.md) — the architecture, the N64→PC bug catalogue, and -how the project is developed. [Building](building.md) covers building and -running from source. +The technical docs live on the [Documentation](documentation.md) page — the +architecture, the N64→PC bug catalogue, and how the project is developed. +[Building](building.md) covers building and running from source. + + diff --git a/docs/input-and-aim.md b/docs/input-and-aim.md index af056086..60ccd252 100644 --- a/docs/input-and-aim.md +++ b/docs/input-and-aim.md @@ -1,6 +1,8 @@ --- title: Input and aim description: How the GoldenEye 007 PC port maps keyboard, mouse and modern controllers onto the N64's controller, including mouse aim. +date: '2026-10-05' +modified_time: '2026-10-05' --- ## Input and aim diff --git a/docs/internals.md b/docs/internals.md index 816750cc..48d24bc9 100644 --- a/docs/internals.md +++ b/docs/internals.md @@ -3,7 +3,7 @@ title: Internals description: Architecture behind the PC port; the software RSP-emulation approach, GoldenEye-vs-Perfect-Dark engine differences, and the phased development plan. --- -## GoldenEye 007 PC Port: architecture & plan +## Architecture & plan > This began as the pre-implementation research note and is kept as the > architecture reference. Sections 1–9 describe the design; section 10 lists diff --git a/docs/porting-notes.md b/docs/porting-notes.md index 3eb820d0..390e04ff 100644 --- a/docs/porting-notes.md +++ b/docs/porting-notes.md @@ -19,14 +19,14 @@ good that you are looking at one of these. ## Contents -- [A. Pointer-width struct growth (32→64); the dominant class](#a-pointer-width-struct-growth-3264--the-dominant-class) +- [A. Pointer-width struct growth (32→64); the dominant class](#a-pointer-width-struct-growth-3264-the-dominant-class) - [B. 16-byte PC `Gfx` / `Vtx` vs 8-byte N64](#b-16-byte-pc-gfx--vtx-vs-8-byte-n64) - [C. Big-endian rodata / ROM data read on little-endian PC](#c-big-endian-rodata--rom-data-read-on-little-endian-pc) - [C2. Port-layer / SDL shims](#c2-port-layer--sdl-shims) - [D. N64 hardware idioms fast3d does not emulate](#d-n64-hardware-idioms-fast3d-does-not-emulate) -- [D2. The HUD/model "X-mirror"; RESOLVED](#d2-the-hudmodel-x-mirror-d114d116--resolved-it-was-an-upside-down-capture) +- [D2. The HUD/model "X-mirror"; RESOLVED](#d2-the-hudmodel-x-mirror-d114d116-resolved-it-was-an-upside-down-capture) - [D3. GCC/mingw makes an all-non-negative `enum` UNSIGNED](#d3-gccmingw-makes-an-all-non-negative-enum-unsigned) -- [D4. N64 "interrupts off" must be a real lock on PC](#d4-n64-interrupts-off-is-not-free-on-pc--it-must-be-a-real-lock) +- [D4. N64 "interrupts off" must be a real lock on PC](#d4-n64-interrupts-off-is-not-free-on-pc-it-must-be-a-real-lock) - [D5. Loop bounds that assume linker adjacency of two globals](#d5-loop-bounds-that-assume-linker-adjacency-of-two-file-scope-globals) - [D12. Collapsing an LOD binding to tile 0 breaks a DETAIL binding](#d12-collapse-an-lod-binding-to-tile-0-is-wrong-for-a-two-texture-detail-binding-d236) - [D13. Per-render accumulation is not associative (1 vs 2–3 ticks/frame)](#d13-per-render-accumulation-is-not-associative-the-ports-1-tickframe-can-reach-states-the-n64s-23-ticksframe-never-did-d329) diff --git a/docs/saves.md b/docs/saves.md index 7d485111..b8510d73 100644 --- a/docs/saves.md +++ b/docs/saves.md @@ -1,6 +1,8 @@ --- title: Saves description: How the GoldenEye 007 PC port backs the N64's EEPROM save with a plain file you can back up. +date: '2026-10-05' +modified_time: '2026-10-05' --- ## Saves diff --git a/docs/security-and-fidelity-status.md b/docs/security-and-fidelity-status.md deleted file mode 100644 index 786fc1e3..00000000 --- a/docs/security-and-fidelity-status.md +++ /dev/null @@ -1,115 +0,0 @@ ---- -title: Security & fidelity status -description: What a release actually installs (no networking, no telemetry, no ROM or assets) -and how faithfully the port tracks the original N64 game's logic. ---- - -## Security & fidelity status - -This page answers two questions plainly: *what does installing this put on -your machine*, and *how faithfully does the port actually reproduce the -original N64 game's logic*. Both were reviewed end-to-end on 2026-09-15 and -re-verified for the v0.3.0 release bundles on 2026-09-18 (bundle contents, -no-networking source check, and the asset converter). The no-networking source -check was repeated against the `release/v0.4.1` tree on 2026-09-30 (no socket, -HTTP or name-resolution calls in `src/` or `port/`; neither the engine nor the -bundled `SDL2.dll` imports a networking DLL; no registry calls in the port's -own code); the bundle contents were not re-checked then. The full engineering -record behind each item is in the project's finding log -([`docs/dev/findings.md`](dev/findings.md)). - -## What a release actually installs - -Each release (`goldeneye-pc-port-*-win64.zip` / `-linux-x86_64.tar.gz`) -contains, and only contains: the game engine executable, a fixed set of -standard runtime libraries (SDL2, zlib, and the MinGW/Linux runtime — no -third-party downloads), the one-time ROM-to-PC asset converter, a README, -and license texts. The build scripts hard-fail if a ROM image or an -oversized blob ends up in the bundle. **No ROM, no game assets (textures, -audio, models, levels, text) are ever shipped** — you supply your own -legally-owned copy. - -At runtime, the engine: - -- has **no networking of any kind** (no sockets, no HTTP, checked directly - against the source), -- has **no telemetry, crash reporting, or analytics** of any kind, -- **never touches the Windows registry** and **never requests elevated - permissions**, -- writes save data, config, and derived ROM assets only inside its own - folder — never to `%APPDATA%`, `Program Files`, or any system location. - -The one-time asset converter (`ge007-convert` / `prepare-assets.py`) — the -part of the bundle most likely to draw suspicion, since it's the thing that -reads your ROM and writes new files — has no network access at all, -verifies your ROM against a fixed table of known retail GoldenEye 007 -hashes before touching it (an unrecognized file is rejected outright), and -writes only inside the bundle folder. Its full source ships right next to -the compiled version in every release if you'd like to read it yourself. The -engine starts it automatically on the first run, when the two derived asset -folders (`data/pcmodels-*`, `data/pccg-*`) are missing, passing it the ROM it -found and the output folder; after that first run it is not launched again. - -See [`SECURITY.md`](../.github/SECURITY.md) for why the binaries are -unsigned and what that means in practice (a SmartScreen prompt on first -run, and why some antivirus engines flag the converter specifically). - -## Building from source - -Cloning and building pulls in only standard, well-known packages: MSYS2 -`pacman` packages on Windows, `apt` packages on Linux — no `npm`, no -`cargo`, nothing downloaded at CMake configure time, no git submodules, and -no prebuilt binaries checked into the repository. The CI build freezes the release's asset-converter tool with `pyinstaller`, -which is pinned to an exact version, and every third-party GitHub Action the -workflows use is pinned to a full commit SHA (both from the release after -v0.4.0; earlier releases installed an unpinned `pyinstaller`). What -PyInstaller packages is entirely in-repo, stdlib-only Python, so a release is -reproducible from a diff of this repository. - -One development-only backdoor exists in source builds: the `GE_DEBUG_UNLOCKALL` -environment variable (`src/game/file2.c`) pre-unlocks all cheat options at boot. -It is off unless explicitly set, changes no shipped behavior, and is documented -here so the env-var surface stays fully accounted for. - -## Fidelity to the original N64 game - -The port's non-negotiable rule is that decompiled game logic is never -changed — only genuine N64-hardware dependencies get a PC-side -replacement, in the `port/` layer. This was audited directly: - -- **Region build macros** exactly mirror the original N64 Makefile's macro - sets for all three regions — verified line-by-line. -- **The audio/library file classification** (which original files compile - vs. get PC-shimmed) exactly matches the project's own ground-truth - manifest — no silent gaps, no silent duplicates. -- **The one shipped game-logic-adjacent change** (a portal-culling - float-precision fix, D271) is `#ifdef PORT`-gated, documented in the - finding log, and moves *toward* matching N64 behavior, not away from it. - Every other deviation from the decompiled source is one of the documented - 32→64-bit pointer-width ABI corrections described above. - -**One issue, found and fixed before v0.3.0 shipped**: the port's -random-number generator (`port/src/random.c`) was documented as a bit-exact -port of the N64's PRNG, but wasn't — a shift-operation helper didn't match -the real MIPS64 instruction semantics it was meant to mirror. This was -independently confirmed by hand-tracing the assembly against the C port, -tracked as finding D284, and fixed before the release (verified clean -across a full level sweep — all 20 missions plus the ending sequence — -no crashes). It affected loot placement, AI -behavior variance, and replay-state determinism from the very first random -draw, in every release before v0.3.0. Because of the fix, RNG-derived -output — including the save-file CRC — changed, which meant existing save -files and any previously recorded replays no longer validate; v0.3.0's -release notes describe the automatic save migration that handles this. We'd -rather say this plainly than let a "verified bit-exact" claim stand -uncorrected, both when it was wrong and now that it's fixed. - -Everything else checked (the documented ABI/pointer-width exceptions used -for the 32-to-64-bit transition, a sample of the finding log's own claimed -fixes against the actual current code) matched what's documented. - ---- - -*This page is kept current alongside future releases. Its source review is a -manual, human-reviewed process today — see the project roadmap for plans to -partially automate the mechanical parts of it.* diff --git a/docs/security.md b/docs/security.md new file mode 100644 index 00000000..cd040941 --- /dev/null +++ b/docs/security.md @@ -0,0 +1,87 @@ +--- +title: Security status +description: What a GoldenEye PC port release installs and doesn't: no networking, no telemetry, no ROM, no system writes — re-verified against the v0.5.0 source. +date: '2026-10-05' +modified_time: '2026-10-05' +--- + +## Security status + +This page answers one question plainly: *what does installing this put on +your machine*? The review was done +end-to-end on 2026-09-15 and re-verified for the v0.3.0 release bundles on +2026-09-18 (bundle contents, no-networking source check, and the asset +converter). The no-networking source check was repeated against the +`release/v0.4.1` tree on 2026-09-30 (no socket, HTTP or name-resolution +calls in `src/` or `port/`; neither the engine nor the bundled `SDL2.dll` +imports a networking DLL; no registry calls in the port's own code); the +bundle contents were not re-checked then. + +**v0.5.0 update pass, 2026-10-05:** the source checks were re-run against +the current tree — no socket/network includes, no network symbols and no +HTTP string literals in the compiled `src/` and `port/` sets; no registry +or library-loading calls in either; the converter's Python sources +(`prepare-assets.py` and its emit-script imports) import no network +module. The bundle contents were re-checked against the packaging +manifests (`tools_pc/bundle-win.sh` / `bundle-linux.sh` — the scripts CI +runs): an explicit allowlist of engine, runtime DLLs, SDL2, licenses and +converter tools, with a hard-fail guard against a ROM image or oversized +blob sneaking in. The v0.5.0 artifacts themselves were not re-unpacked +locally; they are built by the same scripts. The full engineering record +behind each item is in the project's finding log +([`docs/dev/findings.md`](https://github.com/jkdansereau/goldeneye-pc-port/blob/main/docs/dev/findings.md)). + +## What a release actually installs + +Each release (`goldeneye-pc-port-*-win64.zip` / `-linux-x86_64.tar.gz`) +contains, and only contains: the game engine executable, a fixed set of +standard runtime libraries (SDL2, zlib, and the MinGW/Linux runtime — no +third-party downloads), the one-time ROM-to-PC asset converter, a README, +and license texts. The build scripts hard-fail if a ROM image or an +oversized blob ends up in the bundle. **No ROM, no game assets (textures, +audio, models, levels, text) are ever shipped** — you supply your own +legally-owned copy. + +At runtime, the engine: + +- has **no networking of any kind** (no sockets, no HTTP, checked directly + against the source), +- has **no telemetry, crash reporting, or analytics** of any kind, +- **never touches the Windows registry** and **never requests elevated + permissions**, +- writes save data, config, and derived ROM assets only inside its own + folder — never to `%APPDATA%`, `Program Files`, or any system location. + +The one-time asset converter (`ge007-convert` / `prepare-assets.py`) — the +part of the bundle most likely to draw suspicion, since it's the thing that +reads your ROM and writes new files — has no network access at all, +verifies your ROM against a fixed table of known retail GoldenEye 007 +hashes before touching it (an unrecognized file is rejected outright), and +writes only inside the bundle folder. Its full source ships right next to +the compiled version in every release if you'd like to read it yourself. +The engine starts it automatically on the first run, when the two derived +asset folders (`data/pcmodels-*`, `data/pccg-*`) are missing, passing it +the ROM it found and the output folder; after that first run it is not +launched again. + +See [`SECURITY.md`](https://github.com/jkdansereau/goldeneye-pc-port/blob/main/.github/SECURITY.md) for why the binaries are +unsigned and what that means in practice (a SmartScreen prompt on first +run, and why some antivirus engines flag the converter specifically). + +## Building from source + +Cloning and building pulls in only standard, well-known packages: MSYS2 +`pacman` packages on Windows, `apt` packages on Linux — no `npm`, no +`cargo`, nothing downloaded at CMake configure time, no git submodules, and +no prebuilt binaries checked into the repository. The CI build freezes the +release's asset-converter tool with `pyinstaller`, which is pinned to an +exact version, and every third-party GitHub Action the workflows use is +pinned to a full commit SHA (both from the release after v0.4.0; earlier +releases installed an unpinned `pyinstaller`). What PyInstaller packages +is in-repo, stdlib-only Python — a release is reproducible from this +repository alone. + +One development-only backdoor exists in source builds: the `GE_DEBUG_UNLOCKALL` +environment variable (`src/game/file2.c`) pre-unlocks all cheat options at boot. +It is off unless explicitly set, changes no shipped behavior, and is documented +here so the env-var surface stays fully accounted for. diff --git a/docs/the-asset-pipeline.md b/docs/the-asset-pipeline.md index c30569ee..76d19056 100644 --- a/docs/the-asset-pipeline.md +++ b/docs/the-asset-pipeline.md @@ -1,6 +1,8 @@ --- title: The asset pipeline description: How the GoldenEye 007 PC port turns N64 ROM assets into the model and graphics files it loads at runtime. +date: '2026-10-05' +modified_time: '2026-10-05' --- ## The asset pipeline diff --git a/docs/the-software-rsp.md b/docs/the-software-rsp.md index 1f04c8c3..72516c38 100644 --- a/docs/the-software-rsp.md +++ b/docs/the-software-rsp.md @@ -1,6 +1,8 @@ --- title: The software RSP description: How the GoldenEye 007 PC port emulates the N64's Reality Signal Processor (the graphics coprocessor) in software and bypasses the RDP. +date: '2026-10-05' +modified_time: '2026-10-05' --- ## The software RSP diff --git a/port/fast3d/gfx_pc.cpp b/port/fast3d/gfx_pc.cpp index a4d738cb..ff94d711 100644 --- a/port/fast3d/gfx_pc.cpp +++ b/port/fast3d/gfx_pc.cpp @@ -2057,9 +2057,9 @@ static void d526_note_emit(uint32_t f, struct LoadedVertex* const* v_arr, int us ok = 1; } if (!ok) return; - if (d526_box_on) + if (d526_box_on) { if (mxx < d526_box[0] || mnx > d526_box[2] || mxy < d526_box[1] || mny > d526_box[3]) return; - else if (mxx < -1.05f || mnx > 1.05f || mxy < -1.05f || mny > 1.05f) return; // off-screen + } else if (mxx < -1.05f || mnx > 1.05f || mxy < -1.05f || mny > 1.05f) return; // off-screen d526_sig_total++; int found = 0; for (int i = 0; i < d526_nsigs; i++) { diff --git a/tools_pc/audiodebug.ps1 b/tools_pc/audiodebug.ps1 index 4728c5bf..bfb93053 100644 --- a/tools_pc/audiodebug.ps1 +++ b/tools_pc/audiodebug.ps1 @@ -48,7 +48,7 @@ param( [switch]$Dump, # GE_AUDIODUMP=1 -> audiodump.raw [switch]$VoiceDump, # GE_VOICEDUMP=1 -> voicedump.raw (per-voice, D202/M-67) [switch]$MixerTrace, # GE_MIXERTRACE=1 -- SLOW, see warning below - # (the -AB/-Old A/B switches were removed 2026-10-07, D532: GE_D204_OLD no + # (the -AB/-Old A/B switches were removed 2026-10-05, D532: GE_D204_OLD no # longer exists in the game, so they were documented no-ops) # Plumbing diff --git a/tools_pc/capture_p7.sh b/tools_pc/capture_p7.sh index 6b637a40..17222bd5 100755 --- a/tools_pc/capture_p7.sh +++ b/tools_pc/capture_p7.sh @@ -54,7 +54,11 @@ cleanup() { if [ "$EEP_HAD" = 1 ]; then cp "$EEP_ORIG" "$EEP" 2>/dev/null; else rm -f "$EEP" 2>/dev/null; fi rm -f "$INI_ORIG" "$EEP_ORIG" 2>/dev/null } -trap cleanup EXIT INT TERM +# D534: restore on EXIT only; INT/TERM just exit (which fires EXIT), so an +# interrupted capture cannot continue past the restore with the snapshot gone. +trap cleanup EXIT +trap 'exit 130' INT +trap 'exit 143' TERM # recipe comes from the tracked gate tool, not from this file eval "$(grep -m1 '^GOLDEN_DUMP=' "$VSH")" diff --git a/tools_pc/golden/README.md b/tools_pc/golden/README.md index 625f0661..b21d0793 100644 --- a/tools_pc/golden/README.md +++ b/tools_pc/golden/README.md @@ -24,13 +24,13 @@ and per platform (`win`, and `linux` — 21 levels x 3 frames on each, captured D523, **superseded by D529**): the frame depends on the save's **content**, not its presence — D523's "presence, not which save" claim (worst_cell 0.175625 on each of the four candidate saves, 21.557% with none) was only ever measured on - Archives, and the 2026-10-07 A/B refutes it (facility 177.47 scene-level on the + Archives, and the 2026-10-05 A/B refutes it (facility 177.47 scene-level on the 2026-10-05 playtest save, 0.0-0.25 on every other candidate on disk). `verify.sh` and `capture_p7.sh` install the canonical eep the same way they pin the ini (snapshot the local file if any, install the canonical one, restore-or-remove on exit), so the gate is reproducible on a fresh clone and in CI. A local playtest save is **never** the gate's input. The linux 63-frame set was re-captured - on the box under the canonical eep 2026-10-07 (D531; the 2026-10-05 + on the box under the canonical eep 2026-10-05 (D531; the 2026-10-05 re-round, D530, turned out to have run under the box-LOCAL save — the `capture_p7.sh` per-level re-pin bug, fixed in D531) and is valid. - Every run ends via `GE_QUITFRAME` (orderly quit; never a hard kill, D344). @@ -66,7 +66,7 @@ capture with the recipe above and replace `//*.png`. ## History -- **D531 (2026-10-07):** `capture_p7.sh`'s D529 adaptation was incomplete — +- **D531 (2026-10-05):** `capture_p7.sh`'s D529 adaptation was incomplete — the per-level re-pin restored the pre-capture LOCAL save (D524-era line), silently overwriting D529's pre-loop canonical install, so the 2026-10-05 box re-round (D530) ran under the box-local save, not the canonical one. @@ -74,7 +74,7 @@ capture with the recipe above and replace `//*.png`. and re-captured: fresh 21/21 box round, 63 frames committed, replacing the D530 set (47/63 frames differ — save content demonstrably moves the frames; 16 byte-identical save-independent scenes). -- **D530 (2026-10-07, AMENDED BY D531):** the linux set was re-rounded on the +- **D530 (2026-10-05, AMENDED BY D531):** the linux set was re-rounded on the box (X220, tree `442eb2ff`; `~/p7-reround.log` hit `P7 re-round DONE` 2026-10-05 12:53 EDT, 21/21 rc=0) and committed, replacing the D525 cleared-eep set (51/63 frames differ; 12 save-independent scenes byte- diff --git a/tools_pc/verify.sh b/tools_pc/verify.sh index d50cb43f..4109cea6 100644 --- a/tools_pc/verify.sh +++ b/tools_pc/verify.sh @@ -112,7 +112,7 @@ while [ $# -gt 0 ]; do --script) SCRIPT="$2"; USER_SCRIPT=1; shift 2 ;; --against) AGAINST="$2"; shift 2 ;; --json) JSON=1; shift ;; - -h|--help) sed -n '2,32p' "$0"; exit 0 ;; + -h|--help) sed -n '2,34p' "$0"; exit 0 ;; *) POSARGS+=("$1"); shift ;; esac done @@ -121,12 +121,12 @@ set -- "${POSARGS[@]:-}" case "${1:-}" in sweep) MODE=sweep; shift; SWEEP_SUBSET="${*:-}" ;; parity) MODE=parity; LEVEL_ARG="${2:-}"; shift 2 2>/dev/null || true ;; - -h) sed -n '2,32p' "$0"; exit 0 ;; + -h) sed -n '2,34p' "$0"; exit 0 ;; "") # D529 never-do 4: a no-arg run must NEVER look like a green verdict. It - # prints the usage and exits NON-zero (2026-10-07: a no-arg pair was + # prints the usage and exits NON-zero (2026-10-05: a no-arg pair was # cited as a "double-green" 21/21 because it exited 0). - sed -n '2,32p' "$0" + sed -n '2,34p' "$0" echo "error: no mode given -- a sweep is 'verify.sh sweep', one level is 'verify.sh ' (never cite a no-arg run as a gate result; D529)" >&2 exit 1 ;; *) MODE=single; LEVEL_ARG="$1" ;; @@ -257,14 +257,21 @@ pin_ini_640x480() { restore_ini() { if [ -n "$INI_BAK" ]; then cp "$INI_BAK" "$INI" && rm -f "$INI_BAK" elif [ "$INI_CREATED" = 1 ]; then rm -f "$INI"; fi + INI_BAK=""; INI_CREATED=0 } -trap 'restore_ini; restore_eep; kill_ours' EXIT INT TERM +# D534: restore on EXIT only; INT/TERM just exit (which fires EXIT). A combined +# `EXIT INT TERM` trap restored on Ctrl-C and then let the sweep CONTINUE -- +# the next level's pin re-installed the canonical save with the snapshot +# already deleted, so the user's own ge007.eep was left replaced on disk. +trap 'restore_ini; restore_eep; kill_ours' EXIT +trap 'exit 130' INT +trap 'exit 143' TERM # --- save pin (D523, SUPERSEDED by D529): the goldens were captured with a -# save file PRESENT. D529 (2026-10-07 A/B) proved the frame depends on the +# save file PRESENT. D529 (2026-10-05 A/B) proved the frame depends on the # save's CONTENT, not just its presence -- D523's "presence, not which # save" claim had only been measured on Archives (worst_cell 0.175625 on -# each of the four candidate saves, 21.557% with none), and the 2026-10-07 +# each of the four candidate saves, 21.557% with none), and the 2026-10-05 # sweep (facility 177.47 scene-level on the local playtest save vs 0.0-0.25 # on every other candidate on disk) refutes it. The gate now INSTALLS THE # CANONICAL SAVE (tools_pc/golden/ge007.eep, 2 KB, in-tree so a fresh @@ -291,6 +298,7 @@ restore_eep() { [ -n "$EEP_PIN_BAK" ] || return 0 if [ "$EEP_PIN_HAD" = 1 ]; then cp "$EEP_PIN_BAK" "$EEP_PIN"; else rm -f "$EEP_PIN"; fi rm -f "$EEP_PIN_BAK" + EEP_PIN_BAK=""; EEP_PIN_HAD=0 } # --- run one level -> sets globals: R_STATUS R_FRAMES R_SYM R_BRIEF -------