Golden gate: Linux baselines, canonical save, gate hardening (D521-D532)
Linux reference-frame set for all 21 levels and the cross-platform golden rule (D521); golden tooling made non-destructive (D524); the gate pins a canonical in-tree save (D529) and the PRNG seed (D532); the capture tool's per-level re-pin bug fixed and the Linux set re-captured under the canonical save (D530, D531); no-arg verify.sh exits non-zero. README and docs visual/consolidation passes; the 15 MB preview gif replaced by stills. Refs: D521 D523 D524 D525 D526 D527 D528 D529 D530 D531 D532
No files matched your search
@@ -1,10 +1,14 @@
|
||||
# GoldenEye 007 PC Port — v<version>
|
||||
|
||||
<p align="center">
|
||||
<img src="https://github.com/jkdansereau/goldeneye-pc-port/raw/v<version>/docs/media/goldeneye-gh-preview.gif" width="480"
|
||||
alt="~24 s gameplay montage from live play sessions, opening on the Runway tank (no audio track)">
|
||||
<img src="https://github.com/jkdansereau/goldeneye-pc-port/raw/v<version>/docs/img/shots/shot-28.jpg" width="340" alt="Streets, rendered by the port">
|
||||
<img src="https://github.com/jkdansereau/goldeneye-pc-port/raw/v<version>/docs/img/shots/shot-01.jpg" width="340" alt="Dam, rendered by the port">
|
||||
</p>
|
||||
|
||||
<!-- MAINTAINER: header stills are from the public capture set (docs/img/shots/).
|
||||
The 15 MB gameplay-montage gif was dropped from the repo on the v0.5.0
|
||||
docs media pass (C3); the project index now uses a still carousel. -->
|
||||
|
||||
Platforms: **Windows x86-64** and **Linux x86-64 (including Steam Deck)**.
|
||||
Region: **NTSC-U (US) only**. See [Known issues](#known-issues).
|
||||
|
||||
@@ -12,186 +16,156 @@ 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 **2–4 player split-screen multiplayer**, the largest feature
|
||||
the N64 original had that this port was still missing. Alongside it come
|
||||
controller presets and rebinding, audio output options, a steady 60 fps on
|
||||
low-end hardware, and a round of fidelity and stability fixes checked against
|
||||
the N64 game.
|
||||
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.
|
||||
|
||||
---
|
||||
|
||||
## New features
|
||||
|
||||
### 2–4 player split-screen multiplayer (#99)
|
||||
### One options menu, PD-style layout (D504, D519)
|
||||
|
||||
- Local multiplayer on every MP map, with the N64's own setup flow,
|
||||
pause/watch menus and per-player HUD.
|
||||
- **Who controls which player:** with one controller, keyboard/mouse is
|
||||
player 1 and the controller is player 2; with two or more controllers, the
|
||||
controllers take players 1–4. `Input.MPMode` in `ge007.ini` picks Auto
|
||||
(default), PadsOnly or KbmP1.
|
||||
- A controller unplugged and replugged mid-match keeps its player (thanks to
|
||||
dolent, #122). Each controller's LED shows its player number.
|
||||
- The file-select **PC Options** entry now opens the same menu the F10 overlay
|
||||
shows — there is only one options UI, one place to learn (D519).
|
||||
- Options are grouped into six sections in the Perfect Dark port's order
|
||||
(Video, Sound, Controls, Game, …), with **checkboxes, sliders and dropdowns**
|
||||
instead of the old numbered rows (D504).
|
||||
- **Display mode** is a dropdown; **Backspace** goes back, matching the rest of
|
||||
the game's menus (D504 follow-up).
|
||||
|
||||
### Controller presets and rebinding (D469, D498)
|
||||
### Menu wording, units and help text (D505–D507)
|
||||
|
||||
F10 → Input → **Controller** (also in the front-end PC Options):
|
||||
- 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).
|
||||
|
||||
- **Layout preset:** the Xbox release's four styles: "1.1 Jinx" (the
|
||||
default, identical to before), "1.2 Christmas", "1.3 Frost" and
|
||||
"1.4 Elektra" (they swap which stick moves, looks, strafes and turns), or
|
||||
**Custom**.
|
||||
- **Custom:** rebind every action per controller, up to two buttons each.
|
||||
Tap B or Back to cancel a capture, hold Back to clear. Menus always keep
|
||||
A/X accept and B/Y cancel, so you can't lock yourself out.
|
||||
- Builds on ideas from italoarruda's #109 (credited as co-author).
|
||||
- In the tank, the pad follows your layout too: the weapon button switches
|
||||
the tank's weapon and the use button gets out (D495).
|
||||
### Control styles: the game's own N64 layouts (D513, D516, D518)
|
||||
|
||||
### PlayStation and Nintendo button names (D471)
|
||||
- **Control style** offers **Ext** (this port's scheme, unchanged) and
|
||||
**Original**, which shows the game's own N64 styles 1.1–2.4 (D513, D516).
|
||||
- The style is chosen **per seat** and saved with the Bond file, so each player
|
||||
keeps their own (D518, D516).
|
||||
- The Xbox-release presets (1.1 Jinx, 1.2 Christmas, 1.3 Frost, 1.4 Elektra) and
|
||||
Custom rebinding from v0.4.x are unchanged. Preset names refer to the Xbox
|
||||
release's control styles; this project is not affiliated with Microsoft or Rare.
|
||||
- Here the right stick aims and the D-pad strafes, as on the N64; the Xbox
|
||||
release's "left stick aims the crosshair" and "D-pad copies the left stick"
|
||||
behaviours are not reproduced.
|
||||
|
||||
PlayStation and Switch controllers show their own button names in the
|
||||
menus (Cross/Circle, ZL/ZR, …). Xbox and Steam Deck text is unchanged.
|
||||
### Emulator save files load directly (D514)
|
||||
|
||||
### Audio output options (D470)
|
||||
- An emulator-format `ge007.eep` sitting next to the executable is read on first
|
||||
launch, so a save from Project64, mupen or 1964 continues in the port without
|
||||
conversion. (Live playtest owed; the converter `eep_convert.py` still does
|
||||
explicit two-way conversion.)
|
||||
|
||||
- **Master volume**, applied after the music and FX volumes.
|
||||
- **Output device:** pick your audio device. A missing or unplugged device
|
||||
falls back to the system default.
|
||||
### HUD and overlay scaling (D510, D512)
|
||||
|
||||
### Presets, filtering and the original look (D440, D447, D499)
|
||||
- The F10 overlay and the FPS counter are now the **same on-screen size** on the
|
||||
front end and inside a level (D510).
|
||||
- **Game.HudScale** scales the F10 overlay and FPS counter along with the in-game
|
||||
HUD (D512).
|
||||
|
||||
- **Original N64 preset** (F10 → Graphics) sets every value the port changes
|
||||
back to the N64's; **Port defaults preset** restores the port's modern
|
||||
defaults.
|
||||
- **Aspect → Original** shows the exact 4:3 frame with bars (16:9 while the
|
||||
game's own Ratio setting is 16:9).
|
||||
- **Texture filter → Trilinear**, next to Bilinear: smooths distant
|
||||
textures that the game has no mipmaps for.
|
||||
### New settings (D511)
|
||||
|
||||
### Options polish (D443, D472, D497, D502)
|
||||
- **Fullscreen mode**, **Center window**, **crosshair opacity** and
|
||||
**crosshair colour by health** — the four settings the Perfect Dark port has
|
||||
that this port was missing.
|
||||
|
||||
- **Restart game** row: applies restart-only settings such as anti-aliasing
|
||||
with one click.
|
||||
- Clearer values: mouse sensitivity as ×, deadzone, vibration and volumes in
|
||||
whole %, field of view in horizontal degrees, anti-aliasing up to 16×.
|
||||
- Front-end PC Options pages like the game's own dossiers: PREVIOUS and
|
||||
NEXT folder tabs, and B goes back a page. With a controller, the first
|
||||
item is highlighted when you open a section and you return to where you
|
||||
were when you leave it.
|
||||
- The F10 overlay is pillarboxed in widescreen (instead of stretched),
|
||||
experimental options are shown in red, an unknown `ge007.ini` key shows a
|
||||
warning (#90), and with several controllers the one that opened the
|
||||
overlay drives it.
|
||||
### Overlay controls are antialiased (D520)
|
||||
|
||||
### `All unlocked` no longer touches your save (D442)
|
||||
- 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.
|
||||
|
||||
It's now a memory-only override: switch it off and your real progress is
|
||||
back. Fake unlocks written by older builds are not repaired, so back up
|
||||
`data/ge007.eep` if you used it before.
|
||||
### 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.
|
||||
|
||||
---
|
||||
|
||||
## Performance
|
||||
## Fidelity and stability fixes
|
||||
|
||||
- **A steady 60 fps on low-end hardware (D481, #92).** Rendering ran on the
|
||||
same thread that delivers the game's 60 Hz timing signal, so slower GPUs
|
||||
regularly missed their frame slot and dropped to 30–45 fps. Rendering now
|
||||
runs on its own thread, like the N64's graphics chip. A 2011 laptop (Intel
|
||||
HD 3000) went from ~45 to a steady 60 fps at every setting.
|
||||
- **Lighter first-launch defaults on Atom/Celeron-class GPUs (D482, #92).**
|
||||
On Intel HD 400–605 / UHD 600–605 class GPUs and software renderers, the
|
||||
first launch lowers draw distance, LOD distance and anti-aliasing. Settings
|
||||
you've changed yourself are never touched.
|
||||
- **No more in-play shader stutter (D480):** shaders the game has used are
|
||||
compiled at startup on the next launch.
|
||||
- **Render-thread CPU roughly halved in typical scenes (D473),** plus fewer
|
||||
redundant texture lookups and palette re-imports (D474, D476).
|
||||
|
||||
---
|
||||
|
||||
## Fidelity fixes (checked against the N64 game)
|
||||
|
||||
- **Draw distance no longer changes AI behaviour (D466, #125).** The
|
||||
extended draw distance (default 250%) let guards "see" you from beyond the
|
||||
N64's fog, so Xenia's fight on Jungle could start early. AI awareness now
|
||||
uses the level's original distance; you can still see, shoot and use
|
||||
everything that's drawn.
|
||||
- **Ultrawide and high field of view (D468):** the AI keeps the N64's widest
|
||||
(16:9) view. Turn on *Guards see the wider view* if you prefer the old
|
||||
behaviour.
|
||||
- **Sniper rifle (#136):** the scope zooms all the way in again (it was
|
||||
capped at about a third of the original magnification, D484); zoom speed
|
||||
matches the N64 (it was about twice as fast at 60 fps, D485); the crouch
|
||||
key now crouches and stands with it out, as it does in GEPD (D494).
|
||||
- **Turrets and explosions at 60 fps (D486, #126):** turret fire rate,
|
||||
tracers, explosion density and screen-shake length now match the N64.
|
||||
Damage is unchanged. (Explosions that sometimes don't appear in heavy
|
||||
firefights are the N64's own behaviour: it has six explosion slots.)
|
||||
- **Weapons (#114–#118):** automatic-fire cadence follows the N64's rules at
|
||||
any frame rate; grenade launcher recoil is back; the gun sways with mouse
|
||||
look again; the tank turns with the mouse; the run-over death sound no
|
||||
longer loops.
|
||||
- **Light fixtures go dark when shot, with the right impact effects
|
||||
(#119).**
|
||||
- **Dam/Caverns water (D465, #127):** the water animation no longer slides
|
||||
side to side, and it scrolls at the N64's rate.
|
||||
- **Kneeling guards** use their crouched-rifle firing animation (D445,
|
||||
thanks dolent, #120).
|
||||
- **Dam:** the first-tower guard walks his whole patrol instead of a small
|
||||
circle (D490).
|
||||
- **The Rareware logo** in the intro renders correctly (D75).
|
||||
- Props near some doorways no longer count as being in extra rooms, and
|
||||
ejected casings spin as authored (D491).
|
||||
|
||||
---
|
||||
|
||||
## Stability & fixes
|
||||
|
||||
- **Knife-slash crash fixed (D454).**
|
||||
- **64-bit memory-layout fixes** (D441, D453, D456, D457, D461, D462): two
|
||||
item/vertex tables were sized with N64 strides, and several pointer fields
|
||||
were truncated.
|
||||
- A split-screen zero-size texture crash is guarded (D463); stale
|
||||
anisotropic filtering on recycled textures is fixed (D446, thanks dolent,
|
||||
#121).
|
||||
- *Skip intro* no longer skips the failure dossier after a failed mission
|
||||
(D408).
|
||||
- Adjusting the music volume with a controller in F10 no longer drops the
|
||||
frame rate (D489).
|
||||
- Steam Deck: the Deck preset now also applies after a first launch in
|
||||
Desktop Mode (D283).
|
||||
- Aztec: the area by the shuttle with the four turrets no longer breaks
|
||||
into stray triangles. The per-frame vertex buffer, sized for what the
|
||||
N64 could see, could overflow with the port's wider view (D500).
|
||||
- In 3- and 4-player games, weapon pickup messages no longer show stray
|
||||
characters (D496).
|
||||
- A controller no longer keeps vibrating after you quit mid-firefight
|
||||
(D493).
|
||||
- *Aspect → Original* no longer shows a one-pixel strip of flickering
|
||||
pixels at the left and right edges (D493).
|
||||
- Crash logs now include resolved stack frames, which makes reports far
|
||||
easier to act on.
|
||||
- **Fog:** objects far in fog are hidden as on the N64 (fog snap); in widescreen
|
||||
the wider view can still show a faint distant building edge (e.g. Surface's
|
||||
dish from the start area) — 4:3 matches the N64 (D503).
|
||||
- **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).
|
||||
- **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
|
||||
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
|
||||
of a hardcoded copy that had gone stale (D524). The gate's recipe was re-based
|
||||
too: per-level windows that sit in settled gameplay rather than inside the intro
|
||||
flyby, and the save file pinned present (D522, D523).
|
||||
- **Harness:** the debug capture/launch env vars are documented — `GE_PCDUMP` must be
|
||||
`first-last:step` (a colon where the dash belongs silently dumps every frame, D527),
|
||||
and a `GE_STARTMENU` boot skips the EEPROM import, so the imported-save test must run
|
||||
from a normal boot (D528). A per-triangle draw-state census probe
|
||||
(`GE_D526`/`GE_D526BOX`/`GE_D526MAX`) is available for transparency/texture triage
|
||||
(D526; the P13 Dam-ending grate show-through it targeted was confirmed faithful N64
|
||||
behaviour on 1964/GEPD, so no change shipped).
|
||||
|
||||
---
|
||||
|
||||
## Known issues
|
||||
|
||||
- **NTSC-U (US) ROMs only.** PAL and JP ROMs convert, build and boot from
|
||||
source on this release's code, but the release packages don't support them
|
||||
yet (#85).
|
||||
- **No macOS or ARM builds yet.**
|
||||
- The first frame of a level takes a little longer while its textures
|
||||
upload: a brief dip in the FPS counter.
|
||||
- **`Skip intro` and `All unlocked` are still experimental.**
|
||||
- Far objects almost hidden in fog can show as a faint outline with
|
||||
straight edges (e.g. the satellite dish seen from Surface's start area).
|
||||
The N64's 16-bit colour hid it (D503).
|
||||
- The full list, with workarounds, is the
|
||||
| Issue | Impact | Workaround |
|
||||
|---|---|---|
|
||||
| PAL and JP ROMs aren't supported in release packages | NTSC-U only | Use an NTSC-U ROM. Both regions convert, build and boot from source; packaging is still open |
|
||||
| Changing aspect ratio inside a level can briefly glitch the gun/hand model, rarely | Cosmetic, one-off | Change the ratio from the front-end PC Options, or accept it |
|
||||
| No macOS or ARM builds | Platform | — |
|
||||
| Saves from builds before v0.4.1 can hold fake unlocks from `All unlocked` | Save data | Not repaired automatically. Since v0.4.1 the option never writes the save; keep a backup of `data/ge007.eep` from before you used it |
|
||||
| `Skip intro` and `All unlocked` are experimental | Opt-in options | Leave them off for a normal playthrough |
|
||||
| The first frame of a level takes a little longer while its textures upload | Brief FPS-counter dip | None needed |
|
||||
| Far objects almost fully in fog are now hidden as on the N64, except in widescreen where a faint distant building edge can still show (e.g. Surface's dish from the start area); 4:3 matches the N64 | Cosmetic | Higher Draw/LOD distance shows more |
|
||||
|
||||
The full list, with workarounds, is the
|
||||
[known-issues table](https://github.com/jkdansereau/goldeneye-pc-port/blob/main/docs/ROADMAP.md#known-issues).
|
||||
|
||||
**Not yet playtested live** (merged, build- and review-verified, owed a play
|
||||
session before this release is announced): the four new settings (D511),
|
||||
`Game.HudScale` (D512), Control style Original on a pad (D513/D516 per seat),
|
||||
emulator-save import (D514), and the D519 profile-chooser row / no-click-through
|
||||
follow-ups.
|
||||
<!-- MAINTAINER: if the play session did not cover all of the above, keep the
|
||||
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
|
||||
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
|
||||
by eye in a live session.
|
||||
|
||||
---
|
||||
|
||||
## Thanks
|
||||
|
||||
Outside contributions merged for this release: dolent (PRs #120–#122), MST246
|
||||
(#125 draw-distance investigation), italoarruda (#109 gamepad-preset ideas),
|
||||
TenebrusoM (DexDrive / the D514 save-format sample), plus reporter credits on
|
||||
the issue numbers named above.
|
||||
|
||||
## Downloads
|
||||
|
||||
| File | Platform |
|
||||
@@ -232,13 +206,6 @@ sha256sum -c goldeneye-pc-port-<version>-win64.zip.sha256
|
||||
sha256sum -c goldeneye-pc-port-<version>-linux-x86_64.tar.gz.sha256
|
||||
```
|
||||
|
||||
## Thanks
|
||||
|
||||
dolent (#120, #121, #122), italoarruda (#107, #109), MST246 (the #125
|
||||
investigation and handoff used for the fix), JosephAHK (#133), MistaEcho
|
||||
(#119 via #87), and everyone who filed #92, #114–#119, #125, #126, #127,
|
||||
#129, #130 and #136.
|
||||
|
||||
## Source & docs
|
||||
|
||||
<https://github.com/jkdansereau/goldeneye-pc-port>, built on the
|
||||
|
||||
@@ -196,6 +196,11 @@ build-pc-cmd.bat
|
||||
# v0.4.0 modern-options wave: per-landing sweep capture dirs (capture.sh output)
|
||||
tools_pc/sweep-captures/capture-*/
|
||||
|
||||
# Sweep capture output dirs and PPM dumps: regenerable framebuffer captures
|
||||
# (capture.sh OUTDIR), useful on disk, never in the tracked tree.
|
||||
tools_pc/sweep-captures/cap-*/
|
||||
tools_pc/sweep-captures/**/*.ppm
|
||||
|
||||
# Stray full CPython install that landed in repo root (2026-09-28);
|
||||
# referenced by no project tooling. Never commit.
|
||||
/Python/
|
||||
|
||||
@@ -17,6 +17,44 @@
|
||||
4. **`src/libultrare/Makefile.libultrare` is ground truth** for original-vs-Rare libultra files (finding B3). The PC build compiles: `libultra/audio`, `libultrare/audio` (drvrNew/env/reverb), `libultra/gu`, and `libultrare/io/vitbl.c` only. All other `io/` + `os/` files are excluded and shimmed in `port/src/libultra.c`.
|
||||
5. **`rsp/graphics/gmain.s` is the RSP ground truth** — the authoritative reference for which GBI commands GE emits (modified fast3d, 1545 lines). We do not run it on PC; `port/fast3d/` replaces it. Use it to validate the software RSP's command decoding and the custom CC/RM modes.
|
||||
|
||||
## Tree hygiene & release gate
|
||||
|
||||
Standing rules for anything that lands in the tracked tree or in a push. These
|
||||
exist because the repo is public, MIT-licensed, forked, and its history is
|
||||
mostly agent-authored.
|
||||
|
||||
1. **No local machine paths.** Tracked files carry no local absolute paths and
|
||||
no usernames. Use the placeholder convention: `<repo>` = this working
|
||||
directory, `<repos>` = the local source-repos root, `<games>` / `<videos>` =
|
||||
local media roots, `<python>` = the local CPython install, `<temp>` = a
|
||||
writable native temp dir (legend at the top of
|
||||
`docs/dev/HANDOFF-ARCHIVE.md`). Tools must not default to a machine-specific
|
||||
path — take the path as an argument and error if it is missing.
|
||||
2. **No contributor emails in prose.** Credit people by name plus the PR/issue
|
||||
that carries the change ("co-authored with X (#123)"), never an inline
|
||||
`<email>` trailer inside findings text or docs. Commit-trailer emails must
|
||||
be noreply forms, never a contributor's real address.
|
||||
3. **Every vendored third-party component is declared in `NOTICE`** with its
|
||||
license and holder — the "Third-party code vendored into the port layer" and
|
||||
"Vendored build tooling" lists. GPL sources must ship their license text
|
||||
in-tree (`tools/gzipsrc/COPYING`). MIT covers only our original work; never
|
||||
relicense inherited code and never re-license the repo (forks depend on the
|
||||
MIT `LICENSE` staying put).
|
||||
4. **Screenshots are game content.** A framebuffer capture is a derivative work
|
||||
of Nintendo/MGM art regardless of which engine drew it. Keep repo imagery
|
||||
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.
|
||||
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
|
||||
review reports (gitignored, `docs/dev/notes/`) must be run and clean, and the
|
||||
README Legal section must name every rights holder the game's trademarks
|
||||
implicate. Rewriting pushed history (squash) needs explicit maintainer
|
||||
consent, and any such report states the remote + branch it applies to.
|
||||
|
||||
## Critical files
|
||||
|
||||
| File | Role |
|
||||
|
||||
@@ -43,6 +43,12 @@ Having a concrete "next step" from the handoff is NOT license to skip this.
|
||||
2. Skim the README "Status" section for where the project stands.
|
||||
3. If you may dispatch a subagent this session, read `docs/dev-process.md`
|
||||
NOW — not at dispatch time.
|
||||
4. Tree hygiene applies to anything you commit — see AGENTS.md §Tree hygiene &
|
||||
release gate: no local absolute paths or usernames in tracked files (use
|
||||
`<repo>`/`<repos>`/`<games>`/`<videos>`/`<python>`/`<temp>`), no contributor
|
||||
emails in prose (name + PR number), every vendored third-party license
|
||||
declared in `NOTICE`, no bulk ROM-derived captures in the tree (capture
|
||||
dirs are gitignored), and one batched commit per session — not one per fix.
|
||||
|
||||
## Dispatch preflight — EVERY Agent/Task spawn, no exceptions
|
||||
|
||||
@@ -76,7 +82,7 @@ Full template: `docs/dev-process.md` -> "Investigation-brief template".
|
||||
## Local Qwen dispatch (cost-free, data-local worker)
|
||||
|
||||
A local Qwen3.8-27B is wired in via the `delegate-local` MCP server (see
|
||||
`C:\Users\james\Source\Repos\20260902-qwen38claudepair`). Use it as a
|
||||
`<repos>\20260902-qwen38claudepair`). Use it as a
|
||||
*subagent whose compute runs on-machine* for scoped, checkable work — the lead
|
||||
still owns judgment, planning, and the verification gate.
|
||||
|
||||
@@ -93,7 +99,7 @@ still owns judgment, planning, and the verification gate.
|
||||
proxy (`litellm/run.ps1`) + Unsloth Studio both up.
|
||||
- **Corrected 2026-09-04:** the "worker's shell is `cmd.exe`, no grep/cat"
|
||||
claim that used to live here was WRONG — verified directly against the
|
||||
harness's own `_run_bash` (`C:\Users\james\tools\claude-code-delegate-local`):
|
||||
harness's own `_run_bash` (the local `claude-code-delegate-local` harness dir):
|
||||
`grep`, `sed`, `cat`, `ls`, `head`, pipes, and `grep -rn` all work fine
|
||||
(Git-for-Windows/MSYS2 toolchain on PATH). The stale claim, baked into 4 of
|
||||
5 local agent `.md` files, caused a real dispatch (a multi-file
|
||||
|
||||
@@ -18,6 +18,19 @@ Third-party code vendored into the port layer keeps its own license:
|
||||
libultraship / sm64-port lineage; see port/fast3d/LICENSE.txt.
|
||||
* port/fast3d/glad/ - generated OpenGL loader (glad), public domain / MIT.
|
||||
|
||||
Vendored build tooling keeps its own license (none of it is compiled or linked
|
||||
into the PC port; it belongs to the N64 decompilation build):
|
||||
|
||||
* tools/ido5.3_recomp/rabbitizer/ - disassembler library (rabbitizer), MIT,
|
||||
(c) 2022 Decompollaborate; license text in that directory's LICENSE file.
|
||||
* tools/armips.cpp - assembler (armips), MIT, (c) 2009-2020 Kingcom.
|
||||
* tools/extractor/puff.c, puff.h - puff-style inflate, zlib-style license,
|
||||
(c) Mark Adler.
|
||||
* tools/gzipsrc/ - gzip 1.x sources, GPL-2.0-or-later, (c) 1992-1993
|
||||
Jean-loup Gailly; the GPL-2.0 text is tools/gzipsrc/COPYING. These sources
|
||||
are build tooling only: they are never linked into the port, and they are
|
||||
not part of any download package.
|
||||
|
||||
It does NOT cover:
|
||||
|
||||
* src/, include/, assets/, Makefile, tools/, rsp/, ld/, and the other
|
||||
|
||||
@@ -9,9 +9,10 @@
|
||||
and Steam Deck.** Native widescreen, a steady 60 fps, 2–4 player
|
||||
split-screen, mouse and keyboard or a modern controller, rebindable controls
|
||||
and an in-game options menu. No
|
||||
emulator: the original game's own code, rebuilt from the
|
||||
[GoldenEye 007 decompilation](https://github.com/n64decomp/007), running from
|
||||
your own ROM.
|
||||
emulator, and not a binary recompilation: the game's own
|
||||
reconstructed C, rebuilt from the
|
||||
[GoldenEye 007 decompilation](https://github.com/n64decomp/007), compiled
|
||||
for your PC and running from your own ROM.
|
||||
|
||||
**[Download the latest release](https://github.com/jkdansereau/goldeneye-pc-port/releases)**,
|
||||
then follow the [Quick start](#quick-start).
|
||||
@@ -23,22 +24,23 @@ then follow the [Quick start](#quick-start).
|
||||
> downloads contain no Nintendo code or assets, and no ROM. See
|
||||
> [What's a ROM?](#whats-a-rom) and [Legal](#legal).
|
||||
|
||||
**v0.4.1** is the current release. It is a **pre-1.0 release, not a
|
||||
**v0.5.0** is the current release. It is a **pre-1.0 release, not a
|
||||
finished product**: the campaign and split-screen multiplayer play end to
|
||||
end, but expect missing pieces (PAL/JP ROMs, macOS) and the occasional
|
||||
breaking change between versions. v1.0 is the target for a feature-complete
|
||||
build.
|
||||
|
||||
**AI disclosure:** this port was developed agentically, with Claude Code and a
|
||||
local open-weight model, directed by one person in their spare time. The
|
||||
**AI disclosure:** this port was developed using AI coding agents (Claude
|
||||
Code and a local open-weight model), directed by one person in their spare time. The
|
||||
project is as much a study of that process as it is a port. See
|
||||
[How it was made](#how-it-was-made).
|
||||
|
||||
<p align="center">
|
||||
<img src="docs/media/goldeneye-gh-preview.gif" width="64%"
|
||||
alt="~24 s gameplay montage from live play sessions">
|
||||
<br><em>All in-engine, running in the port: a ~24 s gameplay montage
|
||||
from live v0.4.0 play sessions, opening on the Runway tank ·
|
||||
<img src="docs/img/shots/shot-28.jpg" width="23%" alt="Streets, rendered by the port">
|
||||
<img src="docs/img/shots/shot-01.jpg" width="23%" alt="Dam, rendered by the port">
|
||||
<img src="docs/img/shots/shot-03.jpg" width="23%" alt="Runway, rendered by the port">
|
||||
<img src="docs/img/shots/shot-24.jpg" width="23%" alt="Aztec, rendered by the port">
|
||||
<br><em>All in-engine, running in the port ·
|
||||
<a href="docs/index.md">12 level stills in the project index</a></em>
|
||||
</p>
|
||||
|
||||
@@ -46,6 +48,14 @@ 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).
|
||||
- **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
|
||||
@@ -159,7 +169,8 @@ hold B to bind B itself, hold Back to clear; Y clears the selected slot).
|
||||
Each action takes up to two buttons. Menus always keep A/X accept and B/Y
|
||||
cancel, so you can't lock yourself out. There is no warning yet when two
|
||||
actions share a button, and the Xbox release's "left stick aims the
|
||||
crosshair" and "D-pad copies the left stick" behaviors are not reproduced.
|
||||
crosshair" and "D-pad copies the left stick" behaviors are not reproduced
|
||||
(here the right stick aims and the D-pad strafes, as on the N64).
|
||||
|
||||
**Split-screen:** pick Multiplayer from the main menu. With one controller,
|
||||
keyboard/mouse is player 1 and the controller is player 2; with two or more
|
||||
@@ -214,7 +225,7 @@ and the end-of-campaign credits load, render and run crash-free; the full
|
||||
campaign was playtested end to end at Agent difficulty on Windows, Linux and
|
||||
real Steam Deck hardware for v0.4.0. Split-screen multiplayer was played live
|
||||
in 2P on every MP map and in 4P on Temple.
|
||||
<!-- MAINTAINER: after the v0.4.1 play session, add what it covered here. -->
|
||||
<!-- MAINTAINER: after the v0.5.0 play session, add what it covered here. -->
|
||||
|
||||
**Known issues:** see the **[known-issues table](docs/ROADMAP.md#known-issues)**
|
||||
(what you'll notice, impact, workarounds). The short version: NTSC-U ROMs
|
||||
@@ -259,6 +270,12 @@ never writes to `%APPDATA%`, the registry or any system location.
|
||||
| `data/ge007.shaders` | A list of the graphics shaders the game used, compiled at startup to avoid stutter. Safe to delete. |
|
||||
| `ge007.crash.log` | Written in the folder you launched from if the game crashes. |
|
||||
|
||||
**Removing ROM-derived content:** the only files on your machine that come
|
||||
from the game are the ROM you put there and the two generated asset folders
|
||||
(`data/pcmodels-<region>/`, `data/pccg-<region>/`). Delete those and every
|
||||
ROM-derived file is gone; your save (`ge007.eep`) and settings (`ge007.ini`)
|
||||
are separate files, yours to keep or delete.
|
||||
|
||||
**`ge007.ini`** uses `[Section]` headers and `Key = value` lines. A few
|
||||
common keys and their defaults:
|
||||
|
||||
@@ -387,7 +404,7 @@ these matter to you, open an issue — it helps prioritize.
|
||||
|
||||
The port was built by two coding agents, directed by one person part-time.
|
||||
A local open-weight model (`unsloth/Qwen3.8-27B-GGUF` on one RTX 5090, via the
|
||||
[pi](https://pi.dev/) agent) did the groundwork: build, boot chain,
|
||||
[pi](https://github.com/earendil-works/pi) agent) did the groundwork: build, boot chain,
|
||||
software-RSP integration, asset pipeline, first frames. **Claude Code**
|
||||
(Sonnet 5, Opus 5 for the hardest bugs) joined for the collaborative phase:
|
||||
the 21-level sweep, the ABI finding catalog, SDL input, front end. The two
|
||||
@@ -413,7 +430,7 @@ code with this one.
|
||||
| | This project | The other well-known "GoldenEye on PC" recompilation projects (and their Steam Deck builds) |
|
||||
|---|---|---|
|
||||
| **What it ports** | The original **Nintendo 64** game (1997) | The **Xbox 360 XBLA** HD remaster (built ~2007, never released) |
|
||||
| **How** | Decompilation-based source port: human-reconstructed C, compiled for the host; game logic runs as written | Static binary recompilation: the shipped machine code is auto-translated to C; no source-level understanding |
|
||||
| **How** | Decompilation-based source port: human-reconstructed C, compiled for the host; game logic runs as written | Static binary recompilation: the shipped machine code is auto-translated to C; the source is machine-generated |
|
||||
| **Lineage** | [GoldenEye 007 decompilation](https://github.com/n64decomp/007) + [Perfect Dark PC port](https://github.com/fgsfdsfgs/perfect_dark) engine family | Xbox 360 "…Recompiled" static-recompilation family |
|
||||
| **Renderer** | Software RSP → OpenGL | Hardware (Vulkan) |
|
||||
| **Status** | Pre-1.0 releases; full campaign and split-screen playable at 60 fps (see [Status](#status)) | Playable full game |
|
||||
@@ -464,21 +481,25 @@ repo root, put your ROM in it as in [What's a ROM?](#whats-a-rom), and run
|
||||
|
||||
## How it works
|
||||
|
||||
The game code in `src/` is the decompilation's, and its control flow is
|
||||
ground truth. Everything that would touch N64 hardware is redirected into
|
||||
The game code in `src/` is the decompilation's, and its control flow is the
|
||||
reference we preserve. Everything that would touch N64 hardware is redirected into
|
||||
`port/`: a **software RSP** (`port/fast3d/`, adapted from the Perfect Dark
|
||||
port) that interprets the GBI display list the game builds each frame and
|
||||
port) that interprets the graphics command stream (the GBI display list)
|
||||
the game builds each frame and
|
||||
emits OpenGL, bypassing the RDP; libultra OS shims (`port/src/libultra.c`,
|
||||
game threads as real host threads) that let the game's own scheduler run,
|
||||
with the software RSP on its own render thread as the RSP was its own chip;
|
||||
and SDL2/OpenGL/filesystem backends for video, audio, input and storage.
|
||||
Edits inside `src/` are rare, marked `#ifdef PORT`, and each one is
|
||||
documented: mostly mechanical 64-bit fixes (a struct or pointer field that
|
||||
the 32→64-bit move misaligned or truncated), plus a few maintainer-approved
|
||||
timing fixes where per-frame N64 code ran too fast at the PC's 60 fps (for
|
||||
example sniper zoom and turret fire), and the opt-in *All unlocked* hook. Where it diverges from the Perfect Dark port: GoldenEye's N64
|
||||
serialized asset formats are converted offline by Python "sidecar" converters
|
||||
in `tools_pc/` rather than fixed up at load time. Full detail:
|
||||
Every port change inside `src/` is gated behind `#ifdef PORT` and
|
||||
documented. They are few in kind, all narrow in scope: mechanical 32→64-bit
|
||||
pointer/struct fixes (the pointer-width transition touches many structs, so
|
||||
there are a lot of them), a few maintainer-approved timing fixes where
|
||||
per-frame N64 code ran too fast at the PC's 60 fps (for example sniper zoom
|
||||
and turret fire), the opt-in *All unlocked* hook, and env-gated diagnostic
|
||||
probes tied to the bug catalogue (off by default, no behavior change). Where it diverges from the Perfect Dark port: GoldenEye's N64
|
||||
serialized asset formats are converted in a separate step rather than
|
||||
fixed up at load time — on first start for a download, or by the offline
|
||||
Python converters in `tools_pc/` for a source build. Full detail:
|
||||
[`docs/internals.md`](docs/internals.md) and
|
||||
[`docs/porting-notes.md`](docs/porting-notes.md).
|
||||
|
||||
@@ -576,20 +597,39 @@ This port is a thin layer on a large amount of other people's work.
|
||||
|
||||
**Tools and models used to develop the port**
|
||||
|
||||
- Qwen 3.8 (Alibaba Qwen team), run locally from the
|
||||
[`unsloth/Qwen3.8-27B-GGUF`](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF)
|
||||
repo — `UD-Q4_K_XL` as the primary quant, with other quants used along the
|
||||
way; [Unsloth](https://unsloth.ai) (the GGUF quantisation and Unsloth
|
||||
Desktop) as the local model server.
|
||||
- [pi](https://pi.dev/): the local coding-agent harness.
|
||||
- [Claude / Claude Code](https://claude.com/claude-code) (Anthropic).
|
||||
Development ran for most of its life on a single local model — **Unsloth's
|
||||
Qwen 3.8 27B** — which did nearly all of the local (groundwork) work on its
|
||||
own. In the final week that grew into a small menu, and the **current standard
|
||||
is NInfer + Strata**; tools are chosen per task, with local models carrying
|
||||
the high-volume work and hosted models the exception. The full record of the
|
||||
models used, and the harnesses that drove them:
|
||||
|
||||
| Model | Ran as | How it was used |
|
||||
|---|---|---|
|
||||
| **Qwen 3.8 27B** ([Unsloth](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF)) | `UD-Q4_K_XL`, one RTX 5090 | The primary local workhorse for the bulk of the port's development. |
|
||||
| **Qwen 3.8 27B** via [NInfer](https://github.com/natpate/ninfer-windows) (Windows) | base, NVFP4 and NVFP4-full artifacts; MTP (DFlash2) spec-decode; one 5090 | Trialed in the final week, now half of the standard; the fast day-to-day local driver. |
|
||||
| **Qwen 3.8 27B** ([ISTA-DASLab](https://huggingface.co/ISTA-DASLab/Qwen3.8-27B-GSQ-RCO-GGUF)) | `IQ3_S` (GSQ-RCO), larger context | Briefly trialed for larger-context workloads; shelved, little use since v0.2.0. |
|
||||
| **Qwen 3.8 Flash-Next** via [Strata](https://github.com/Niko1221/Strata) | 125B MoE, `IQ1_M` (1-bit), one gaming GPU | The big-context local tier: a model that normally needs a server, run locally for the largest-context passes. |
|
||||
| **Claude** (Anthropic) | Sonnet 5 / Opus 5, now **Sonnet 5.5 / Opus 5.5** (out in the last few weeks) | The orchestrator in the two-agent phase: held what the 27B couldn't close, took over on limit handoff; the 5.5 tier has driven the most recent work. |
|
||||
| **GPT-6 (Sol)** (OpenAI), via [OpenRouter](https://openrouter.ai) | a frontier hosted model | Trialed. |
|
||||
|
||||
*Harnesses and tooling:*
|
||||
|
||||
- **[pi](https://github.com/earendil-works/pi)** — the local coding-agent
|
||||
harness that drives the local models.
|
||||
- **[Claude Code](https://github.com/anthropics/claude-code)** (Anthropic) —
|
||||
the agentic harness that drove the Claude models above; the orchestrator in
|
||||
the two-agent phase.
|
||||
- **[claude-code-delegate-local](https://github.com/fegone/claude-code-delegate-local)** —
|
||||
the MCP bridge that let Claude and the local models talk (delegating Claude's
|
||||
subagents to the local engines).
|
||||
|
||||
## Legal
|
||||
|
||||
This is a non-commercial fan preservation/research project, in the same
|
||||
category as the many other N64 decompilation and native-port repositories on
|
||||
GitHub. It is **not affiliated with, endorsed by, or sponsored by** Nintendo,
|
||||
Rare, Microsoft, MGM, Danjaq, EON Productions, or any rights holder in
|
||||
Rare, Microsoft, Valve, MGM, Danjaq, EON Productions, or any rights holder in
|
||||
GoldenEye or James Bond. "GoldenEye 007", "007", "James Bond" and related
|
||||
marks belong to their respective owners; they are used here only to say which
|
||||
game this port runs. No official logos, box art, or marketing assets are used.
|
||||
@@ -635,4 +675,4 @@ is covered by [`NOTICE`](NOTICE), not by that license.
|
||||
|
||||
---
|
||||
|
||||
*Last updated 2026-10-03 — v0.4.1 · <a href="https://github.com/jkdansereau/goldeneye-pc-port">GitHub</a> · <a href="https://github.com/jkdansereau/goldeneye-pc-port/releases">Releases</a>*
|
||||
*Last updated 2026-10-05 — v0.5.0 · <a href="https://github.com/jkdansereau/goldeneye-pc-port">GitHub</a> · <a href="https://github.com/jkdansereau/goldeneye-pc-port/releases">Releases</a>*
|
||||
@@ -26,6 +26,10 @@ local and remote branches, and known community forks.
|
||||
row and delete it when the item lands.
|
||||
- Release scoping (which items go into which version) happens in the
|
||||
release notes, not here.
|
||||
- The **release gate** (what must be green before pushing / publishing a
|
||||
release) is kept as a local working note — `docs/dev/notes/RELEASE-BLOCKERS-v050.md`
|
||||
(gitignored, like the other P-pass reports). Keep it current; don't fork a
|
||||
second copy of it into the tracked tree.
|
||||
- **Known issues** (the player-facing subset) live in the table below. The
|
||||
README links to it and does not duplicate it.
|
||||
|
||||
@@ -67,9 +71,11 @@ platform carries the same gate: a full 21-level sweep plus a campaign pass.
|
||||
|
||||
## Order of work (decided 2026-09-30)
|
||||
|
||||
Work continues on `release/v0.4.1`, **to ship as v0.5.0 (named 2026-10-03,
|
||||
maintainer)** — cut after the playtest session and the easy folds land
|
||||
(push checklist: `dev/notes/PUSH-TASKS-v041.md`). There are two stages:
|
||||
Work continues on `release/v0.4.1`, **shipped as v0.5.0 (named 2026-10-03,
|
||||
cut staged 2026-10-05, maintainer)** — the docs, golden set (both platforms,
|
||||
63 frames each) and tooling are complete; the P11 squash + tag + push is owed
|
||||
maintainer consent (push checklist: `dev/notes/PUSH-TASKS-v041.md`). There are
|
||||
two stages:
|
||||
|
||||
**Stage A: now → the pre-1.0 build**
|
||||
1. Clear the §3 verification debt.
|
||||
@@ -134,7 +140,7 @@ 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 | open | D173, D292; maintainer 2026-10-02 | **Maintainer to re-check the end of Dam** (the last place a Bond offset was seen; the D173 level-start probe found no defect). Also seen in the Dam cutscene: an edge railing texture (a 2D element, maintainer believes) with odd transparency; possibly already fixed by the alpha pass. Capture a screenshot of each if still present. |
|
||||
| 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). |
|
||||
| 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. |
|
||||
@@ -175,7 +181,9 @@ A single campaign pass with these on the checklist would clear most of them.
|
||||
| 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). |
|
||||
| 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. |
|
||||
| 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 `<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. |
|
||||
|
||||
|
||||
|
||||
## 5. Modern-remaster layer (port features beyond the N64)
|
||||
|
||||
@@ -274,11 +282,12 @@ and a stage loader.
|
||||
| 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. |
|
||||
| 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 | **Windows done 2026-10-02:** all 21 levels re-based (`tools_pc/golden/<level>/win/`; pinned seed, START skips the intro, frames 900-1500; Cuba captures its ending); two-tier gate (structural + per-pixel with per-level limits). **Left:** Linux baselines (needs a real-GPU Linux box: `GE_PCDUMP` reads black on llvmpipe/WSLg), then the parity gate per platform. |
|
||||
| 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). |
|
||||
| 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 <file> --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. |
|
||||
| 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. |
|
||||
|
||||
@@ -1,15 +1,15 @@
|
||||
# GitHub Pages config. Site is served from the /docs folder on the default
|
||||
# branch (Settings -> Pages -> Deploy from a branch -> main / docs).
|
||||
title: GoldenEye 007 PC Port
|
||||
# v0.2.1 (Bing SEO): keep this SHORT -- jekyll-seo-tag splices it into the
|
||||
# Keep this SHORT (Bing SEO): jekyll-seo-tag splices it into the
|
||||
# home page <title> whenever the page title equals the site title, and the
|
||||
# long version made a ~230-char title. It also renders as the masthead
|
||||
# tagline on every page. Per-page meta/og descriptions come from each page's
|
||||
# front matter `description:` (see index.md), not from here.
|
||||
description: GoldenEye 007 (Nintendo 64, 1997) on the PC - decompiled, ported, and playable.
|
||||
# No GitHub theme: the site uses a hand-rolled _layouts/default.html plus the
|
||||
# design in _includes/head-custom.html (charcoal/off-white/amber, square and
|
||||
# flat.)
|
||||
# design in _includes/head-custom.html (the dark "amber terminal" theme: a
|
||||
# near-black starfield, a single amber accent, mono chrome over a sans body).
|
||||
show_downloads: false
|
||||
|
||||
# Canonical site location - used by jekyll-seo-tag (canonical URLs, og: tags)
|
||||
@@ -19,7 +19,7 @@ baseurl: /goldeneye-pc-port
|
||||
|
||||
# Link-preview card image for jekyll-seo-tag (og:image / twitter:image).
|
||||
# Path is relative to the site root; the plugin prepends url + baseurl.
|
||||
image: /img/attract-bunker1.png
|
||||
image: /img/shots/shot-28.jpg
|
||||
|
||||
# Both are on the GitHub Pages plugin whitelist - no Gemfile needed.
|
||||
# jekyll-seo-tag: per-page <title>/description/canonical/OpenGraph via {% seo %}
|
||||
@@ -35,35 +35,47 @@ plugins:
|
||||
# instead, so they resolve from the published pages too.
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# The site publishes a small curated set (see docs/dev/notes/DE-SLOP-DOCS-PLAN.md):
|
||||
# index.md, building.md, internals.md, porting-notes.md, dev-process.md,
|
||||
# dev/agentic-development.md
|
||||
# The site publishes a small curated set:
|
||||
# index.md, under-the-hood.md (the "under the hood" hub), building.md, plus
|
||||
# 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, 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.
|
||||
# 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:
|
||||
- BACKLOG.md
|
||||
- HANDOFF.md
|
||||
- ROADMAP.md
|
||||
- SetupGuide.md
|
||||
- StructureGuide.md
|
||||
- StyleGuide.md
|
||||
- dev/ACCURACY-AUDIT-2026-09-20.md
|
||||
- dev/AUDIO-PLAN.md
|
||||
- dev/AUDIT-M6-player-offsets.md
|
||||
- dev/CONTROLLER-INPUT-PLAN.md
|
||||
- dev/CUT-CONTENT-BACKLOG.md
|
||||
- dev/D176a-SKY-NOTES.md
|
||||
- dev/D356-SETTINGS-REGROUP-PLAN.md
|
||||
- dev/D357-SETTINGS-VALUES-PLAN.md
|
||||
- dev/GE-ENV-PROBES.md
|
||||
- dev/GEPD-INPUT-PLAN.md
|
||||
- dev/HANDOFF-ARCHIVE.md
|
||||
- dev/LEVEL-OBJECTIVES.md
|
||||
- dev/LEVEL-PLAYTEST.md
|
||||
- dev/LEVEL-STATUS.md
|
||||
- dev/MODERN-PORT-FEATURES-BACKLOG.md
|
||||
- dev/notes
|
||||
- dev/OPTIONS-MENU-PLAN.md
|
||||
- dev/preview-video.md
|
||||
- dev/QOL-INVENTORY.md
|
||||
- dev/RUMBLE-PLAN.md
|
||||
- dev/TEXTURE-GLITCH-ANALYSIS.md
|
||||
- dev/UNLOCKED-FPS-PLAN.md
|
||||
- dev/VIEWMODEL-RESEARCH.md
|
||||
- dev/WIDESCREEN-FOV-PLAN.md
|
||||
- dev/WATCH-SETTINGS-PLAN.md
|
||||
- dev/findings-index.csv
|
||||
- dev/findings.md
|
||||
- dev/game-behavior-reference.md
|
||||
|
||||
@@ -3,154 +3,166 @@
|
||||
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Libre+Baskerville:ital,wght@0,400;0,700;1,400&display=swap" rel="stylesheet">
|
||||
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono:wght@400;600;700&family=IBM+Plex+Sans:wght@400;600&display=swap" rel="stylesheet">
|
||||
|
||||
<style>
|
||||
/* ----------------------------------------------------------------------
|
||||
Hand-rolled site theme (replaces jekyll-theme-cayman, 2026-09-28).
|
||||
Look: committed charcoal/off-white/amber "night desk" — flat and square,
|
||||
serif display type (Libre Baskerville) over a system sans body, thin
|
||||
amber rules between sections, text navigation separated by middots.
|
||||
No gradients, no shadows, no rounded corners, no theme switcher.
|
||||
Site theme (replaces the 2026-09-28 "night desk" theme).
|
||||
Look: a dark, minimal "amber terminal" -- near-black space background
|
||||
(a faint starfield), a single amber accent (GoldenEye gold), a
|
||||
console/mono identity for chrome (IBM Plex Mono) over a clean
|
||||
readable sans body (IBM Plex Sans). Flat and calm: to reduce eye
|
||||
strain and evoke the old-school web. Content is untouched; this is
|
||||
a presentation layer only.
|
||||
--------------------------------------------------------------------- */
|
||||
:root {
|
||||
--bg: #1b1b1d; /* charcoal page */
|
||||
--bg-soft: #232326; /* code / alt rows */
|
||||
--bg-deep: #141416; /* code blocks, masthead */
|
||||
--ink: #e8e6e3; /* off-white body text */
|
||||
--ink-soft: #b5b1a9; /* secondary text */
|
||||
--ink-mute: #8d897f; /* captions, footer */
|
||||
--line: #3a3a3e; /* 1px borders */
|
||||
--accent: #ffb454; /* amber/gold */
|
||||
--accent-hi: #ffd08a; /* link hover */
|
||||
--measure: 44rem;
|
||||
--bg: #050607; /* true near-black */
|
||||
--bg-frame: #0c0d0f; /* the page frame */
|
||||
--bg-panel: #121316; /* code / cards / tables */
|
||||
--ink: #e6e4df; /* warm off-white */
|
||||
--ink-soft: #a9a7a0; /* secondary text */
|
||||
--ink-mute: #70706a; /* captions, footer */
|
||||
--line: #26282c; /* 1px borders */
|
||||
--line-2: #3a3d44; /* stronger borders */
|
||||
--accent: #e8a13a; /* GoldenEye gold (amber) */
|
||||
--accent-hi: #ffca7a; /* link / accent hover */
|
||||
--code-ink: #ffcf9e; /* code text (warm) */
|
||||
--th: #15161a; /* table header fill */
|
||||
--zebra: #0e0f12; /* alt row fill */
|
||||
--measure: 46rem;
|
||||
--mono: "IBM Plex Mono","Courier New",monospace;
|
||||
--sans: "IBM Plex Sans",Verdana,Geneva,sans-serif;
|
||||
}
|
||||
|
||||
html { background: var(--bg); }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||
"Helvetica Neue", Arial, sans-serif;
|
||||
font-size: 16.5px;
|
||||
line-height: 1.7;
|
||||
color: var(--ink);
|
||||
background: var(--bg);
|
||||
font-family: var(--sans);
|
||||
font-size: 14px;
|
||||
line-height: 1.62;
|
||||
/* faint starfield (the old-school "space" tile, softened) */
|
||||
background-color: var(--bg);
|
||||
background-image:
|
||||
radial-gradient(1px 1px at 22px 34px, rgba(255,255,255,.50), transparent),
|
||||
radial-gradient(1px 1px at 120px 82px, rgba(255,255,255,.28), transparent),
|
||||
radial-gradient(1.4px 1.4px at 68px 138px, rgba(232,161,58,.45), transparent),
|
||||
radial-gradient(1px 1px at 172px 44px, rgba(255,255,255,.20), transparent),
|
||||
radial-gradient(1px 1px at 92px 172px, rgba(255,255,255,.32), transparent);
|
||||
background-size: 200px 200px;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
text-rendering: optimizeLegibility;
|
||||
}
|
||||
|
||||
/* ---- Masthead -------------------------------------------------------- */
|
||||
.site-masthead {
|
||||
background: var(--bg-deep);
|
||||
border-bottom: 1px solid var(--accent);
|
||||
}
|
||||
.masthead-inner {
|
||||
max-width: 52rem;
|
||||
margin: 0 auto;
|
||||
padding: 2.6rem 1.5rem 1.6rem;
|
||||
text-align: center;
|
||||
}
|
||||
.masthead-title {
|
||||
display: inline-block;
|
||||
font-family: "Libre Baskerville", Georgia, "Times New Roman", serif;
|
||||
font-weight: 700;
|
||||
font-size: 2.1rem;
|
||||
line-height: 1.25;
|
||||
color: var(--ink);
|
||||
text-decoration: none;
|
||||
letter-spacing: 0.01em;
|
||||
}
|
||||
.masthead-title:hover { color: var(--accent); }
|
||||
.masthead-tagline {
|
||||
margin-top: 0.5rem;
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.95rem;
|
||||
}
|
||||
.masthead-nav {
|
||||
margin-top: 1.3rem;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.masthead-nav a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
padding: 0.1rem 0.15rem;
|
||||
}
|
||||
.masthead-nav a:hover { color: var(--accent-hi); text-decoration: underline; }
|
||||
.nav-sep { color: var(--ink-mute); padding: 0 0.35rem; }
|
||||
|
||||
/* ---- Content ---------------------------------------------------------- */
|
||||
.doc-content {
|
||||
max-width: 52rem;
|
||||
margin: 0 auto;
|
||||
padding: 2.5rem 1.5rem 1rem;
|
||||
}
|
||||
.doc-content p,
|
||||
.doc-content ul,
|
||||
.doc-content ol,
|
||||
.doc-content blockquote { max-width: var(--measure); }
|
||||
|
||||
.doc-content h1,
|
||||
.doc-content h2,
|
||||
.doc-content h3,
|
||||
.doc-content h4 {
|
||||
font-family: "Libre Baskerville", Georgia, "Times New Roman", serif;
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
color: var(--ink);
|
||||
margin-top: 2.4rem;
|
||||
margin-bottom: 0.9rem;
|
||||
}
|
||||
.doc-content h1 { font-size: 1.75rem; }
|
||||
.doc-content h2 { font-size: 1.35rem; }
|
||||
.doc-content h3 { font-size: 1.1rem; }
|
||||
.doc-content > h1:first-child { margin-top: 0.5rem; }
|
||||
|
||||
.doc-content a {
|
||||
a {
|
||||
color: var(--accent);
|
||||
text-decoration: underline;
|
||||
text-decoration-color: rgba(255, 180, 84, 0.45);
|
||||
text-decoration-color: rgba(232,161,58,.45);
|
||||
text-underline-offset: 2px;
|
||||
}
|
||||
.doc-content a:hover {
|
||||
a:hover { color: var(--accent-hi); text-decoration-color: currentColor; }
|
||||
|
||||
/* ---- the centred "page" frame -------------------------------------- */
|
||||
.frame {
|
||||
max-width: 50rem;
|
||||
margin: 20px auto 32px;
|
||||
background: var(--bg-frame);
|
||||
border: 1px solid var(--line-2);
|
||||
box-shadow: 0 0 0 1px #000, 0 18px 50px -30px #000;
|
||||
}
|
||||
|
||||
/* ---- banner -------------------------------------------------------- */
|
||||
.banner { text-align: center; padding: 2.4rem 1rem 1.4rem; }
|
||||
.banner .t {
|
||||
font-family: var(--mono);
|
||||
font-weight: 700;
|
||||
font-size: 1.7rem;
|
||||
letter-spacing: .05em;
|
||||
color: var(--ink);
|
||||
}
|
||||
.banner .tag { color: var(--ink-mute); font-size: .82rem; margin-top: .55rem; }
|
||||
|
||||
/* ---- top nav row --------------------------------------------------- */
|
||||
.sitenav {
|
||||
text-align: center;
|
||||
padding: .5rem 1rem;
|
||||
font-family: var(--mono);
|
||||
font-size: .78rem;
|
||||
letter-spacing: .05em;
|
||||
}
|
||||
.sitenav a {
|
||||
color: var(--accent);
|
||||
text-decoration: none;
|
||||
padding: 0 .35rem;
|
||||
}
|
||||
.sitenav a:hover { color: var(--accent-hi); text-decoration: underline; }
|
||||
.sitenav .sep { color: var(--ink-mute); }
|
||||
|
||||
/* the amber rule */
|
||||
.rule { border: 0; height: 1px; background: var(--line); margin: 0; }
|
||||
.rule.g {
|
||||
background: linear-gradient(90deg, transparent, var(--accent), transparent);
|
||||
height: 1px; opacity: .55;
|
||||
}
|
||||
|
||||
/* ---- content ------------------------------------------------------- */
|
||||
.doc-content { padding: 1.6rem 1.6rem .6rem; }
|
||||
.doc-content p, .doc-content ul, .doc-content ol,
|
||||
.doc-content blockquote { max-width: var(--measure); }
|
||||
|
||||
.doc-content h1 {
|
||||
font-family: var(--mono);
|
||||
font-size: 1.5rem;
|
||||
font-weight: 700;
|
||||
color: var(--accent);
|
||||
letter-spacing: .02em;
|
||||
margin: .2rem 0 1rem;
|
||||
}
|
||||
.doc-content > h1:first-child { margin-top: .2rem; }
|
||||
.doc-content h2 {
|
||||
font-family: var(--mono);
|
||||
font-size: 1.05rem;
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
letter-spacing: .03em;
|
||||
margin: 2rem 0 .8rem;
|
||||
padding-bottom: .35rem;
|
||||
border-bottom: 1px solid var(--line-2);
|
||||
}
|
||||
.doc-content h2::before { content: "\25aa "; color: var(--accent); }
|
||||
.doc-content h3 {
|
||||
font-family: var(--mono);
|
||||
font-size: .92rem;
|
||||
color: var(--accent-hi);
|
||||
text-decoration-color: currentColor;
|
||||
margin: 1.6rem 0 .5rem;
|
||||
}
|
||||
.doc-content > p > em:only-child {
|
||||
font-style: normal; color: var(--ink-soft); font-size: 1.05rem;
|
||||
}
|
||||
|
||||
.doc-content hr {
|
||||
border: 0;
|
||||
height: 1px;
|
||||
background: var(--accent);
|
||||
opacity: 0.5;
|
||||
margin: 2.5rem 0;
|
||||
border: 0; height: 1px; background: var(--line-2); margin: 2rem 0;
|
||||
}
|
||||
|
||||
.doc-content blockquote {
|
||||
color: var(--ink-soft);
|
||||
border-left: 3px solid var(--accent);
|
||||
padding: 0.2rem 0 0.2rem 1.1rem;
|
||||
padding: .2rem 0 .2rem 1rem;
|
||||
margin-left: 0;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
/* Lead paragraph on doc pages (the italic summary under the H1) */
|
||||
.doc-content > p > em:only-child {
|
||||
font-style: normal;
|
||||
color: var(--ink-soft);
|
||||
font-size: 1.05rem;
|
||||
}
|
||||
|
||||
/* ---- Code ------------------------------------------------------------ */
|
||||
/* ---- code ---------------------------------------------------------- */
|
||||
.doc-content code {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||
font-size: 0.86em;
|
||||
background: var(--bg-soft);
|
||||
font-family: var(--mono);
|
||||
font-size: .86em;
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
padding: 0.12em 0.4em;
|
||||
color: var(--ink);
|
||||
padding: .1em .4em;
|
||||
color: var(--code-ink);
|
||||
}
|
||||
.doc-content pre {
|
||||
background: var(--bg-deep);
|
||||
background: var(--bg-panel);
|
||||
border: 1px solid var(--line);
|
||||
padding: 1rem 1.15rem;
|
||||
max-width: var(--measure);
|
||||
@@ -158,105 +170,98 @@
|
||||
line-height: 1.55;
|
||||
}
|
||||
.doc-content pre code {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
color: var(--ink);
|
||||
font-size: 0.84rem;
|
||||
background: transparent; border: 0; padding: 0;
|
||||
color: var(--code-ink); font-size: .84rem;
|
||||
}
|
||||
|
||||
/* ---- Tables ----------------------------------------------------------- */
|
||||
/* ---- tables -------------------------------------------------------- */
|
||||
.doc-content table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
max-width: var(--measure);
|
||||
margin: 1.5rem 0;
|
||||
font-size: 0.92rem;
|
||||
border-collapse: collapse; width: 100%;
|
||||
max-width: var(--measure); margin: 1.5rem 0; font-size: .9rem;
|
||||
}
|
||||
.doc-content table th,
|
||||
.doc-content table td {
|
||||
border: 1px solid var(--line);
|
||||
padding: 0.5rem 0.75rem;
|
||||
text-align: left;
|
||||
.doc-content th, .doc-content td {
|
||||
border: 1px solid var(--line); padding: .5rem .7rem; text-align: left;
|
||||
}
|
||||
.doc-content table th { background: var(--bg-soft); font-weight: 600; }
|
||||
.doc-content table tr:nth-child(2n) { background: #1f1f22; }
|
||||
.doc-content th {
|
||||
background: var(--th); color: var(--accent);
|
||||
font-family: var(--mono); font-size: .74rem;
|
||||
letter-spacing: .05em; font-weight: 600;
|
||||
}
|
||||
.doc-content tr:nth-child(2n) td { background: var(--zebra); }
|
||||
|
||||
/* ---- Images & gallery -------------------------------------------------- */
|
||||
/* ---- images -------------------------------------------------------- */
|
||||
.doc-content img {
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 0;
|
||||
max-width: 100%;
|
||||
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-soft);
|
||||
padding: 0.6rem 0.9rem;
|
||||
margin: 1.5rem 0;
|
||||
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;
|
||||
}
|
||||
.doc-content summary { cursor: pointer; color: var(--accent); }
|
||||
|
||||
/* ---- Footer ------------------------------------------------------------- */
|
||||
.site-footer {
|
||||
max-width: 52rem;
|
||||
margin: 3rem auto 0;
|
||||
padding: 1.5rem 1.5rem 2.5rem;
|
||||
border-top: 1px solid var(--line);
|
||||
/* ---- console-viewer carousel (home page media) --------------------- */
|
||||
.viewer {
|
||||
max-width: 38rem; margin: 1rem auto;
|
||||
border: 1px solid var(--line-2);
|
||||
background: var(--bg-panel);
|
||||
font-family: var(--mono);
|
||||
}
|
||||
.footer-inner { max-width: var(--measure); }
|
||||
.viewer .vbar {
|
||||
display: flex; align-items: center; gap: .5rem;
|
||||
padding: .4rem .6rem; border-bottom: 1px solid var(--line);
|
||||
font-size: .72rem; color: var(--ink-mute); letter-spacing: .05em;
|
||||
}
|
||||
.viewer .vbar .dots { display: flex; gap: .3rem; }
|
||||
.viewer .vbar .dots i {
|
||||
width: 8px; height: 8px; border: 1px solid var(--line-2);
|
||||
display: inline-block;
|
||||
}
|
||||
.viewer .vbar .dots i:nth-child(1) { border-color: var(--accent); }
|
||||
.viewer .vbar .lbl { flex: 1; }
|
||||
.viewer .vbar .idx { color: var(--accent); }
|
||||
.viewer .vstage { position: relative; background: #000; aspect-ratio: 16 / 9; }
|
||||
.viewer .vstage img { width: 100%; height: 100%; object-fit: contain; display: block; }
|
||||
.viewer .vbtn {
|
||||
position: absolute; top: 50%; transform: translateY(-50%);
|
||||
background: rgba(6,7,8,.55);
|
||||
border: 1px solid var(--line-2);
|
||||
color: var(--accent);
|
||||
font-family: var(--mono); font-size: 1.3rem; line-height: 1;
|
||||
width: 34px; height: 52px; cursor: pointer; padding: 0;
|
||||
}
|
||||
.viewer .vbtn:hover { color: var(--accent-hi); border-color: var(--accent); }
|
||||
.viewer .vbtn.prev { left: 0; }
|
||||
.viewer .vbtn.next { right: 0; }
|
||||
.viewer .vcap {
|
||||
display: flex; justify-content: space-between; align-items: center;
|
||||
padding: .45rem .6rem; border-top: 1px solid var(--line);
|
||||
font-size: .74rem; color: var(--ink-soft); letter-spacing: .02em;
|
||||
}
|
||||
|
||||
/* ---- footer -------------------------------------------------------- */
|
||||
.site-footer { text-align: center; padding: 1.2rem 1rem 1.6rem; }
|
||||
.footer-stamp {
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.88rem;
|
||||
margin: 0 0 0.8rem;
|
||||
font-family: var(--mono); font-size: .78rem;
|
||||
letter-spacing: .03em; color: var(--ink-soft);
|
||||
margin: 0 0 .7rem;
|
||||
}
|
||||
.footer-stamp a { color: var(--accent); text-decoration: none; }
|
||||
.footer-stamp a:hover { color: var(--accent-hi); text-decoration: underline; }
|
||||
.footer-legal {
|
||||
color: var(--ink-mute);
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.6;
|
||||
margin: 0;
|
||||
color: var(--ink-mute); font-size: .72rem; line-height: 1.55;
|
||||
margin: 0 auto; max-width: var(--measure);
|
||||
}
|
||||
|
||||
@media (max-width: 42rem) {
|
||||
.masthead-title { font-size: 1.7rem; }
|
||||
.masthead-inner { padding: 2rem 1rem 1.2rem; }
|
||||
.doc-content { padding-top: 1.8rem; }
|
||||
@media (max-width: 40rem) {
|
||||
.frame { margin: 8px; }
|
||||
.banner .t { font-size: 1.35rem; }
|
||||
}
|
||||
|
||||
/* ---- Mermaid diagrams ---------------------------------------------------- */
|
||||
/* Kramdown renders ```mermaid fences as <pre><code class="language-mermaid">.
|
||||
The script below hands their text to mermaid and swaps in the SVG. Keep
|
||||
the pre unstyled (no code-block background) while it waits to render. */
|
||||
.doc-content pre.mermaid,
|
||||
.doc-content pre > code.language-mermaid {
|
||||
background: transparent;
|
||||
border: 0;
|
||||
color: var(--ink);
|
||||
padding: 0;
|
||||
}
|
||||
.doc-content pre.mermaid { text-align: center; overflow-x: auto; }
|
||||
.doc-content pre.mermaid:not([data-processed]) { visibility: hidden; }
|
||||
</style>
|
||||
|
||||
<script type="module">
|
||||
import mermaid from "https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs";
|
||||
|
||||
// Move each ```mermaid code block's text into a <pre class="mermaid"> that
|
||||
// mermaid.run() recognises.
|
||||
document.querySelectorAll("pre > code.language-mermaid").forEach((code) => {
|
||||
const pre = document.createElement("pre");
|
||||
pre.className = "mermaid";
|
||||
pre.textContent = code.textContent;
|
||||
code.parentElement.replaceWith(pre);
|
||||
});
|
||||
|
||||
mermaid.initialize({
|
||||
startOnLoad: false,
|
||||
securityLevel: "loose", // our own docs; needed for <br/> in node labels
|
||||
theme: "dark",
|
||||
});
|
||||
mermaid.run({ querySelector: "pre.mermaid" });
|
||||
</script>
|
||||
@@ -3,46 +3,44 @@
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="icon" type="image/png" href="{{ '/img/attract-bunker1.png' | relative_url }}">
|
||||
<link rel="icon" type="image/jpeg" href="{{ '/img/shots/shot-28.jpg' | relative_url }}">
|
||||
{%- seo -%}
|
||||
{%- include head-custom.html -%}
|
||||
</head>
|
||||
<body>
|
||||
<header class="site-masthead">
|
||||
<div class="masthead-inner">
|
||||
<a class="masthead-title" href="{{ '/' | relative_url }}">{{ site.title }}</a>
|
||||
<div class="masthead-tagline">{{ site.description }}</div>
|
||||
<nav class="masthead-nav">
|
||||
<a href="{{ '/' | relative_url }}">Home</a><span class="nav-sep">·</span>
|
||||
<a href="{{ '/internals' | relative_url }}">Internals</a><span class="nav-sep">·</span>
|
||||
<a href="{{ '/porting-notes' | relative_url }}">Porting notes</a><span class="nav-sep">·</span>
|
||||
<a href="{{ '/building' | relative_url }}">Building</a><span class="nav-sep">·</span>
|
||||
<a href="{{ '/dev-process' | relative_url }}">Dev process</a><span class="nav-sep">·</span>
|
||||
<a href="{{ '/dev/agentic-development' | relative_url }}">Two-agent study</a>
|
||||
</nav>
|
||||
</div>
|
||||
<div class="frame">
|
||||
|
||||
<header class="banner">
|
||||
<div class="t">{{ site.title }}</div>
|
||||
<div class="tag">{{ site.description }}</div>
|
||||
</header>
|
||||
|
||||
<nav class="sitenav">
|
||||
<a href="{{ '/' | relative_url }}">home</a><span class="sep">/</span>
|
||||
<a href="{{ '/under-the-hood' | relative_url }}">under the hood</a><span class="sep">/</span>
|
||||
<a href="{{ '/building' | relative_url }}">building</a>
|
||||
</nav>
|
||||
<div class="rule g"></div>
|
||||
|
||||
<main class="doc-content">
|
||||
{{ content }}
|
||||
</main>
|
||||
|
||||
<div class="rule g"></div>
|
||||
|
||||
<footer class="site-footer">
|
||||
<div class="footer-inner">
|
||||
<p class="footer-stamp">
|
||||
Last updated 2026-09-28 — v0.4.0
|
||||
<span class="nav-sep">·</span>
|
||||
<a href="https://github.com/jkdansereau/goldeneye-pc-port">GitHub</a>
|
||||
<span class="nav-sep">·</span>
|
||||
<a href="https://github.com/jkdansereau/goldeneye-pc-port/releases">Releases</a>
|
||||
</p>
|
||||
<p class="footer-legal">
|
||||
Non-commercial fan preservation/research project. No ROM or game assets
|
||||
are distributed; you supply a ROM you already own. Not affiliated with
|
||||
or endorsed by Nintendo, Rare, Microsoft, MGM, Danjaq, or EON
|
||||
Productions.
|
||||
</p>
|
||||
<div class="footer-stamp">
|
||||
last updated: 2026-10-07 · v0.5.0
|
||||
· <a href="https://github.com/jkdansereau/goldeneye-pc-port">GitHub</a>
|
||||
· <a href="https://github.com/jkdansereau/goldeneye-pc-port/releases">releases</a>
|
||||
</div>
|
||||
<div class="footer-legal">
|
||||
Non-commercial fan preservation / research project. No ROM or game assets are
|
||||
distributed; you supply a ROM you already own. Not affiliated with or endorsed
|
||||
by Nintendo, Rare, Microsoft, MGM, Danjaq, or EON Productions.
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -185,6 +185,13 @@ justifies. Rules:
|
||||
spend a cycle rebasing it**; re-derive only whatever part of its content
|
||||
is still true as a fresh, small PR against current `main`. Merging stale
|
||||
content back over newer information is a regression, not a save.
|
||||
- **Commit/tree hygiene is a standing rule, not a per-session judgment** —
|
||||
see AGENTS.md §Tree hygiene & release gate. In short: no local absolute
|
||||
paths or usernames in tracked files (use the placeholder convention), no
|
||||
contributor emails in prose (name + PR number), every vendored third-party
|
||||
license declared in `NOTICE`, no bulk ROM-derived captures in the tree
|
||||
(capture output dirs are gitignored — keep them on disk, untracked), and
|
||||
one batched commit per session rather than one per approved fix.
|
||||
|
||||
## 7. The rule-2 sign-off procedure (genuine `src/game` behavior changes)
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ More is already in place than "audio not started" suggests:
|
||||
`osAiGetLength()` in `port/src/libultra.c:852-862` discard everything
|
||||
(`osAiGetLength` returns 0), and `port/src/audio.c` never opens an SDL
|
||||
device. `main.c:141-142` already calls `audioInit()` / `mixerInit()`.
|
||||
- **The PD reference is local** at `C:\Users\james\Source\Repos\pd_port`,
|
||||
- **The PD reference is local** at `<repos>\pd_port`,
|
||||
with the full audio stack: `port/src/audio.c` (75 lines, SDL device +
|
||||
queue), `port/src/mixer.c` (~720 lines, software RSP-audio-ucode), and the
|
||||
wiring trick in `include/PR/abi.h`.
|
||||
@@ -201,4 +201,4 @@ M-50) → v0.4.0".
|
||||
| `port/src/libultra.c:848-863` | `osAi*` shims — wire to audio.c (Phase 1) |
|
||||
| `port/src/libultra.c:1237` | M_AUDTASK branch — stays a completing no-op; hosts `GE_AUDIOTRACE` |
|
||||
| `port/src/ucode.c` | Dummy `aspMain*` markers stay forever under CPU-bypass — document decision |
|
||||
| `C:\Users\james\Source\Repos\pd_port\port\src\{audio,mixer}.c`, `include\PR\abi.h` | PD ground truth: device glue + DSP cores + the macro-swap pattern |
|
||||
| `<repos>\pd_port\port\src\{audio,mixer}.c`, `include\PR\abi.h` | PD ground truth: device glue + DSP cores + the macro-swap pattern |
|
||||
@@ -5,7 +5,7 @@ this session; verification in `docs/dev/findings.md` D356 (GE_WSPROBE_RESET
|
||||
front + in-stage probes, regressions, build). Supersedes the "BOND FILE"
|
||||
section introduced in D353. Baselines: Turok PC port options (Nightdive, list
|
||||
provided by the maintainer), the PD port's `port/src/optionsmenu.c`
|
||||
(`C:\Users\james\Source\Repos\pd_port`), and plan §6 of
|
||||
(`<repos>\pd_port`), and plan §6 of
|
||||
`docs/dev/OPTIONS-MENU-PLAN.md` (the original "align with Turok" note).
|
||||
|
||||
## 1. Goal
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# D357 — Settings thresholds & values: align display units/defaults with the PD port + Turok PC
|
||||
|
||||
Status: **IMPLEMENTED 2026-09-30 (findings D443) for mouse sens ×, deadzone whole-%, volume step, FOV degrees, MSAA 16×, Restart game; texture filter unchanged by decision; frame-rate cap deferred (§0 presets + custom = separate project). Approved with changes; see §0.** Proposal dated 2026-09-27. Baselines: the PD port
|
||||
checkout (`C:\Users\james\Source\Repos\pd_port`, `port/src/optionsmenu.c` +
|
||||
checkout (`<repos>\pd_port`, `port/src/optionsmenu.c` +
|
||||
`input.c`/`video.c` config registrations), the Turok PC (Nightdive) list from
|
||||
the maintainer's notes (`docs/dev/notes/OPTIONS-MENU-PLAN.md` §6 — *taken from
|
||||
user notes, not checked independently*), and the N64 watch (ground truth for
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
# HANDOFF ARCHIVE — session-by-session narrative
|
||||
|
||||
> **Local machine paths are genericised** in this archive: `<repo>` = this repo's
|
||||
> working directory, `<repos>` = the local source-repos root, `<games>` / `<videos>`
|
||||
> = the local games / video roots, `<python>` = the local CPython install, `<temp>`
|
||||
> = a writable native temp dir. Session facts are unchanged.
|
||||
|
||||
> **Reference only.** Current state lives in the [README](../../README.md)
|
||||
> "Status" section and `docs/dev/LEVEL-STATUS.md`; per-finding detail is in
|
||||
> [`findings.md`](findings.md) §F/§H — look up via
|
||||
@@ -985,7 +990,7 @@ In KEEP-A test (sky quad only), the band shows (98,97,128) = fog color. This mea
|
||||
### Environment & build
|
||||
|
||||
- Build: `export PATH="/c/msys64/mingw64/bin:$PATH" && cmake --build build-pc -j 8`
|
||||
- Python: `"C:\Users\james\AppData\Local\Programs\Python\Python313\python.exe"` (has numpy+PIL)
|
||||
- Python: `"<python>\python.exe"` (has numpy+PIL)
|
||||
- Run: from repo root, ROM in `./data/`. Level: `-level_36`
|
||||
- Frame capture: F12 → `ppm/frame_NNN.ppm`; or `GE_PCDUMP=lo-hi:step`
|
||||
- Input script: `GE_INPUTSCRIPT="120:START;..."` (M-99: CANNOT reach mouselook)
|
||||
@@ -1081,7 +1086,7 @@ Facility = `-level_34` (stageId 34 = `LEVELID_FACILITY`; `boss.c` decodes `-leve
|
||||
4. Diff behaviour vs a level whose opening beat DOES work (e.g. Dam `-level_33`) to isolate "Facility data" vs "general beat machinery".
|
||||
5. Any fix: port-only unless it's the documented ABI/layout class; new finding = D266+; append to `docs/porting-notes.md` if a new bug class emerges.
|
||||
|
||||
**Environment (unchanged from prior sessions):** build `export PATH="/c/msys64/mingw64/bin:$PATH" && cmake --build build-pc -j 8` (or `./build-pc.sh ntsc-final`); kill any running `ge007*` before linking. Exe `build-pc/ge007.x86_64.exe`, run from repo root (ROM in `./data/`). Headless: `GE_INPUTSCRIPT` (tokens A/B/Z/START/UP/DOWN/LEFT/RIGHT/CUP/CDOWN/CLEFT/CRIGHT/SDOWN/SNONE, `tick:TOKEN;…`, pulse = 6 reads), `GE_PCDUMP=lo-hi:step`, `GE_INPUTLOG=1`, F12 → `ppm/shot_NNN.ppm`. Python (PIL/numpy) at `/c/Users/james/AppData/Local/Programs/Python/Python313/python.exe`; **cast uint8 arrays to int32 before bit shifts**.
|
||||
**Environment (unchanged from prior sessions):** build `export PATH="/c/msys64/mingw64/bin:$PATH" && cmake --build build-pc -j 8` (or `./build-pc.sh ntsc-final`); kill any running `ge007*` before linking. Exe `build-pc/ge007.x86_64.exe`, run from repo root (ROM in `./data/`). Headless: `GE_INPUTSCRIPT` (tokens A/B/Z/START/UP/DOWN/LEFT/RIGHT/CUP/CDOWN/CLEFT/CRIGHT/SDOWN/SNONE, `tick:TOKEN;…`, pulse = 6 reads), `GE_PCDUMP=lo-hi:step`, `GE_INPUTLOG=1`, F12 → `ppm/shot_NNN.ppm`. Python (PIL/numpy) at `<python>`; **cast uint8 arrays to int32 before bit shifts**.
|
||||
|
||||
---
|
||||
|
||||
@@ -1345,7 +1350,7 @@ will do a full manual playtest in lieu of the `verify.sh` sweep.
|
||||
3. **Steam Deck test session (~1 hr, needs the hardware — who has the Deck?):** install tarball via USB, Facility walk+fire (D203 repro profile), gamepad check, `Video.DisplayFPS` on Dam + Streets, grab `ge007.crash.log` if anything faults.
|
||||
4. Tag `v0.2.0-pre`; publish pre-release with known issues: D243 (Dam end race), D230 (music quality), D245 (water seam), D246 (edge strips), D197 (face wrap on Silo), D75 (front-end models), the D251 4K ghost row, and "not yet verified on real Deck hardware". Flip to final after the user's playtest passes.
|
||||
|
||||
**Environment:** build as before (`/c/msys64/usr/bin/bash.exe -lc 'export PATH=/c/msys64/mingw64/bin:$PATH; cd /c/Users/james/Source/Repos/gh-fullhist && ./build-pc.sh ntsc-final'`). CI (`.github/workflows/ci.yml`) builds Linux per push — the `linux-build` job's artifact is the bundle source for the tarball if local Linux testing isn't available.
|
||||
**Environment:** build as before (`/c/msys64/usr/bin/bash.exe -lc 'export PATH=/c/msys64/mingw64/bin:$PATH; cd <repo> && ./build-pc.sh ntsc-final'`). CI (`.github/workflows/ci.yml`) builds Linux per push — the `linux-build` job's artifact is the bundle source for the tarball if local Linux testing isn't available.
|
||||
|
||||
## F10 overlay QoL session (2026-09-13, shipped in v0.2.0-pre) — D251; known bug: intermittent 4K-fullscreen "repeat of top entry" at panel bottom (MSAA×fullscreen driver suspect, unverified)
|
||||
|
||||
@@ -1373,7 +1378,7 @@ will do a full manual playtest in lieu of the `verify.sh` sweep.
|
||||
**Environment / state notes:**
|
||||
- User's live `data/ge007.ini` already has `Fullscreen = 1`, `MSAA = 4` (backup at `/tmp/ge007.ini.bak` — I did not modify their ini). Monitor: single display 3072×1728, window fullscreen at 0,0.
|
||||
- `ppm/user4k.png` exists from a PowerShell `CopyFromScreen` capture but was taken with the overlay **closed** — low value; user asked to stop analyzing captures.
|
||||
- Build: `/c/msys64/usr/bin/bash.exe -lc 'export PATH=/c/msys64/mingw64/bin:$PATH; cd /c/Users/james/Source/Repos/gh-fullhist && ./build-pc.sh ntsc-final'`. Launch from repo root: `./build-pc/ge007.x86_64.exe &` (needs `./data/` ROM).
|
||||
- Build: `/c/msys64/usr/bin/bash.exe -lc 'export PATH=/c/msys64/mingw64/bin:$PATH; cd <repo> && ./build-pc.sh ntsc-final'`. Launch from repo root: `./build-pc/ge007.x86_64.exe &` (needs `./data/` ROM).
|
||||
- Design doc for the overlay: `docs/dev/OPTIONS-MENU-PLAN.md` (approach C: port-layer overlay, zero src/ menu edits).
|
||||
|
||||
## M-123 (2026-09-14) — D194 ① log mined + wrap-spike fixed; spazz NOT in our writes (they're rock-stable); next: live capture with the new residue/autoaim logging, then user A/B playtest
|
||||
@@ -1398,7 +1403,7 @@ will do a full manual playtest in lieu of the `verify.sh` sweep.
|
||||
|
||||
## M-122 (2026-09-13) — D194 GEPD-mirror aim IMPLEMENTED + user-confirmed "mechanically works"; three items left: ① crosshair/arm spazz glitch, ② aim-mode FPS drop (perf.ps1), ③ sensitivity tuning. Next session starts at ①.
|
||||
|
||||
**The aim rewrite landed.** After four failed stick-synthesis models (absolute → dial → relative-angle → velocity-mapped stick; see M-121 + commits `8a8d066b`/`650b7f63`/`7f65ed15`), the session found and read **GEPD's actual source** (`C:\Users\james\Games\Emulators\Nintendo 64\1964_GEPD_Edition\1964\source.tar.xz` → `MouseInjectorPlugin/games/goldeneye.c`, extracted copy at `/c/Users/james/AppData/Local/Temp/gepd-src/`) and mirrored its model exactly. **User verdict: "mechanically it works... the aim mode works how we'd want."** Committed as `ef49d9e6` on `fix/d194-mouse-dt-decouple` (branch is up to date with main; PR #75/#76 already merged in).
|
||||
**The aim rewrite landed.** After four failed stick-synthesis models (absolute → dial → relative-angle → velocity-mapped stick; see M-121 + commits `8a8d066b`/`650b7f63`/`7f65ed15`), the session found and read **GEPD's actual source** (`<games>\Emulators\Nintendo 64\1964_GEPD_Edition\1964\source.tar.xz` → `MouseInjectorPlugin/games/goldeneye.c`, extracted copy at `<temp>/gepd-src/`) and mirrored its model exactly. **User verdict: "mechanically it works... the aim mode works how we'd want."** Committed as `ef49d9e6` on `fix/d194-mouse-dt-decouple` (branch is up to date with main; PR #75/#76 already merged in).
|
||||
|
||||
**The model (all in `port/src/input.c`, `aimGepdCompute()` ~line 1377):** mouse delta moves a crosshair POSITION accumulator (`s_gepdCrossX/Y`, clamped ±`GEPD_CROSSHAIR_LIMIT`=5.16); each tick it writes `crosshair_x/y_pos` + `gun_azimuth_angle/turning` (GEPD formulas, failsafe weapon offsets 0.15/0, RATIOFACTOR=1 for our 4:3, `GEPD_BASE_FOV`=90) and edge-scrolls the camera only past 72% toward the screen edge (`(ratio-0.72)*475*dt*(fov/90)`). **No look stick is emitted while aiming.** On aim release we stop writing and the game's own damping eases crosshair+arm back to centre; every non-aim tick adopts `crosshair_x/y_pos` so re-entry starts where the game left it (GEPD does the same).
|
||||
|
||||
@@ -1437,7 +1442,7 @@ will do a full manual playtest in lieu of the `verify.sh` sweep.
|
||||
**RMB aim-mode (D194 continuation): two more attempts this session, both did not land, session ended mid-plan on the weekly usage limit.**
|
||||
|
||||
- **Attempt 1 (small, safe): raised `AIM_MOVE_THRESH` 0.3->2.5px and lowered `Input.MouseAimSpeed` default 16->14 (matching the user's live ini, which had drifted to 50).** User's verdict: **"didn't actually fix anything"** — confirms the core problem isn't a magnitude/gain issue, it's the shape of the response.
|
||||
- **Real root cause found via code reading (still valid, not invalidated by what follows):** `bondviewProcessInput(s8 stick_x, s8 stick_y, ...)` in `src/game/bondview2.c` takes a **signed 8-bit stick** — the smallest representable nonzero aim-turn value is a hard jump to 10% speed (stick=61; `(stick-60)/10` is exactly 0 at stick<=60). No threshold/curve tuning on the port's synthesis side can produce a smooth ramp through an integer that can only be 60 or >=61. Confirmed via `pd_port` (`C:\Users\james\Source\Repos\pd_port`) reference: PD's own mouse-look (`src/game/player.c:3669`, `src/game/bondeyespy.c:945`) never synthesizes an N64 stick at all -- it's `theta += mdx * scale`, a continuous float angle accumulator, no floor, no int8 quantization anywhere.
|
||||
- **Real root cause found via code reading (still valid, not invalidated by what follows):** `bondviewProcessInput(s8 stick_x, s8 stick_y, ...)` in `src/game/bondview2.c` takes a **signed 8-bit stick** — the smallest representable nonzero aim-turn value is a hard jump to 10% speed (stick=61; `(stick-60)/10` is exactly 0 at stick<=60). No threshold/curve tuning on the port's synthesis side can produce a smooth ramp through an integer that can only be 60 or >=61. Confirmed via `pd_port` (`<repos>\pd_port`) reference: PD's own mouse-look (`src/game/player.c:3669`, `src/game/bondeyespy.c:945`) never synthesizes an N64 stick at all -- it's `theta += mdx * scale`, a continuous float angle accumulator, no floor, no int8 quantization anywhere.
|
||||
- **User also reported a second, more specific symptom mid-investigation: aim resets to center the instant mouse movement pauses (even while still holding RMB), breaking precision tasks (shoot-the-lock, watch-laser-cuts-metal-in-train puzzles).** Traced `bondviewCurrentPlayerUpdateSpeedTheta`/`speedVertaUp`/`Down` (bondview2.c) in detail: confirmed this is a smoothed rate-based decel model (`g_CurrentPlayer->speedtheta` eases toward 0 over several ticks when input stops, does NOT hard-reset) -- so a literal snap-to-center was not found in that specific mechanism. Root cause of the reset symptom specifically was **not conclusively identified** before moving to the rewrite.
|
||||
- **Attempt 2 (bigger, PD-style, approved by user before starting): bypassed the s8-stick synthesis entirely for aim-mode.** `port/src/input.c` computed a continuous -1..1 turn/vert speed (`inputGetContinuousAimSpeed()`, new function + header decl) instead of synthesizing `sx`/`sy`; `src/game/bondview2.c`'s aim-mode block (`#ifdef PORT`) read it directly, bypassing the `(stick-60)/10` conversion but leaving all downstream `speedtheta`/`speedverta` easing untouched. Removed the now-dead `Input.AimBand`/`Input.MouseAimCurve` knobs. **Compiled clean (verified both PORT and N64 `#ifdef` brace balance carefully), crash-free in a headless sanity check -- but broke live: "somethings broken... can't aim at all... it locks my input completely with the mouse."** Root cause of THAT breakage was not identified (no repro available headlessly; ran out of session before a careful live-debug pass). **Fully reverted** (`git checkout -- src/game/bondview2.c port/src/input.c port/include/input.h` back to the `db6eba2b` merge commit) — repo confirmed clean, PR #62's mouse work is back to its pre-session (working, if imperfectly-tuned) state. Live ini's `MouseAimSpeed` also manually restored 14->16 to match the reverted code default.
|
||||
|
||||
@@ -1893,7 +1898,7 @@ current testing, folded in:
|
||||
player's already-fixed 1P viewmodel isn't implicated. Narrows the
|
||||
search surface.
|
||||
3. **D194 mouse feel — external corroboration, not a new mechanism.**
|
||||
Investigated `C:\Users\james\Games\Emulators\Nintendo 64\1964_GEPD_Edition`
|
||||
Investigated `<games>\Emulators\Nintendo 64\1964_GEPD_Edition`
|
||||
(the user's reference point for "feels better") — the 1964 emulator's
|
||||
bundled `Mouse_Injector.dll` (closed-source, no source available; the
|
||||
emulator's own `source.tar.xz` is just the stock 1964 core). Its exports
|
||||
@@ -2839,7 +2844,7 @@ except `bg.c`'s `GE_D63` lines which are not `#ifdef PORT`-guarded).
|
||||
|
||||
## Playtest bug reports (M-49 cont., 2026-09-04) — logged for future triage, NOT v0.1.0 blockers
|
||||
|
||||
User playtested the **v0.1.0 win64 bundle** (`C:\Users\james\Games\goldeneye-pc-port-0.1.0-win64`,
|
||||
User playtested the **v0.1.0 win64 bundle** (`<games>\goldeneye-pc-port-0.1.0-win64`,
|
||||
fresh save). Bugs filed in findings §F (D191–D197), all alpha-acceptable.
|
||||
NB: Cradle does **not** crash — it's the D193 scripted-sequence stall (an
|
||||
earlier note here wrongly said "Cradle also crashes").
|
||||
@@ -3303,7 +3308,7 @@ collision geometry. Full ranked next-steps in findings.md D205.
|
||||
6. Build + smoke after probe removal: `./build-pc.sh ntsc-final`, then
|
||||
`-level_09` 60 s crash-free with `GE_AUDIOTRACE=1`.
|
||||
### Environment (unchanged except noted):
|
||||
- Build: `/c/msys64/usr/bin/bash.exe -lc 'cd /c/Users/james/Source/Repos/gh-fullhist && ./build-pc.sh ntsc-final'` (login shell for MINGW64 PATH; SDL2.dll at `/c/msys64/mingw64/bin`).
|
||||
- Build: `/c/msys64/usr/bin/bash.exe -lc 'cd <repo> && ./build-pc.sh ntsc-final'` (login shell for MINGW64 PATH; SDL2.dll at `/c/msys64/mingw64/bin`).
|
||||
- Capture run: `GE_AUDIOTRACE=1 GE_INPUTSCRIPT="$(cat ../scratchpad/exact_run3_script.txt)" ./ge007.x86_64.exe -level_09` from `build-pc/`; kill with `taskkill //F //PID <pid>` (msys `kill` does not work).
|
||||
- `mupen64/` (N64 emulator, user's A/B reference) at repo root — added to .gitignore this session.
|
||||
- `scratchpad/` is gitignored: banktest/, exact_match.py, fire scripts, match outputs are all local-only. To re-verify the bank exoneration: `scratchpad/banktest/harness.exe` (rebuild from harness.c against port/src/romdata.c if stale) then `python scratchpad/banktest/diff_bank.py`.
|
||||
@@ -3391,7 +3396,7 @@ archived user capture: `scratchpad/d202-m66/` (`check_dump.py`,
|
||||
The `[EXPIRE]` line and the guard/expire code STAY (permanent, documented).
|
||||
Build note for this shell: toolchain at `/c/msys64/mingw64/bin`, and
|
||||
`cmake --build -j` hit "Cannot create temporary file in C:\\Windows\\" —
|
||||
`ninja -j1` with `TMP/TEMP=C:/Users/james/AppData/Local/Temp` works.
|
||||
`ninja -j1` with a writable native `TMP/TEMP` works.
|
||||
|
||||
## Done this session (M-66) — D202 ROOT CAUSE ESTABLISHED: the audible stuck door loop is sound 203 behaving exactly as ROM + ground-truth code specify — faithful N64 behaviour (original quirk), not a port bug. Disposition A/B/C pending user decision.
|
||||
|
||||
@@ -4765,7 +4770,7 @@ does not.
|
||||
Build locally: `cmake -S . -B build-linux -G Ninja -DROMID=ntsc-final
|
||||
-DCMAKE_C_COMPILER=gcc-14 -DCMAKE_CXX_COMPILER=g++-14` then `ninja -C
|
||||
build-linux` (~20s on drvfs). WSL `Ubuntu` distro provisioned (toolchain +
|
||||
SDL2/zlib/GL dev); repo at `/mnt/c/Users/james/Source/Repos/gh-fullhist`
|
||||
SDL2/zlib/GL dev); repo at `<repo>`
|
||||
(fixed the malformed `/etc/wsl.conf` `[automount]` + `wsl --shutdown`).
|
||||
`data/` has the ROM + sidecars already.
|
||||
|
||||
@@ -5074,7 +5079,7 @@ playtest handoff: `docs/dev/M-35-PLAYTEST-CHECKLIST.md`; WI-3 design:
|
||||
(`d43_emit.py` / `d69_emit.py` / `d88_emit.py --regen`, all pass clean) → `./build-pc.sh
|
||||
ntsc-final` links 242/242, `-level_09` runs crash-free past frame 1200. **No `assets/*.bin`
|
||||
extraction needed** — the committed `assets/` is self-sufficient; the ROM is runtime-only.
|
||||
The old repo at `C:/Users/james/Source/Repos/007` has the ROM + a prior build.
|
||||
The old repo at `<repos>/007` has the ROM + a prior build.
|
||||
|
||||
- **D168 — VERIFIED.** Rebuilt with the flip fix, captured `-level_09` frame 320:
|
||||
the exit arrow points up, ammo "7|93" reads normally bottom-right — matches
|
||||
|
||||
@@ -50,7 +50,7 @@ and forth** through shared written artifacts, directed by one human.
|
||||
|
||||
| | |
|
||||
|---|---|
|
||||
| Local agent | `unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL` on a single **NVIDIA RTX 5090**, driven mainly through the **[pi](https://pi.dev/)** coding agent. Unsloth Desktop (Unsloth's local model runtime, which can drive agents such as Claude Code) was also trialed but not used significantly. |
|
||||
| Local agent | `unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XL` on a single **NVIDIA RTX 5090**, driven mainly through the **[pi](https://github.com/earendil-works/pi)** coding agent. Unsloth Desktop (Unsloth's local model runtime, which can drive agents such as Claude Code) was also trialed but not used significantly. |
|
||||
| Hosted agent | **Claude**, via **Claude Code**, on a Claude Pro subscription; mostly **Sonnet 5**, with **Opus 5** used as an escalation tier for the hardest problems and whenever there was subscription budget to spend on it |
|
||||
| Human | one person: direction, work partitioning, integration, and every build / playtest / frame-capture the agents could not run |
|
||||
| Base | fork of the [GoldenEye 007 decompilation](https://github.com/n64decomp/007) (years of prior work by Larry Ficken ("kholdfuzion") and contributors) |
|
||||
@@ -59,58 +59,32 @@ and forth** through shared written artifacts, directed by one human.
|
||||
The port is the GoldenEye-specific porting work **on top of** those two
|
||||
existing bodies of work; it is not a from-scratch reimplementation of either.
|
||||
|
||||
In practice the models formed a **three-tier escalation**: the local model
|
||||
handled well-scoped work, Sonnet 5 took what it stalled on, and Opus 5 was
|
||||
reserved for the bugs that needed the most reasoning held at once; the same
|
||||
"escalate when stuck" move applied at every level.
|
||||
In practice the models formed a **three-tier escalation** — the local model
|
||||
handled well-scoped work, Sonnet 5 took what it stalled on, and Opus 5 the
|
||||
rest; the same "escalate when stuck" move applied at every level.
|
||||
|
||||
## Timeline
|
||||
|
||||
All dates from this repository's own commit history (August–September 2026).
|
||||
|
||||
```mermaid
|
||||
gantt
|
||||
dateFormat YYYY-MM-DD
|
||||
axisFormat %b %d
|
||||
title GoldenEye 007 PC port; from fork to v0.3.0
|
||||
section Local model - Qwen 3.8 via pi
|
||||
Fork + PC-port scaffolding :m1, 2026-08-16, 1d
|
||||
Full compile + link (~230 TUs) :m2, 2026-08-20, 1d
|
||||
Boot to window (ROM map + SDL2) :m3, 2026-08-20, 1d
|
||||
OS shims, threads, fast3d integration :2026-08-21, 2d
|
||||
First rendered frames :m4, 2026-08-22, 1d
|
||||
Offline asset-conversion pipeline :2026-08-22, 2d
|
||||
Entire intro renders (logos to cast) :m5, 2026-08-24, 1d
|
||||
section Both agents - handoff workflow
|
||||
Claude joins :milestone, 2026-08-27, 0d
|
||||
Stage load; Bunker 1 renders + firefight :2026-08-27, 2d
|
||||
21 solo levels load + render + no-crash :m6, 2026-08-29, 1d
|
||||
SDL input layer (kbd/mouse/gamepad) :2026-08-29, 1d
|
||||
Front-end flow (menu to briefing to start) :m7, 2026-08-30, 1d
|
||||
File-backed EEPROM saves :2026-08-31, 1d
|
||||
section Month one - front end to v0.3.0
|
||||
Audio mixer (music + SFX) :2026-09-02, 7d
|
||||
Steam Deck hardening + F10 overlay QoL :2026-09-14, 3d
|
||||
Final fix sweep (D309-D321) + release review :2026-09-18, 3d
|
||||
v0.3.0 released (full campaign playtested) :milestone, 2026-09-20, 0d
|
||||
```text
|
||||
# phase timeline (from the port's own commit dates)
|
||||
Aug 16 day 0 fork + PC-port scaffolding
|
||||
Aug 20 day 4 full compile + link (~230 TUs); boot to window
|
||||
Aug 22 day 6 first rendered frames; offline asset-conversion pipeline
|
||||
Aug 24 day 8 entire intro renders (logos -> gun-barrel -> cast)
|
||||
Aug 27 day 11 Claude joins (handoff workflow begins)
|
||||
Aug 29 day 13 21 solo levels load + render + unattended no-crash; SDL input layer
|
||||
Aug 30 day 14 front-end flow (menu -> briefing -> start)
|
||||
Aug 31 day 14 file-backed EEPROM saves
|
||||
Sep 02 audio mixer (music + SFX)
|
||||
Sep 14 Steam Deck hardening + F10 overlay QoL
|
||||
Sep 18 final fix sweep (D309-D321) + release review
|
||||
Sep 20 day 35 v0.3.0 released: full campaign at Agent difficulty, audio
|
||||
complete, bundles for Windows, Linux and Steam Deck
|
||||
```
|
||||
|
||||
- **Day 0** (16 Aug): repository forked, PC-port scaffolding added.
|
||||
- **Day 4** (20 Aug): the entire ~230-TU game + libultra set compiles and
|
||||
links as a host binary.
|
||||
- **Day 6** (22 Aug): first real frames render.
|
||||
- **Day 8** (24 Aug): the whole intro sequence renders; logos, gun-barrel,
|
||||
cast roll.
|
||||
- **Day 11** (27 Aug): second agent joins.
|
||||
- **Day 13** (29 Aug): **all 21 solo missions load, render, and survive an
|
||||
unattended play window without crashing.**
|
||||
- **Day 14** (30–31 Aug): front end playable end to end (menu → mission
|
||||
select → difficulty → briefing → start); file-backed saves.
|
||||
- **Day 35** (20 Sep): **v0.3.0 released** — the full campaign playtested
|
||||
end to end at Agent difficulty, audio complete, bundles for Windows,
|
||||
Linux and Steam Deck.
|
||||
|
||||
So roughly **two weeks**, one person part-time, to take a decompilation from
|
||||
Roughly **two weeks**, one person part-time, took the decompilation from
|
||||
"builds an N64 ROM" to "boots on desktop, renders every solo level, playable
|
||||
through the front end into the early game". The rest of month one (29 Aug –
|
||||
20 Sep) took it from there to a public release: the 21-level sweep to
|
||||
@@ -134,12 +108,15 @@ issues documented rather than outstanding.
|
||||
|
||||
### Commit velocity
|
||||
|
||||
```mermaid
|
||||
xychart-beta
|
||||
title "Commits per week (week start)"
|
||||
x-axis ["8/16", "8/23", "8/30", "9/6", "9/13", "9/20"]
|
||||
y-axis "commits" 0 --> 250
|
||||
bar [20, 109, 203, 141, 220, 6]
|
||||
```text
|
||||
# commits per week (week start)
|
||||
8/16 ## 20
|
||||
8/23 ########### 109
|
||||
8/30 #################### 203
|
||||
9/6 ############## 141
|
||||
9/13 ###################### 220
|
||||
9/20 # 6 (release day only)
|
||||
(each # ~ 10 commits)
|
||||
```
|
||||
|
||||
Weekly totals (the final week is release day only). The step up from the
|
||||
@@ -150,10 +127,11 @@ at once.
|
||||
|
||||
### Who did what
|
||||
|
||||
```mermaid
|
||||
pie showData title "Commits by agent (raw count, snapshot through early September)"
|
||||
"Claude" : 161
|
||||
"Local model (Qwen 3.8 / pi)" : 62
|
||||
```text
|
||||
# commits by agent (raw count, snapshot through early September)
|
||||
Claude ################ 161 (72%)
|
||||
Local model (Qwen 3.8/pi) ###### 62 (28%)
|
||||
(each # ~ 10 commits)
|
||||
```
|
||||
|
||||
(Phase A, the first 26 commits, to 24 Aug, was entirely the local model,
|
||||
@@ -184,10 +162,11 @@ chain in relatively few, large commits, and the local model kept contributing
|
||||
raw commits; the foundation is a heavier third of the project than its commit
|
||||
share suggests; the developer's estimate is roughly:
|
||||
|
||||
```mermaid
|
||||
pie showData title "Agent effort, milestone-weighted"
|
||||
"Claude" : 60
|
||||
"Local model (Qwen 3.8 / pi)" : 40
|
||||
```text
|
||||
# agent effort, milestone-weighted
|
||||
Claude ############ 60%
|
||||
Local model (Qwen 3.8/pi) ######## 40%
|
||||
(each # ~ 5%)
|
||||
```
|
||||
|
||||
The local model's ~40% is front-loaded and foundational (the build, the boot
|
||||
@@ -201,21 +180,19 @@ result worth taking away.
|
||||
|
||||
This is the part worth paying attention to.
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
H["Human: direction, integration,<br/>build + playtest verification"]
|
||||
C["Claude / Claude Code<br/>frontier, hosted"]
|
||||
Q["Qwen 3.8 via pi<br/>open-weight, local RTX 5090"]
|
||||
D[(Shared artifacts:<br/>HANDOFF.md · findings.md · porting-notes.md)]
|
||||
```text
|
||||
# the handoff workflow
|
||||
|
||||
H -->|scopes task, budget, files| C
|
||||
H -->|scopes task, budget, files| Q
|
||||
C <-->|reads / appends| D
|
||||
Q <-->|reads / appends| D
|
||||
C -.->|usage limit reached| Q
|
||||
Q -.->|hard structural bug| C
|
||||
C -->|patch + write-up| H
|
||||
Q -->|patch + write-up| H
|
||||
HUMAN
|
||||
direction, integration, build + playtest verification
|
||||
| scopes task, budget, files | scopes task, budget, files
|
||||
v v
|
||||
CLAUDE (hosted) QWEN 3.8 / pi (local, RTX 5090)
|
||||
| patch + write-up | patch + write-up
|
||||
+--------------+ +--------------+
|
||||
v v
|
||||
SHARED ARTIFACTS (both agents read + append)
|
||||
HANDOFF.md . findings.md . porting-notes.md
|
||||
```
|
||||
|
||||
Both agents worked against the **same three written artifacts**, which is what
|
||||
@@ -255,8 +232,7 @@ Honest notes, for anyone weighing whether this transfers.
|
||||
|
||||
- The **local model carried the groundwork phase**; build system, boot
|
||||
chain, OS shims, and the offline-converter architecture. None of it was
|
||||
rewritten later. A 27B open model on one consumer GPU was genuinely productive
|
||||
on this.
|
||||
rewritten later.
|
||||
- **The written-artifact discipline made agent output compound.** An agent
|
||||
starting cold late in the project was more effective than one early on,
|
||||
because the accumulated notes were good. This is also what made the two
|
||||
@@ -286,3 +262,63 @@ Honest notes, for anyone weighing whether this transfers.
|
||||
**Still outstanding:** see
|
||||
[`docs/ROADMAP.md`](https://github.com/jkdansereau/goldeneye-pc-port/blob/main/docs/ROADMAP.md),
|
||||
the single tracker for open work.
|
||||
|
||||
## Since the two-agent phase
|
||||
|
||||
The two-agent setup above is the **initial phase**, snapshotted at v0.3.0
|
||||
(the project has since shipped v0.4.x and is heading to v0.5.0; the model
|
||||
and harness record below is current through that point).
|
||||
|
||||
Since then the emphasis has shifted from "the two models that built it" to
|
||||
**testing a lot of new models and harnesses**, to find what's fastest and
|
||||
cheapest per task. The long-time local workhorse stayed **Unsloth's Qwen 3.8 27B**;
|
||||
in the final week that broadened into a menu, and the **current standard is
|
||||
NInfer + Strata**. On the hosted side, the Claude orchestrator stepped up from
|
||||
**Sonnet 5 / Opus 5** to the **5.5 tier** (Sonnet 5.5 / Opus 5.5, out in the
|
||||
last few weeks), which has driven the most recent work. The local menu:
|
||||
|
||||
- **Qwen 3.8 27B** ([Unsloth](https://huggingface.co/unsloth/Qwen3.8-27B-GGUF),
|
||||
`UD-Q4_K_XL`) — the primary local workhorse for most of the port's life, doing
|
||||
nearly all of the local groundwork on its own. A larger-context
|
||||
[`ISTA-DASLab/Qwen3.8-27B-GSQ-RCO-GGUF`](https://huggingface.co/ISTA-DASLab/Qwen3.8-27B-GSQ-RCO-GGUF)
|
||||
(`IQ3_S`) was also briefly trialed for bigger-context workloads, then shelved
|
||||
(little use since v0.2.0).
|
||||
- **NInfer (Windows)**
|
||||
([natpate/ninfer-windows](https://github.com/natpate/ninfer-windows), v0.9.0,
|
||||
CUDA 13.1 — a standalone Windows port of the NInfer engine, with MTP /
|
||||
DFlash2 speculative decoding) — ran **Qwen 3.8 27B** in base, NVFP4 and
|
||||
NVFP4-full artifacts; trialed in the final week, now half of the standard
|
||||
and the fast day-to-day local driver.
|
||||
- **Qwen 3.8 Flash-Next** via
|
||||
[Strata](https://github.com/Niko1221/Strata) (Niko1221's MIT-licensed
|
||||
C++/CUDA/HIP engine) — a 125B mixture-of-experts model that normally needs a
|
||||
server, run **locally on a single gaming GPU** (IQ1_M, 1-bit) for the
|
||||
biggest-context coding passes. The other half of the standard.
|
||||
- **[claude-code-delegate-local](https://github.com/fegone/claude-code-delegate-local)**
|
||||
— an MCP server that let the Claude Code orchestrator and the local models
|
||||
talk, delegating Claude's subagents to the local engines. A nice tool, but
|
||||
**mostly deprecated now**: hand-offs between agents are done by hand for now,
|
||||
pending a better parallel-agent handoff system that drops the MCP overhead.
|
||||
- **GPT-6 (Sol)** via [OpenRouter](https://openrouter.ai) — a frontier hosted
|
||||
model, **trialed** to see how it held up (not the hardest-work tool). Its
|
||||
code output is still owed a code-quality audit in this doc.
|
||||
|
||||
The practical effect: the project stopped depending on any one model or its
|
||||
usage limits. The local GPU is now the default path for high-volume work, with
|
||||
hosted frontier models reserved for the problems the local models can't close.
|
||||
|
||||
**Measurement owed.** Which of these was best on a given task — and each
|
||||
model's rough cost-to-performance ratio — has not yet been measured properly;
|
||||
attributing individual commits to the model that produced them is still a
|
||||
manual pass. That includes a **code-quality audit of the GPT-6 (Sol) trial**.
|
||||
All of it is pending and will be added here when it lands.
|
||||
|
||||
A concrete reason the commit-date route is unreliable: the `D350` commit
|
||||
(`1303c0eb`, "expose watch-backed Bond settings in PC options and F10")
|
||||
carries a **27 Sep** timestamp on both its author and committer dates, but
|
||||
the session notes place that work on **4 Oct**. Because the two git dates
|
||||
agree, this is **not** a rebase artifact (a rebase would have moved the
|
||||
committer date while preserving the author date); it is a genuine divergence
|
||||
between the recorded commit time and the working session, and resolving it
|
||||
takes the session log, not the commit date. This is the class of edge case
|
||||
the attribution pass has to walk by hand.
|
||||
@@ -380,4 +380,16 @@ D517,FIXED,Holding left/right on a Bond settings row dropped the fps:,FIXED (liv
|
||||
D518,FIXED,Pad layout preset was one global value:,FIXED (live pass 2026-10-04 incl.
|
||||
D519,FIXED,"One PC options UI: the file-select ""PC Options"" entry opens the F10 overlay; the MENU_PC_OPTIONS screen is deleted —",FIXED (headless-verified 2026-10-04; by-eye pass owed)
|
||||
D520,FIXED,"F10 overlay slider wedge, slider marker and dropdown arrow antialiased (they stepped in whole canvas pixels that grow with the window) —",FIXED (headless before/after capture; by-eye owed)
|
||||
D521,PARTIAL,A fixed-frame golden window that overlaps the intro flyby is platform-nondeterministic (cross-platform goldens); verify.sh's linux pixel-gate skip is driver-co…,PARTIAL (rule recorded 2026-10-04; the two residual bands CLOSED 2026-10-05 by D525's box re-round —
|
||||
D522,FIXED,The golden window's frame-900 stem is not settled gameplay on several levels —,FIXED 2026-10-05 (recipe-side re-base:
|
||||
D523,CLOSED,The golden recipe does not pin save state:,SUPERSEDED by D529 (2026-10-07) —
|
||||
D524,FIXED,The golden tooling was destructive and drift-prone:,FIXED 2026-10-05 (both pins idempotent —
|
||||
D525,FIXED,"The whole <level>/linux/ golden set was stale, not just on the six re-based levels —","FIXED 2026-10-05 (21-level box re-round, 63 frames, save pinned present; structural parity 21/21, Archives/Streets bands attributed to save state, not a renderer)"
|
||||
D526,CLOSED,P13 Dam-ending grate / edge-railing transparency is faithful N64 behaviour (port == 1964/GEPD); the per-triangle census probe is retained as a generic draw-sta…,"CLOSED — faithful, confirmed on 1964/GEPD (2026-10-07):"
|
||||
D527,OPEN,GE_PCDUMP written first:last (a colon where the dash belongs) silently dumps EVERY frame —,OPEN (harness documentation; a log-or-fail-closed fix is owed in the port layer)
|
||||
D528,OPEN,"GE_STARTMENU skips the EEPROM read, so front-end-only sessions never import the emulator save —",OPEN (harness documentation; the D514 imported-save test must run from a normal boot; a log-or-import fix is owed in the port layer)
|
||||
D529,FIXED,The golden gate depends on the save file's CONTENT —,FIXED 2026-10-07 (canonical save in-tree at tools_pc/golden/ge007.eep; verify.sh installs it like the ini pin; all 21 win sets re-captured under it —
|
||||
D530,?,The box (X220) linux golden re-round landed —,AMENDED BY D531 (2026-10-07) —
|
||||
D531,FIXED,capture_p7.sh's D529 adaptation was incomplete —,"FIXED 2026-10-07 (loop re-pins the canonical save per level + cd ""$ROOT""; fresh 21/21 box re-capture committed —"
|
||||
D532,FIXED,The three P12 U5 should-fixes applied —,FIXED 2026-10-07 (tooling-only:
|
||||
D450,FIXED,"Unplugging and replugging a controller was not picked up (user, GameSir-G7 Pro; 2026-09-29).","FIXED — port-only: the render thread's gfx_sdl_handle_events drains the same SDL queue as video.c's host pump but had no case for SDL_CONTROLLERDEVICEADDED/REMOVED, so it usually swallowed them and i…"
|
||||
|
@@ -788,6 +788,18 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
|
||||
| D518 | **Pad layout preset was one global value: setting another player's preset changed the main player's mapping too** — Port-only; D498 follow-up, maintainer report; full `## D518` entry at file tail | FIXED (live pass 2026-10-04 incl. split-screen 2P) |
|
||||
| D519 | **One PC options UI: the file-select "PC Options" entry opens the F10 overlay; the MENU_PC_OPTIONS screen is deleted** — Port-only (the one front.c entry call is kept); Bond-file rows + watch chooser re-keyed to file select; 4 probes retired, `GE_FILESELECT_OVERLAY` added; full `## D519` entry at the end of this file. | FIXED (headless-verified 2026-10-04; by-eye pass owed) |
|
||||
| D520 | **F10 overlay slider wedge, slider marker and dropdown arrow antialiased (they stepped in whole canvas pixels that grow with the window)** — Port-only; D504 follow-up, P4k investigation; full `## D520` entry at file tail | FIXED (headless before/after capture; by-eye owed) |
|
||||
| D521 | **A fixed-frame golden window that overlaps the intro flyby is platform-nondeterministic (cross-platform goldens); verify.sh's linux pixel-gate skip is driver-conditional** — Port-only (docs + verify.sh probe); D117 class, P7; full `## D521` entry at the end of this file | PARTIAL (rule recorded 2026-10-04; the two residual bands CLOSED 2026-10-05 by D525's box re-round — save state, not a renderer; cross-platform parity stays informational, the gate is per platform) |
|
||||
| D522 | **The golden window's frame-900 stem is not settled gameplay on several levels — the win golden gate is not green (5/21 REGRESSION honest sequential, 7/21 concurrent)** — Golden gate; D117 follow-up; full `## D522` entry at file tail | FIXED 2026-10-05 (recipe-side re-base: per-level windows settled on every level; win gate 21/21 PASS honest sequential; no tol limit raised) |
|
||||
| D523 | **The golden recipe does not pin save state: `verify.sh` pins the ini but never touches `data/ge007.eep`, and the goldens were captured with a save present** — Golden gate; D514/D522 follow-up; full `## D523` entry at file tail | SUPERSEDED by D529 (2026-10-07) — the frame depends on the save's *content*, not its presence (the presence claim was only ever measured on Archives); the gate now installs the canonical in-tree save `tools_pc/golden/ge007.eep` |
|
||||
| D524 | **The golden tooling was destructive and drift-prone: `verify.sh`'s per-level ini/save pin snapshotted the previous level's game-written file (a sweep overwrites the user's own `data/ge007.ini`), and `capture_p7.sh` hardcoded the pre-D522 windows while deleting the save it captures with** — Golden tooling (U5 review of `tools_pc/**`); D522/D523 follow-up; full `## D524` entry at file tail | FIXED 2026-10-05 (both pins idempotent — first call snapshots the user's files, every level re-pins from that snapshot; `capture_p7.sh` reads the recipe out of `verify.sh` and requires the save present) |
|
||||
| D525 | **The whole `<level>/linux/` golden set was stale, not just on the six re-based levels — re-rounding it closes D521's two residual render bands** — Golden gate (P7b, box); D521/D522/D523 follow-up; full `## D525` entry at file tail | FIXED 2026-10-05 (21-level box re-round, 63 frames, save pinned present; structural parity 21/21, Archives/Streets bands attributed to save state, not a renderer) |
|
||||
| D526 | **P13 Dam-ending grate / edge-railing transparency is faithful N64 behaviour (port == 1964/GEPD); the per-triangle census probe is retained as a generic draw-state diagnostic** — P13 Dam ending (B1 lead); full `## D526` entry at file tail | CLOSED — faithful, confirmed on 1964/GEPD (2026-10-07): the show-through is in both (port == N64), so no port bug, no fix (D306/D291 class); the per-triangle census probe (GE_D526/GE_D526BOX/GE_D526MAX) is retained as a generic diagnostic |
|
||||
| D527 | **`GE_PCDUMP` written `first:last` (a colon where the dash belongs) silently dumps EVERY frame** — harness trap (`port/src/video.c`); full `## D527` entry at file tail | OPEN (harness documentation; a log-or-fail-closed fix is owed in the port layer) |
|
||||
| D528 | **`GE_STARTMENU` skips the EEPROM read, so front-end-only sessions never import the emulator save** — harness trap; D514 follow-up; full `## D528` entry at file tail | OPEN (harness documentation; the D514 imported-save test must run from a normal boot; a log-or-import fix is owed in the port layer) |
|
||||
| D529 | **The golden gate depends on the save file's CONTENT — the D523 "presence, not which save" claim is refuted; the gate must pin a canonical save** — Golden gate; D523 follow-up (D525/D524); full `## D529` entry at file tail | FIXED 2026-10-07 (canonical save in-tree at `tools_pc/golden/ge007.eep`; `verify.sh` installs it like the ini pin; all 21 win sets re-captured under it — three consecutive clean 21/21 sweeps are the valid green record; the no-arg `verify.sh` path now exits 1; the box linux re-round under it landed as D530) |
|
||||
| D530 | **The box (X220) linux golden re-round landed — first cross-machine test of the four D117 flaky levels** — Golden gate (P7, box); D529 never-do 3/6 follow-up; full `## D530` entry at file tail | AMENDED BY D531 (2026-10-07) — the "canonical eep" provenance claim was wrong: the re-round ran under the box-LOCAL save (the `capture_p7.sh` per-level re-pin bug); the D531 re-capture under the true canonical eep supersedes this set |
|
||||
| D531 | **`capture_p7.sh`'s D529 adaptation was incomplete — the per-level re-pin restored the local save, so the 2026-10-05 box re-round ran under the box-local save; fixed + re-captured under the canonical eep** — Golden gate (P7, box); D529/D530 follow-up, P12 U5 review finding; full `## D531` entry at file tail | FIXED 2026-10-07 (loop re-pins the canonical save per level + `cd "$ROOT"`; fresh 21/21 box re-capture committed — 47/63 frames differ from the box-local-save set; pixel-gated box verify owed for a window with the box's WSLg GPU display active — today's session is on the software fallback, black read-backs) |
|
||||
| D532 | **The three P12 U5 should-fixes applied — the gate's ini pin now fails closed, the seed is hard-pinned (stray `GE_RSEED` warns), and `audiodebug.ps1`'s dead A/B switches are gone** — golden gate + tooling; D531/U5 follow-up; full `## D532` entry at file tail | FIXED 2026-10-07 (tooling-only: `pin_ini_640x480` fails closed like `pin_eep`; `GOLDEN_SEED` literal + warning, capture `eval` unaffected; `-AB`/`-Old`/`-OldMode` removed with a note) |
|
||||
| D407 | **Tanks cannot be boarded/exited on PC — the v0.4.0 use/reload split (D378/D393) removed the B-button tap the engine's tank handlers in `bondview2.c` consume (user report 2026-09-28, Runway/Streets)** — full `## D407` entry at file tail | PARTIAL (fix landed in v0.4.0, port-only): present `GE_CONT_B` on the use (E / pad A) rising edge only while `g_PlayerIsInTank == 1` or `g_BondCanEnterTank != 0`; E keeps its D378 no-reload-fallback semantics elsewhere, N64 layout unaffected. D407(b) same cycle: front PC Options page-edge highlight clamp + mouse-wheel / W-S paging (wheel queue consumed on the menu, D223); its wheel mapping shipped inverted and was fixed to match W/S (wheel up = step up). D407(c): board-animation lockout, menu-accept B gate in tank states, in-tank aim routed through the legacy velocity stick + `Input.TankAimScale` knob. RESOLVED 2026-09-28: all port-only (src/game zero-diff), 10 TANKDBG probes stripped, release binary verified clean, user live tank drive signed off (board + exit OK, aim feel good). |
|
||||
| D408 | **`Game.SkipIntro` skips the post-mission failure dossier: with it on, entering a level then aborting (watch Z+A) or dying (KIA) returns straight to the menus — no REPORT / "Mission status: KILLED IN ACTION / ABORTED" screen** (user report 2026-09-28; save/AllUnlocked ruled out) — full `## D408` entry at file tail | OPEN (cosmetic, not root-caused past the handoff site). v0.4.0 decision: SkipIntro stays EXPERIMENTAL / not recommended for regular users — F10 row relabelled "Skip intro (EXPERIMENTAL)". |
|
||||
| D450 | **Unplugging and replugging a controller was not picked up (user, GameSir-G7 Pro; 2026-09-29).** — full `## D450` entry at file tail | FIXED — port-only: the render thread's `gfx_sdl_handle_events` drains the same SDL queue as video.c's host pump but had no case for `SDL_CONTROLLERDEVICEADDED/REMOVED`, so it usually swallowed them and `inputRescanPads` never ran. Both pumps now post a request; the rescan runs at the top of `inputUpdate`, on the thread that reads the pads. |
|
||||
@@ -5998,7 +6010,7 @@ probe scripts left.
|
||||
prints `ALL CHECKS PASSED`, and `-level_09` / `-level_20` then segfault before
|
||||
frame 1 (`EXCEPTION: 0xc0000005`, faulting return address is an ASCII
|
||||
model-name string). The pre-migration working sidecar
|
||||
(`C:/Users/james/Source/Repos/007/data/pccg-ntsc-final/pccg.bin`, 3604378 B,
|
||||
(`<repos>/007/data/pccg-ntsc-final/pccg.bin`, 3604378 B,
|
||||
2421-B manifest) drops into this tree and `-level_09` runs crash-free.
|
||||
|
||||
**Isolation done (M-38).**
|
||||
@@ -12871,7 +12883,7 @@ Verified: build clean (`build-pc`, MSYS2 MINGW64/ninja); `tools_pc/verify.sh bun
|
||||
|
||||
## D317 — Crash when toggling Game.AllUnlocked back and forth in-game (user report, v0.3.0 playtest build, 2026-09-18) — no dump left behind.
|
||||
|
||||
**Reported:** "Toggling unlock all back and forth in game causes a crash." Reproduced by the user on the current-tip playtest exe (the one at `C:\Users\james\Desktop\ge007-v0.3.0-playtest`, includes D310/D311 fixes). **Repro detail (user, 2026-09-18):** level = **Runway**. **No crash dump or log was left in that folder** — hard exit, so Windows Event Viewer is the only system-side artifact to check.
|
||||
**Reported:** "Toggling unlock all back and forth in game causes a crash." Reproduced by the user on the current-tip playtest exe (the Desktop `ge007-v0.3.0-playtest` folder, includes D310/D311 fixes). **Repro detail (user, 2026-09-18):** level = **Runway**. **No crash dump or log was left in that folder** — hard exit, so Windows Event Viewer is the only system-side artifact to check.
|
||||
|
||||
**Closed as a symptom of D316, not a real crash.** Evidence:
|
||||
- No WER report exists for the playtest exe; every `ge007` AppCrash in Windows Error Reporting traces to this repo's own `build-pc` dev runs, none after 12:00 that day, while the playtest session was ~13:15 — a real crash would have logged one.
|
||||
@@ -15453,7 +15465,7 @@ I logged all of this as **D397** (`docs/dev/findings.md`, index CSV, and the D18
|
||||
|
||||
**Status (2026-09-28, per `scratch/d397_fs/HANDOFF-D397.md`) — OPEN, re-scoped; no fix landed.** An in-tree CI→I import-routing block in `gfx_pc.cpp` `import_texture` was tried and is **ineffective** (the D116 stale-tile-metadata hypothesis is REFUTED: D161 already routes `fmt==CI && palette==G_TT_NONE` to `import_texture_i8`; the GE_TEXI logs show `palfmt=0` on all 299 rows) — the block was removed 2026-09-28. Established: the data path is 100% clean (299 row strips upload byte-exact vs `scratch/d397_fs/gunbarrel.pgm`); the artifact is static; MSAA, mips, RAND_NOISE, 5-bit quant and texture-cache aliasing are ruled out. Leading hypotheses: **H1** — the file-select custom-CC LERP (`gDPSetCombineLERP(TEXEL0, PRIMITIVE, ENV_ALPHA, ...)`) uses ENV_ALPHA 0x14 as the mix factor; the port feeds it as `20/255 ≈ 0.078` (`G_CCMUX_ENV_ALPHA` input, `gfx_pc.cpp`), so a black texel renders ≈0.92×prim ≈ the measured 27/28/17 black-region census — the N64 RDP 5-bit coefficient scaling (check `rsp/graphics/gmain.s` and the PD port's CC engine for the same combine id) is the root-cause question; **H2** — the 6-of-9-row comb is a second, separate row-strip-geometry defect (non-integer quad y + NEAREST, or the 256-tri vertex-ring wrap between the two tris of a row); **H3** — part of the darkened look is authentic N64 (G_TC_FILT 4-bit intensity dither, unimplemented here). A `GE_D397SH` env-gated shader-formula probe (in `gfx_opengl.cpp`, ~30 lines) stays in-tree until the root cause lands, then is removed. Next steps per the handoff: Q1 correlate which of the 20 compiled shaders is the gun-barrel row-strip one, Q4 settle the LERP-factor scaling against gmain.s/PD, Q5 native 640×480 FBO capture to re-measure the comb period.
|
||||
|
||||
**Status (2026-09-27b, static read against the local `pd_port` checkout) — H1 REFUTED, H2 promoted to leading cause.** Cross-checked `G_CCMUX_ENV_ALPHA` handling against `C:\Users\james\Source\Repos\pd_port\port\fast3d\gfx_pc.cpp`: byte-identical formula (`tmp.r=tmp.g=tmp.b=rdp.env_color.a`, then `/255.0f` at the vertex-color pack site) — no special-cased coarser scaling in either port. There is no "5-bit RDP coefficient" quirk in the real color combiner; PRIM/ENV alpha are genuine 8-bit `gDPSetEnvColor` fields and the combiner's A/B/C/D math is full 8-bit on real hardware (the 5-bit-precision idea conflated the CC with an unrelated hardware unit). **H1 is very likely not the (or not the only) cause; the uniform darkening it predicts may simply be faithful/authored** — do not correct it without an N64/1964-GEPD reference frame.
|
||||
**Status (2026-09-27b, static read against the local `pd_port` checkout) — H1 REFUTED, H2 promoted to leading cause.** Cross-checked `G_CCMUX_ENV_ALPHA` handling against `<repos>\pd_port\port\fast3d\gfx_pc.cpp`: byte-identical formula (`tmp.r=tmp.g=tmp.b=rdp.env_color.a`, then `/255.0f` at the vertex-color pack site) — no special-cased coarser scaling in either port. There is no "5-bit RDP coefficient" quirk in the real color combiner; PRIM/ENV alpha are genuine 8-bit `gDPSetEnvColor` fields and the combiner's A/B/C/D math is full 8-bit on real hardware (the 5-bit-precision idea conflated the CC with an unrelated hardware unit). **H1 is very likely not the (or not the only) cause; the uniform darkening it predicts may simply be faithful/authored** — do not correct it without an N64/1964-GEPD reference frame.
|
||||
|
||||
Found a concrete geometric mechanism for **H2**, promoted to leading cause: `titleRenderFolderMenuBackgroundLines` (`src/game/title2.c:24`) emits `lry - uly == 3` (U10.2 units) per row — a **0.75-native-pixel-tall** rect, not 1.0 — via the universal N64 `((y+1)<<2)-1` idiom. That idiom is only exact on real hardware because the RDP's scan converter *floors* the fixed-point Y to an integer scanline before testing coverage. `gfx_draw_rectangle` (`gfx_pc.cpp:3443`) does not replicate that: it maps the U10.2 corners straight to continuous NDC floats and rasterizes with the GPU's ordinary continuous coverage rules. A sub-1-native-pixel-tall rect stacked 299 times (each offset exactly 1.0 native px from the last) does not reliably rasterize as "the one scanline the idiom intended" under continuous rasterization — which of the two neighbouring device rows a given source row's fractional edges land in drifts as the stack accumulates, worsened by any non-integer native→window scale (`gfx_adjust_viewport_or_scissor`'s `ratioY`). This is a rasterization property of the geometry, not stale state, so it naturally explains the first-render (non-re-entry) trigger that refuted D182's hypothesis for this instance. Full mechanism + tell for future occurrences: `docs/porting-notes.md` §D15. **Not yet done: a live capture to confirm the comb period tracks `ratioY`** (predicted: comb period should change if the window/render resolution changes, at fixed native resolution) — this is the concrete falsifiable test before attempting a fix. No code changed this pass (diagnosis only).
|
||||
|
||||
@@ -16524,7 +16536,7 @@ Whichever loop runs `SDL_PollEvent` first gets the event, and the render loop us
|
||||
|
||||
**Maintainer live-pad checklist.** (a) Fresh ini, no edits: Jinx 1.1 plays exactly as before (RT fire, LT/LB aim, A use, X reload, B gadget, Y next weapon, stick clicks crouch, Start pause; menus A/X accept, B/Y cancel). (b) F10 (Back) -> Input -> Controller...: Layout preset right -> Custom; seat/action rows appear. (c) Select Fire, press A: "Press button..." (the A press does not bind itself); press LB -> Primary: LB; fire with LB in a level, RT no longer fires. (d) Capture then tap B: cancelled, row unchanged, page does not close. Capture then hold B ~1 s: binds B. (e) Capture then tap Back: cancel; hold Back ~1 s: slot cleared. Esc and Delete work too. (f) Y on a row clears the slot without a modal. Start unbound -> still pauses (guard). (g) Reset to defaults restores Jinx 1.1 strings on all seats. (h) Controller-only: do (b)-(f) without touching the keyboard; never trapped. (i) 2P split-screen with two pads: set seat Pad 2 to a different Fire; confirm P1 and P2 differ; Pad 2 Y/B in menus still accept/cancel. With keyboard/mouse as P1 (one pad, MPMode Auto) the single pad uses the seat 0 table. (j) Unplug/replug a pad mid-match: bindings stay with the seat. (k) PrevWeapon (unbound by default): bind it and confirm it steps weapons backwards (untested live; reuses the wheel-back A+Z idiom). (l) Front-end PC Options -> Input -> Controller: same page; A starts capture, hold Back clears. (m) Quit and relaunch: the ini keeps `PadPreset` and the `Input.Pad*.` strings.
|
||||
|
||||
**Credit.** The two-names-per-action SDL string format, the menu lockout guard idea and release-before-capture arming follow outside PR #109 (italoarruda); the commits carry `Co-authored-by: italoarruda`.
|
||||
**Credit.** The two-names-per-action SDL string format, the menu lockout guard idea and release-before-capture arming follow outside PR #109 (italoarruda); the commits carry a `Co-authored-by: italoarruda` trailer.
|
||||
|
||||
**Confidence.** High: preset 0 byte-equivalence (exhaustive over the mapping function), build, boot/level/overlay smoke, page render. Medium: pad-driven capture timing and the front-end page behaviour (no physical pad here). Low: PrevWeapon feel.
|
||||
|
||||
@@ -17114,3 +17126,168 @@ keep: the next `./build-pc.sh` run reconfigures in place and picks up x86_64.
|
||||
**Fix (`port/src/optionsoverlay.c`).** `drawWedge`: the solid body as runs of equal whole height plus one coverage-alpha top pixel per 1-px column; the marker and the dropdown arrow via `drawTriDownAA` (per-pixel area coverage from 4 sub-rows). Render state is set once per shape (`aaBegin`) and each piece is 2 commands (`aaPiece`, pieces under alpha 6 skipped), so the display-list size stays about where it was (D516 overflow history; `OV_BUF_CMDS` 16384). Applies in every Display mode (maintainer: the overlay is port UI, not part of the N64 picture).
|
||||
|
||||
**Verified.** Build rc=0, no warnings; `GE_OPTIONSOVERLAY=1 GE_OPTIONSOVERLAY_SECTION=__HdrAudio GE_PCDUMP=300-300` before/after: the ramps go from ~7 visible steps to a smooth ramp, markers/arrows softened; no DL overflow logged. **Owed:** maintainer by-eye at their window size.
|
||||
|
||||
|
||||
## D521: A fixed-frame golden window that overlaps the intro flyby is platform-nondeterministic (P7, cross-platform goldens)
|
||||
|
||||
**Problem (P7, 2026-10-04, Linux golden baselines).** The golden recipe was run verbatim on a real-GL Linux box (Mesa 23.0.3 / Intel HD 3000, X, 640x480 ini pin, `GE_INPUTSCRIPT="20:START"`, `GE_PCDUMP="900-1500:300"`, `GE_QUITFRAME=1502`) for all 21 levels and compared against `tools_pc/golden/<level>/win/`. At the settled stems (1200/1500; Cuba 600/900) the cross-platform delta is 1.142-6.958% of pixels over tol 2 -- worst non-outlier dam 1500 6.958%, worst grid-cell dmean 14.315 (surface1 1200), phash <= 3 -- but Dam's frame 900 measures 92.863% over tol 2 with phash 83, a completely different scene. The same-machine spread is what makes that number meaningful: repeat Linux runs of the same recipe differ by 0.115% (1200) and 0.219% (1500) at those stems (cap_1 vs cap_3, exact tol 2) but by 72.460% at 900, i.e. the 900 disagreement is the flyby still moving, not a renderer.
|
||||
|
||||
**Cause (D117 class, no new nondeterminism).** The intro flyby is wall-clock paced, and the capture harness steps *sim* frames, not wall-clock frames: `GE_PCDUMP` and `GE_QUITFRAME` both key off `frames` (`port/src/video.c:1406`, `++frames` in `videoEndFrame()`), which advances once per `portRenderGfxTask()` (`port/src/libultra.c:1256`), i.e. once per retrace actually consumed by `__scMain`. Retrace messages are dropped at the posting site when the game's queue is nearly full (`port/src/libultra.c:753`), so a box that cannot hold 60 fps stretches wall time *without* advancing `frames` (per-frame render times in cap_dam.log: frame 900 13009 us, 1200 15121 us, 1500 11808 us). On this box the flyby was still running at frame 900 -- it settles between frame ~830 and ~1130 run to run -- while the Windows golden run had it settled before 900. The scripted START press is not the difference: the pulse's six `GE_INPUTLOG` lines precede "frame 5 rendered" on both platforms, and `GE_INPUTSCRIPT_MS=1` variants (150/300/600 ms) change nothing.
|
||||
|
||||
**Rule (this is a documentation finding, not a code fix).** A cross-platform golden comparison is only meaningful at stems that are settled gameplay on *every* platform in the set. Either move the window past the slowest platform's flyby settle point, or report in-flyby stems as a scene offset and never gate on them. `GE_DETERM=1` is the right tool for Linux-vs-Linux regression runs only: it makes retrace generation call-sequenced (det_a vs det_b, exact tol 2: 4.654/5.562/5.331% at 900/1200/1500) but changes the frame -> sim mapping, so its 14.6-16.2% against the win/ goldens (det_a vs dam/win: 15.788/14.594/16.171%) is expected and must never be used for cross-platform comparison.
|
||||
|
||||
**Port-layer change (same sweep).** `tools_pc/verify.sh`'s linux pixcount/framediff skip (N6 / M-48, "GE_PCDUMP reads black") is a property of the GL *driver*, not of the platform. It is now driver-conditional: a `glxinfo -B` renderer probe sets `LINUX_SOFTWARE_GL`, and only llvmpipe/softpipe/swrast/dynrecomp skip the pixel gate; a real driver (Mesa iGPU/dGPU, NVIDIA) runs the same gate as Windows. `glxinfo` missing or unrecognised keeps the old conservative skip, so an unknown box behaves exactly as before. **Known limitation:** the probe whitelists by *blacklist* (`llvmpipe|softpipe|swrast|dynrecomp`), so a future software driver outside that list would enable the pixel gate and false-FAIL; the probe is not rewritten here.
|
||||
|
||||
**Verified (P7 sweep, 21 levels, one pass).** Structural framediff (cell means / coverage / phash) linux-vs-win: rc=0 on 19/21 levels; rc=1 on Dam (frame 900, the flyby) and Streets (frame 1200). Exact `--tol 2` over-tol2 %: 1.142-6.618% on the 17 levels with no outlier stem (bunker2 1.142-1.144%, facility 1.396-1.842%, surface1 1.494-2.779%, bunker1 1.710-2.341%, train 1.573-2.160%, control 2.125-2.688%; highest non-outlier surface2 5.592-6.170%, jungle 5.858-6.618%). Outliers, each classified: **Archives** 19.956-24.554% at all three stems, but the 16x12 grid map is confined to grid rows 0-2 (cell means up to 50.8; row 3 is 2.5-4.6, rows 4-11 <= 3.1) and the per-pixel histogram is a constant ~18-22 delta (counts at d=18,19,20,21,22) -- systematic, a candidate real rendering difference (sky/fog band), needs a same-sim-state capture pair before it is called a renderer bug. **Cuba** 10.705-26.350% with 16x12 cell means <= 11.1 and per-pixel deltas clustered at d 3-6 across the whole frame -- the dither/quantization noise floor, structural-clean. **Jungle** 5.858-6.618% (worst cell dmean 14.8-26.7). **Streets** 1200: 9.564% with a diagonal band of 16x12 cell means 16.0-148.4 across rows 0-4, cols 0-6 (a region delta, not noise); Streets 900/1500 are clean (4.738/5.040%) and show a separate residue in the lower-right cells (row 9 col 10: 20.9 at 900, 33.0 at 1500). **Dam** 900: 92.863% over tol 2, phash 83 -- the flyby offset.
|
||||
|
||||
**Open (docs/ROADMAP.md §4).** The Archives top-band and the Streets-1200 band, as the two candidates for a genuine cross-platform rendering difference; both need a same-sim-state capture pair (identical scripted input and a settled camera on both boxes) before either can be called a renderer bug.
|
||||
## D522: The golden window's frame-900 stem is not settled gameplay on several levels — the win golden gate is not green (golden gate; D117 follow-up)
|
||||
|
||||
**FIXED 2026-10-05 — recipe-side re-base only (no game-code change, no tol limit raised).** The measurements below are kept as the record of why. `tools_pc/verify.sh`'s `golden_dump_for` now carries per-level windows: `900-1500:300` on the 14 levels where 900 is settled, `1000-1400:200` on Surface 2 / Streets / Depot / Cradle / Frigate, `1500-1700:100` on Dam. Chosen by measuring run-to-run agreement: at 900 those levels fail the per-pixel limit and Dam's 900 is still flyby; at the new stems three independent runs agree within tol 2 (frigate + the four: 1000/1200/1400 all 0.000% max-pair; Dam's 1200 agreed on two runs then diverged 5.761% on a third, so it moved to 1500-1700 — 1500/1700 0.000%, 1600 keeps ~40 moving pixels at 0.013%, inside the 1% limit). Full 21-level sweep after the re-base: **21/21 PASS** (`scratch/verify-rebase2.log`).
|
||||
|
||||
**Problem (2026-10-05 golden-gate pass, this tree).** A full 21-level sweep run with the recipe exactly as `tools_pc/golden/README.md` states it does not pass against the tracked `tools_pc/golden/<level>/win/` set. Honest sequential pass (one run at a time, `scratch/sweep-par.sh 1`, live `data/ge007.eep` present): **5/21 REGRESSION** — dam 6.542 % @900 (worst_cell 12.11), surface2 3.752 % @900 (limit 3.0), streets 1.001 % @1200, depot 1.267 % @900, cradle 1.754 % @900, all against a 1.0 % per-pixel limit; 16/21 PASS. The concurrent pass (`JOBS=7`, 21 levels in 1:56 wall) failed **7/21**: the same five plus runway 1.668 % @900 and frigate 4.484 % @900, both of which pass cleanly when run alone (worst_cell 0.616 / 0.109). Both numbers are recorded, not averaged; the difference is pacing (see below).
|
||||
|
||||
**Cause (D117 wall-clock pacing, not a render bug).** `GE_INPUTSCRIPT="20:START"` skips the intro flyby, but the flyby's own length is wall-clock paced: the scripted pad advances in real time, so which camera beat frame 900 lands on moves with the run's pacing. dam's 900 stem measured 1.598 / 2.689 / 3.424 / 6.542 / 7.240 % across five runs of the same recipe — the spread is the flyby beat, not noise. The unstable stem itself moves between passes: streets failed at 1500 in the concurrent pass and at 1200 in the sequential one. `tools_pc/golden/README.md`'s claim that "frames 900/1200/1500 are settled gameplay on every level" is contradicted.
|
||||
|
||||
**Also measured (D117 follow-up not closed).** `GE_DETERM=1` does not make the window reproducible: repeats differ 4.654 / 5.562 / 5.331 % with `GE_DETERM=1` against 0.115 / 0.219 % plain at the same stems — the switch makes the spread worse, not deterministic.
|
||||
|
||||
**Never do:** raise the tol limits to fit. That hides exactly the D117 class this gate exists to catch.
|
||||
|
||||
**Fix owed (recipe-side only; no game-code change):** either (a) move the capture window to stems settled on every level (1200–1800:300 is the measured candidate) and re-capture the 63 `win/` PNGs plus the box's linux set, or (b) keep the set and state the gate honestly (structural green + per-pixel marginal at 900) in `golden/README.md`, the ROADMAP row and the known-issues table. Driver: `scratch/sweep-par.sh` (gitignored; mirrors `verify.sh`'s ini pin, `GE_*` recipe, framediff flags and per-level limits, one cwd per level so runs can overlap — parallel verdicts are diagnosis only, the honest numbers come from the `JOBS=1` pass).
|
||||
|
||||
## D523: The golden recipe does not pin save state — `verify.sh` pins the ini but never touches `data/ge007.eep` (golden gate; D514/D522 follow-up) — **SUPERSEDED by D529 (2026-10-07): the frame depends on the save's CONTENT, not its presence; the gate now installs the canonical in-tree save**
|
||||
|
||||
**FIXED 2026-10-05 — `verify.sh` now pins the save file** (back up + require `data/ge007.eep`, mirroring the ini pin; it exits 2 with a message when a clone has none, rather than silently measuring a different scene), and `tools_pc/golden/README.md` states the pin. The measurements below are the record of why the gap mattered.
|
||||
|
||||
**Problem.** The goldens were captured with a save file present, and nothing in the recipe records that. `tools_pc/verify.sh` backs up and pins `data/ge007.ini` (`pin_ini_640x480` / `restore_ini` / the EXIT trap) but has no analogue for `data/ge007.eep`, and `tools_pc/golden/README.md`'s recipe list never mentions it. The game's save file changes the rendered scene, so the recipe reproduces the goldens only on a machine that happens to hold a matching save — and a fresh clone of this repo ships no `.eep` at all.
|
||||
|
||||
**Measured on this tree (2026-10-05, two honest sequential 21-level passes, same build, same ini pin).** With the live `data/ge007.eep` present: 5/21 REGRESSION. With no `.eep` at all: **19/21 REGRESSION** — Archives 21.557 % @900 (worst_cell 31.64), Silo 40.9 and Dam 116.6 worst_cell (framediff-level misses, not even per-pixel-comparable), and every remaining level 1.2–5.5 % against a 1.0 % limit. Only Bunker 2 (0.001) and Cuba (structural tier only) survive. So the save state, not the renderer, is what separates a passing gate run from a failing one.
|
||||
|
||||
**Box-side corroboration (P7, D521).** Archives cell means 0.0 → 27–31 from `.eep` presence alone, and dam's 900 phash 3 → 83: same cause, opposite direction, because the box's save differs from the win capture's. The box changed no golden and no `verify.sh` for it — correct, since the fix belongs to the recipe, not to either artifact.
|
||||
|
||||
**Fix owed (port tooling + docs only).** Pin the save file the way the ini is pinned: back up `data/ge007.eep`, install the capture-time save (or explicitly none) for golden runs, restore on exit, and state in `golden/README.md` which save state the set carries. `data/` already holds candidate capture-time saves (`ge007 - Copy.eep`, `ge007 copy.eep`, `d295_repo_eep_prior.bak`), all with different sha256 from the live one; which one the set was captured with is not recorded and has to be identified before pinning.
|
||||
|
||||
**Measured refinement (2026-10-05, same build, Archives as the discriminating level).** The variable that matters is **save-file presence, not which save**: Archives measures 0.175625 worst_cell with the live `ge007.eep`, with `ge007 - Copy.eep`, with `ge007 copy.eep`, and with `d295_repo_eep_prior.bak` (four different sha256s, all identical verdicts), and 21.557 % / worst_cell 31.64 with no `.eep` at all. So pinning "a save present" reproduces the set on Archives from any of these; pinning nothing does not. Dam cannot discriminate — its 900 stem spread (D522) dominates the save delta (measured 3.03 / 12.11 / 17.37 / 116.6 / 132.96 worst_cell across runs of the same recipe). Until that is done, no sweep verdict from a fresh clone is comparable to the committed goldens.
|
||||
|
||||
## D524: The golden tooling was destructive and drift-prone — `verify.sh`'s per-level ini/save pin snapshotted the previous level's game-written file, and `capture_p7.sh` hardcoded the pre-D522 windows while deleting the save it captures with (golden tooling; D522/D523 follow-up)
|
||||
|
||||
**FIXED 2026-10-05 — tooling only (no game-code change, no golden touched by this finding).** Two sites, both found in the U5 code review of `tools_pc/**` (paths / quoting / subprocess / destructive behaviour) that the v0.4.1 release audit owed.
|
||||
|
||||
**(1) `verify.sh` sweep overwrote the maintainer's own settings.** `pin_ini_640x480` and `pin_eep` are called once per level inside the sweep loop, and the game rewrites `data/ge007.ini` (and writes `data/ge007.eep`) after every run. So from level 2 onward the "backup" is the *previous level's game-written* ini, and the EXIT trap restores that: after one 21-level sweep the user's personal settings (`FovScale`, `DrawDistance`, MSAA, sound…) are replaced on disk by compiled-in defaults, with no way back, and the save restore hands back the second-to-last level's written save rather than the pre-sweep one. It also leaked one `mktemp -d` directory per level. Fix: both pins are idempotent — the FIRST call snapshots the user's ini/save, later calls only re-pin — and `pin_eep` re-installs that snapshot before every level, so each run starts from the same save state (D523 says presence, not which save, is what the frame depends on; the runs write the save, so re-installing is what makes the sweep reproducible).
|
||||
|
||||
**(2) `tools_pc/capture_p7.sh` carried the pre-D522 recipe and cleared the save.** It hardcoded `GE_PCDUMP="900-1500:300"` / `GE_QUITFRAME=1502` (stale on the six levels D522 re-based) and ran `rm -f data/ge007.eep` before every run — the D523 gap, and the reason the 2026-10-04 linux set had to be re-captured at all. It now reads the windows, scripts and seed out of `tools_pc/verify.sh` (`golden_dump_for` / `golden_script_for` / `GOLDEN_SEED`) so the capture tool and the gate cannot drift, computes `GE_QUITFRAME = last requested frame + 2`, and refuses to run without `data/ge007.eep` present.
|
||||
|
||||
**Never do:** keep the per-level backup semantics. A sweep that leaves the user's ini rewritten is a silent data-loss bug in a tool the maintainer runs against their own `data/` directory; "restore what the last run left" is never the intent.
|
||||
|
||||
## D525: The whole `<level>/linux/` golden set was stale, not only on the six re-based levels — re-rounding it closes D521's two residual render bands (golden gate; D521/D522/D523 follow-up)
|
||||
|
||||
**FIXED 2026-10-05 — recipe/asset side only (box re-round + merge; no game-code change, no tol limit raised).** The 13-frame order owed to the box was the wrong size. D523's evidence says save-file *presence* moves ~every level (Archives 21.557 % with no `.eep`, 0.175625 worst_cell with any of four saves), and P7's 2026-10-04 round ran with `data/ge007.eep` cleared while the win set carries a save present — so the entire 21-level linux set was stale, not just the six levels D522 moved. The box re-captured all 21 levels (63 frames, 21/21 rc=0, frames=3, 25-29 s each, ~10 min wall) at the windows `verify.sh` now carries, with the save pinned present, on the same real-GL box (Mesa 23.0.3 / Intel HD 3000, X on vt2, `DISPLAY=:0`, 640x480 ini pin, `GE_RSEED` pinned, per-level `GE_INPUTSCRIPT`). The base branch already carried 50 linux PNGs (the five re-based levels kept only their 1200 stem, Dam had none); after the re-round the set is 63, one frame per recipe stem on every level.
|
||||
|
||||
**Cross-platform measurement after the re-round (framediff linux-vs-win at the recipe stems, tol 2).** Structural tier: **21/21 PASS** — worst 16x12 cell mean 4.43 (streets), phash <= 6 on 20 levels, the one structural signal at the limit being **cradle phash 40** (threshold 40; cell means 1.17, so it is a perceptual-hash tie, not a region delta). Exact tier, % of pixels over tol 2: **0.348-3.983 %** on the 20 non-Cuba levels (facility 0.348-0.477, surface2 0.408-0.488, surface1 0.835-0.931, train 0.782-0.825, bunker1 0.951-1.063, statue 0.605-1.105, bunker2 1.142-1.143, depot 1.098-1.155, control 1.298-1.332, runway 1.340-1.396, caverns 1.462-1.473, silo 2.352-2.363, egypt 2.874-2.951, dam 2.964-3.443, frigate 1.282-3.181, cradle 3.063-3.337, jungle 3.127-3.851, aztec 3.704-3.830, archives 3.590-3.859, streets 3.616-3.983); Cuba 23.657-36.338 % is the recipe's structural-tier-only level (cutscene animation timing, `golden_tolpct_for cuba` = empty). The same spread measured 1.142-6.958 % on the stale set, so the re-round roughly halves the worst cross-platform deviation.
|
||||
|
||||
**D521's two open items are closed by this measurement, and neither is a renderer bug.** **Archives** measured 19.956-24.554 % with cell means up to 50.8 confined to grid rows 0-2 on the stale pair; it now measures 3.590-3.859 % with worst cell mean 2.31 and phash 1 — the band was the save-state difference D523 names, not a sky/fog rendering difference. **Streets** frame 1200 measured 9.564 % with a diagonal band of cell means 16.0-148.4; it now measures 3.616-3.983 % with worst cell mean 4.43 and phash 0. **Dam** frame 900's 92.863 % / phash 83 is the flyby offset (D521/D522) and is not in the set any more: Dam sits at 1500-1700, where the cross-platform delta is 2.964-3.443 %.
|
||||
|
||||
**Box-side caveat (this is why the box's own sweep is not a pixel gate).** `verify.sh`'s D521 renderer probe fails closed on this box: `glxinfo -B` exits 255 with "Error: unable to open display :0" from a non-login ssh shell even though the game's SDL GL context and `glReadPixels` readback work (the captured PNGs are 128-355 KB, not black). `LINUX_SOFTWARE_GL` therefore stays 1 and the box's sweep reports "pixcount/framediff skipped, crash-detect only". The cross-platform numbers above are computed on Windows against the merged linux PNGs, which is the only honest place to compute them from this box. **Never claim the linux pixel gate ran on this box** — the claim that holds is that the linux captures are structurally clean against the win set and that the linux *crash* gate ran on the box.
|
||||
|
||||
**Never do:** call the cross-platform spread a parity gate. The per-pixel limit (1 % / 3 % on jungle+surface2) is calibrated per platform against that platform's own run-to-run noise; 0.348-3.983 % across platforms is informational, and the gate each platform ships is its own captures against its own goldens.
|
||||
|
||||
## D526: P13 Dam-ending grate / edge-railing transparency is faithful N64 behaviour (port == 1964/GEPD); the per-triangle census probe is retained as a generic draw-state diagnostic
|
||||
|
||||
**CLOSED as faithful 2026-10-07 (no port bug; no fix).** The P13 "Dam-ending grate show-through" (2nd POSENDS camera: the area under the grate Bond stands on shows through — the 2D edge-railing/grate texture's between-anchors region) was triaged as a suspected B1 port bug (the grate quad's texture stage taking an alpha/blend path it shouldn't — D195/D268 class). To bucket it, a per-triangle draw-state census probe was built (this D526) and smoke-tested. The 1964/GEPD reference of the same 2nd-POSENDS shot then showed **the port and the N64 look the same** — the show-through is in *both*. So it is the original game's 2D grate/railing texture rendering, reproduced faithfully: a known issue that **closes in the D306/D291 class** (a "bug" that 1964 shows is intended behaviour). There is nothing to fix in `port/fast3d/`.
|
||||
|
||||
**Probe (retained as a generic tool):** `d526_note_emit()` in `port/fast3d/gfx_pc.cpp`, folded into the D474 `tri_dbg()` gate (zero player cost when off). Env: `GE_D526="lo-hi"` (sim-frame window), `GE_D526BOX="x0 y0 x1 y1"` (NDC region, y-up; default whole screen), `GE_D526MAX=N` (cumulative per-triangle line cap, 400; the per-frame `D526F:` signature aggregate is uncapped). Emits to stderr: per-triangle NDC bbox, z/w, `gm`, `oml`, `omh`, combine mode, first-tile tmem/fmt/siz, prim + vertex alpha, use_alpha/modulate/invisible/2-cycle, plus a per-frame `D526F:` signature map. It is the only per-triangle *draw-state* census (D474/D75D/ZF/D303 are specialized) and is the right tool for the alpha/texture bug class (D195/D266/D268/D273).
|
||||
|
||||
**Static audit (no code change):** `palette_to_rgba32` (`gfx_pc.cpp:1044`) matches the game's ground truth — `texChannelsToPixels` (`src/game/image.c:1836`) packs RGBA5551 as `R<<11|G<<6|B<<1|A` (alpha = bit 0) and IA16 as intensity-hi/alpha-lo (the D228-fixed decode), so a global palette bit-flip is ruled out; blend state is stock and D268-verified. Consistent with the faithful result (the port's alpha/texture path is correct).
|
||||
|
||||
**Never do:** ship a `port/fast3d/` "fix" for the Dam-ending grate show-through — 1964/GEPD confirms it is faithful N64 behaviour (D306/D291 class). Do not drop the census probe without a finding that uses it; it is a maintained diagnostic.
|
||||
|
||||
## D527: `GE_PCDUMP` written `first:last` (a colon where the dash belongs) silently dumps EVERY frame (harness trap)
|
||||
|
||||
**OPEN (harness documentation; no code change yet).** `port/src/video.c:1357` parses `GE_PCDUMP` with `sscanf("%d-%d:%d")`. If the lo/hi pair is written `first:last` (a colon where the dash belongs), the `%d-%d` part fails to match, the fallback sets `hi=0x7fffffff` and `step=1`, and the dump writes **every** frame from `first` onward at ~921 KB each — a first launch in the 2026-10-05 session ate ~1.5 GB before it was noticed (the run went to ~1500 fps until the window was closed). The failure mode is silent: no error line, the dump just runs.
|
||||
|
||||
Fix owed (port-layer only): log a NOTE when the `sscanf` does not consume all three fields (e.g. "GE_PCDUMP 'x:y' is not 'first-last:step' — dumping every frame"), or fail closed (ignore the value) — either way the operator must see which window is active.
|
||||
|
||||
**Never do:** write `GE_PCDUMP` as `lo:hi` — use `first-last:step` (a dash between lo and hi, a colon before the step).
|
||||
|
||||
## D528: `GE_STARTMENU` skips the EEPROM read, so front-end-only sessions never import the emulator save (harness trap)
|
||||
|
||||
**OPEN (harness documentation).** A `GE_STARTMENU` session (front end up with no level load) never reaches the EEPROM read — no `eeprom:` log line; the front end reports `watchsettings: no valid Bond save in any folder` even with `data/ge007.eep` present. Consequence: the D514 imported-save test **cannot** be driven from a `GE_STARTMENU` boot; it must run from a **normal boot**. There it reproduces exactly: `eep: imported emulator save (6 regions converted, backup data\\ge007.eep.emulator.bak)` and `data/ge007.eep` md5 `3ca7cf84…` = the in-game-proven converted save (2026-10-05 session).
|
||||
|
||||
Decision owed: document it (a NOTE log line when `GE_STARTMENU` is active: "EEPROM import skipped") or make the front end perform the import — port-layer only; the N64 front end has no EEPROM at all, so this is pure port scaffolding.
|
||||
|
||||
**Never do:** drive the D514 imported-save test from a `GE_STARTMENU` boot — it must run from a normal boot, or the EEPROM import is skipped and the test cannot see the converted save.
|
||||
|
||||
---
|
||||
|
||||
## D529: The golden gate depends on the save file's CONTENT — the D523 "presence, not which save" claim is refuted; the gate must pin a canonical save (golden gate; refines D523; follow-up to D525/D524)
|
||||
|
||||
**2026-10-07 (pre-push session).** The D523 pin installed *whichever* local save existed ("presence, not which save — Archives measures worst_cell 0.175625 on each of the four candidate saves and 21.557% with none"). That claim had only ever been measured **on Archives**. The 2026-10-07 sweep refutes it: the local `data/ge007.eep` had been rewritten by the live Dam-ending playtest sessions (md5 `a41d7ab…`, mtime 2026-10-05 07:27), and the honest sequential sweep under it produced **6/21 REGRESSION** on a *byte-identical binary* (the 2026-10-04 run had been 21/21).
|
||||
|
||||
**The A/B (facility, window 900-1500:300):**
|
||||
|
||||
| save (2 KB each) | facility worst_cell |
|
||||
|---|---|
|
||||
| `ge007 copy.eep` (2026-09-02, the pre-live-sessions save) | **0.0–0.25 — PASS** |
|
||||
| `ge007 - Copy.eep` (2026-09-16) | 0.0–0.25 — PASS |
|
||||
| `build-pc/ge007.eep` (2026-09-28) | 0.25 (f2) / 0.25 (f3) — PASS |
|
||||
| `../007/data/ge007.eep` (2026-09-15) | 0.25–0.5 — PASS |
|
||||
| `gamefaqs_dexdrive_2001.eep` (external) | **177.47 (f2) / 177.22 (f3) — REGRESSION** |
|
||||
| `data/ge007.eep` local (a41d7ab, the 2026-10-05 sessions) | **177.47 (f2) / 177.22 (f3) — REGRESSION** |
|
||||
|
||||
Two distinct classes in the 6-failure sweep, proven apart by the A/B:
|
||||
1. **Save-content dependent (facility, streets, jungle, cradle):** same failure signature (177.2–177.5 at the 1200/1500 stems — a *different scene* in-window, not a jitter class) only on the two saves with that content (the 2001 dexdrive save and the 10-05 local save share it; 2001-vintage saves apparently hold state that routes facility's intro differently). The other four candidates pass. **This is what D529 fixes: the gate installs ONE canonical save, so "which save" can never vary.**
|
||||
2. **D117 wall-clock flyby (dam@1500 4.9%, frigate@1000 3.2–8.3%, streets@1000 2.5–25.7%):** fails *identically on every save* — the window's first stem is inside the real-time-paced intro flyby, so the content at a given sim frame varies with machine load (D117, long-known, previously masked because the Oct-4 goldens happened to be captured the same way).
|
||||
|
||||
**Resolution (FIXED 2026-10-07, same session):**
|
||||
- **Canonical save in-tree: `tools_pc/golden/ge007.eep`** (2 KB, = `ge007 copy.eep`, the stable pre-live-sessions save the Oct-4 gate was green on). In-tree = a fresh clone or CI can run the gate with zero external state, and the D523 "fresh clone has no save → exit 2" failure branch is gone for good.
|
||||
- **`verify.sh` installs it like the ini pin** (snapshot the local `data/ge007.eep` if any → install the canonical → restore-or-remove on exit). The local playtest save is never the gate's input.
|
||||
- **All 21 win golden sets re-captured under the canonical save** (`scratch/golden-recapture-win-v050.sh`; 21/21 rc=0). The first four flaky levels (dam/frigate/streets/jungle — the D117 flyby stems) were re-captured again **clean** (no concurrent processes) after the first interleaved capture.
|
||||
- **Green protocol held (final, clean):** the first "double-green" pair ran `verify.sh` **with no arguments — which at the time only printed the usage and exited 0** (a no-op, not a sweep — that pair does not count) and was also interleaved with the in-flight re-capture. The valid record is **three consecutive clean full sweeps (`verify.sh sweep`) all 21/21 PASS** after the clean 4-level re-capture (`scratch/verify-v050-flaky3-run{1,2,3}.log`). **`verify.sh` with no arguments must never be cited as a green run.** CODE-PATCHED the same day (independent-reviewer follow-up): the no-arg path now prints the usage and **exits 1** with an explicit "never cite a no-arg run as a gate result" error — the failure class is closed in code, not just doc.
|
||||
- **Linux side:** the box set was re-rounded 2026-10-05 under a *cleared* eep (D525) — a cleared eep is a *specific content state* (empty save), not the canonical one. The box re-round under `tools_pc/golden/ge007.eep` is owed in parallel on the release branch (off the critical path; the box pixel probe fails closed there anyway — D244 — so it is a second-surface check, not a gate).
|
||||
|
||||
**Why this is not a renderer drift:** the binary is byte-identical to the 2026-10-04 21/21 run; the only variable that changed is `data/ge007.eep`, and the A/B is save-keyed (same binary, four passing saves, one failing save). No renderer code changed.
|
||||
|
||||
**Never do:** (1) run the golden gate against a local playtest save — it is exactly this class of drift; always the in-tree canonical eep. (2) claim "the frame depends on save *presence*" again — it depends on *content*; the D523 claim survives only as the (superseded) measurement on Archives. (3) treat the linux 63-frame set as valid under the canonical eep before the box re-round lands — it is the D525 cleared-eep round, and **no linux number may be cited (e.g. in the README pass) until that re-round lands**. (4) cite `verify.sh` with **no arguments** as a green run — a sweep is `verify.sh sweep` (the no-arg path now exits 1, closing the trap in code). (5) run a re-capture concurrently with a sweep (contention moves the D117 flyby stems). (6) treat **same-session** sweeps as proof the four D117 flaky levels (dam/frigate/streets/jungle) are stable across wall-clock: the first-stem content is time-dependent and the pin was fixed to "now" — the cross-machine test is the box re-round; **if those four go red there, the fix is to exclude or widen the first-stem cell for those levels, NOT to re-capture again** (an independent reviewer's 2026-10-07 verification flagged this; same-session worst_cell swings were streets 8.38→4.41, jungle 1.42→3.87). (7) trust a `capture_p7.sh` round's provenance claim without checking WHICH save state the loop actually installed — the D524-era per-level re-pin restored the local save and silently overwrote D529's pre-loop canonical install (D531); the linux set is only "canonical" if the loop line says `cp "$CANON_EEP"`.
|
||||
|
||||
## D531: `capture_p7.sh`'s D529 adaptation was incomplete — the per-level re-pin restored the local save, so the 2026-10-05 box re-round ran under the box-local save (not the canonical one); fixed + re-captured (golden gate; D529/D530 follow-up; P12 U5 review finding)
|
||||
|
||||
**Blocker found in the 2026-10-07 P12 U5 review (local session).** `tools_pc/capture_p7.sh`'s per-level loop had `cp "$EEP_ORIG" "$EEP"` (introduced by D524 to make the tool non-destructive): it restored the pre-capture **local** save — or a 0-byte eep when `data/ge007.eep` did not exist — before every level. D529's adaptation (`8fbf8aa0`) installed the canonical save (`cp "$CANON_EEP" "$EEP"`) **once, before the loop** and added the canonical-missing guard, but left the D524-era re-pin line in place — so from level 1 the canonical install was silently overwritten and every capture ran under the local save state. The header comment ("the pinned save, restored before every level") asserted what the code did not do.
|
||||
|
||||
**Impact — D530's provenance claim is wrong:** the 2026-10-05 box re-round (`~/p7-reround.log`, "P7 re-round DONE") ran under the box-local save (`sha256 d9f306…`, 2 KB — distinct from the in-tree canonical `65247b…`), not the canonical eep. D530's / the golden README's / the commit message's "63 PNGs under the canonical eep" did not hold, and D530's first-stem table (measured in that re-round's report against the old D525 goldens — its numbers do check out against the report) is not canonical-save evidence. **The win set is unaffected** — `verify.sh`'s `pin_eep` re-installs the canonical save on every call (that code was right); the bug was capture-side only.
|
||||
|
||||
**Fix (this batch, tooling only):** the loop now runs `cp "$CANON_EEP" "$EEP"` per level (`EEP_ORIG` is kept for the exit-restore only), and the script now `cd "$ROOT"` — the loop's dump/golden paths are root-relative (`ppm/`, `tools_pc/golden/`), contradicting the header's "can be invoked from anywhere" claim.
|
||||
|
||||
**Re-capture (this batch):** a fresh 21-level box re-round under the canonical eep (21/21 rc=0, 63 frames), committed into `tools_pc/golden/<level>/linux/` replacing the D530 box-local-save set: **47/63 frames differ** from it (save content demonstrably moves the frames — D529's drift class), 16 byte-identical (save-independent scenes). **Note on the box's pixel capability:** the 10-05 re-round's p7_report diff table (1–26% over on real content) proves its WSLg GPU display was active then; today the box reads back **black even with `DISPLAY=:0`** (`glxinfo -B` under `:0` reports no usable renderer — the WSLg GPU display is session-state-dependent, and today's session is on the software fallback), so today's `verify.sh` runs (D530's crash-detect sweep and this batch's `~/p7-verify-pixel.log`) are legitimately crash-detect-only. The pixel-gated cross-machine verify is owed for a window in which the box's GPU display is up again.
|
||||
|
||||
**Pixel-gated cross-machine gate (the real D117 datum):** `verify.sh sweep` on the box against the new canonical goldens — **2026-10-07 attempt (log `~/p7-verify-pixel.log`): 21/21 PASS, 0 FAIL, crash-detect only** (the run's renderer probe correctly failed closed on today's software-fallback session, so it adds stability, not, pixel data); the pixel verdict — including the four flaky levels' first stems — is owed for a window in which the box's WSLg GPU display is active. If the four flaky levels (dam/frigate/streets/jungle) go red there, the never-do 6 decision stands: exclude or widen the first-stem cell, do NOT re-capture.
|
||||
|
||||
**U5 review notes from the same pass (non-blockers):** the three should-fixes below were applied 2026-10-07 as D532 (gate hardening: fail-closed ini pin, hard-pinned seed, dead A/B switches removed).
|
||||
|
||||
## D532: the three P12 U5 should-fixes applied — the golden gate's ini pin now fails closed, the seed is hard-pinned (a stray `GE_RSEED` warns instead of re-seeding), and `audiodebug.ps1`'s dead A/B switches are gone (golden gate + tooling; D531/U5 follow-up)
|
||||
|
||||
**2026-10-07 (local session).** Three tooling-only fixes, no game logic:
|
||||
|
||||
1. **`verify.sh` `pin_ini_640x480` failed OPEN** — the three call sites (`single`/`sweep`/`parity`) were `pin_ini_640x480 2>/dev/null || true`, so a failed ini write (missing `data/`, read-only tree, failed snapshot) ran the gate under an **unpinned ini** — the frame depends on a defaults-only 640x480 ini exactly like the save pin (D529), so that was a spurious-verdict source, not a skipped nicety. The pin now fails closed (snapshot and write both `error + exit 2`, matching `pin_eep`'s style) and the call sites are plain.
|
||||
2. **`GOLDEN_SEED` hard-pinned** — it was `"${GE_RSEED:-0x0123456789abcdef}"`, so a stray `GE_RSEED` in the environment silently re-seeded the documented-as-pinned gate (frames shift → spurious verdicts). It is now the literal, with a warning when a differing env var is present (verified: `GE_RSEED=0xdead` warns, the pinned value is silent). `capture_p7.sh`'s `eval grep -m1 '^GOLDEN_SEED='` still picks up the same line, so capture and gate stay consistent.
|
||||
3. **`audiodebug.ps1` dead switches removed** — `-AB`/`-Old` were documented no-ops (`GE_D204_OLD` no longer exists in the game); the switches, the A/B block, and the `-OldMode` plumbing are gone, with a one-line removal note (D532).
|
||||
|
||||
PS parse: 0 errors; `bash -n` clean; the warning path verified. **Owed (unchanged):** the pixel-gated box verify in a WSLg GPU-display window (D531).
|
||||
|
||||
## D530: The box (X220) linux golden re-round landed — first cross-machine test of the four D117 flaky levels (golden gate; D529 never-do 3/6 follow-up) — **AMENDED BY D531 (2026-10-07): the 10-05 re-round ran under the box-LOCAL save, not the canonical one (the capture_p7.sh bug); the true canonical re-capture is D531**
|
||||
|
||||
**2026-10-07 (local session).** The box re-round D529 owed (never-do 3: the linux set was the D525 *cleared*-eep round and no linux number could be cited until it was re-rounded under `tools_pc/golden/ge007.eep`) ran on X220 on 2026-10-05 (tree `442eb2ff`, worktree `~/gh-fullhist-wt-p7`, log `~/p7-reround.log` hit `P7 re-round DONE` 12:53 EDT). **21/21 levels rc=0** (the box self-gate is crash-detect only — the pixel probe fails closed on the box, D244). This session pulled the 63 PNGs (21 levels × 3 frames) and committed them into `tools_pc/golden/<level>/linux/`, replacing the D525 set: 51/63 differ from the D525 round; the 12/63 that are byte-identical are save-independent scenes — including **frigate's first stem (001000)**, a direct D117 stability signal. **AMENDED BY D531: that re-round ran under the box-local save (the `capture_p7.sh` bug), so the "canonical eep" provenance claim — and the first-stem table below, as canonical-save evidence — does not hold; the D531 re-capture supersedes this set.**
|
||||
|
||||
The `fail` rows in the box report are the expected cross-eep diffs against the *old* in-tree set, not regressions: structural tier is all `ok` (phash ≤ 3, cradle@40 the documented exception; Cuba 26.4% inside its documented 23.7–36.3% structural-only range).
|
||||
|
||||
**D117 flaky-four first-stem check (D529 never-do 6 — the first cross-machine test):**
|
||||
|
||||
| level (first stem) | worst_cell | phash |
|
||||
|---|---|---|
|
||||
| dam@1500 | 1.70375 | 0 |
|
||||
| frigate@1000 | — (byte-identical to the D525 round) | 0 |
|
||||
| streets@1000 | 1.775625 | 0 |
|
||||
| jungle@900 | 5.115625 | 3 |
|
||||
|
||||
All four sit in the stable-levels' range (e.g. silo 0.41–0.84% over, worst_cell 0.41–0.42 is the quietest; archives 1.51–2.31 is noisier). **None went red → the never-do 6 decision closes with NO recipe change** (no first-stem cell excluded or widened on the linux side).
|
||||
|
||||
**Unblocked (D529 never-do 3):** the README/index v0.5.0 line "re-based on both platforms (21 levels, 63 frames each)" now stands on a canonical-eep linux set (D531's re-capture; the D530 set it first pointed at was the box-local-save round — see D531). **Fresh box sweep (crash-detect):** with the new goldens staged on the box, `verify.sh sweep` (2026-10-07) is **21/21 PASS** — but that run was crash-detect only (GE_PCDUMP reads black on the box's llvmpipe/WSLg, the D244 note), so it is a stability re-check, not a pixel datum. **AMENDED BY D531:** the "black frames" were **not** simply the no-DISPLAY llvmpipe path — the box's WSLg display is session-state-dependent: its 10-05 session was GPU-backed (the re-round's p7_report pixel table proves real frames), while today's reads back black even with `DISPLAY=:0` (software fallback; `glxinfo -B` reports no usable renderer). D531's pixel-gated attempt (21/21 crash-detect, `~/p7-verify-pixel.log`) is valid as a stability re-check; the pixel datum is owed for a GPU-display window.
|
||||
@@ -31,7 +31,7 @@ passes together fit in ~55 s.
|
||||
|
||||
## Source material
|
||||
|
||||
`C:\Users\james\Videos\NVIDIA\Build-pc\new\` — ShadowPlay recordings
|
||||
`<videos>\NVIDIA\Build-pc\new\` — ShadowPlay recordings
|
||||
(3072x1728 @ 60 fps, h264), no usable audio track (cut with `-an`). The four
|
||||
sessions and their tags are hardcoded in `SESSIONS` at the top of the script;
|
||||
add a new session there (tag, filename suffix, duration) to use it.
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
---
|
||||
title: Framerate and pacing
|
||||
description: How the GoldenEye 007 PC port holds a steady 60 fps and keeps per-frame game timing correct on a fast CPU.
|
||||
---
|
||||
|
||||
## Framerate and pacing
|
||||
|
||||
The port targets a **steady 60 fps** with VSync and an optional 30/60 frame
|
||||
cap, including on low-end GPUs.
|
||||
|
||||
The N64's game code assumed a **60 Hz vertical-interrupt tick** — it ran a
|
||||
few ticks per displayed frame. On a PC the CPU is far faster, so left alone
|
||||
the game logic would race ahead. The port handles this in two places:
|
||||
|
||||
- **Frame pacing** happens in `port/src/video.c` on the render thread (the
|
||||
software RSP thread), which paces the frame and does the swap and FPS
|
||||
accounting.
|
||||
- **Per-frame counters** in the game are gated with `portN64FrameStep()` so
|
||||
they advance at the *display* rate, not the CPU rate. Getting this wrong is
|
||||
a recurring class of subtle timing bug (a counter that ticks too fast, a
|
||||
stall that isn't real) — the [porting notes](porting-notes.md) track these
|
||||
(for example D13, D22 and D486).
|
||||
|
||||
On very weak integrated GPUs (Atom/Celeron class) the first launch writes
|
||||
lighter values for draw distance, LOD distance and anti-aliasing into
|
||||
`ge007.ini` so the game still holds 60 fps.
|
||||
|
||||
*More: [porting notes](porting-notes.md) — the timing-related findings (D13,
|
||||
D22, D486).*
|
||||
@@ -1,12 +1,9 @@
|
||||
---
|
||||
title: GoldenEye 007 PC Port
|
||||
# v0.4.0: status bumped from v0.3.0; Honest status replaced by the v0.4.0
|
||||
# release-notes known issues (rainbow/water/z-fight/cameras/trees/muzzle/
|
||||
# stretched-widescreen/gunshot items all fixed; D335b F10-overlay stretch is
|
||||
# the new widescreen residual). Description kept short (Bing's 160 limit).
|
||||
# v0.5.0: status bumped from v0.4.0. Description kept short (Bing's 160 limit).
|
||||
description: >-
|
||||
A native PC port of GoldenEye 007 (Nintendo 64, 1997), built from
|
||||
decompiled source with a software RSP. v0.4.0 for Windows, Linux and
|
||||
decompiled source with a software RSP. v0.5.0 for Windows, Linux and
|
||||
Steam Deck.
|
||||
---
|
||||
|
||||
@@ -19,39 +16,41 @@ in a dedicated `port/` layer, following the architecture of the
|
||||
[Perfect Dark PC port](https://github.com/fgsfdsfgs/perfect_dark), the same
|
||||
Rare "Indy" engine family, one hardware generation apart.
|
||||
|
||||
[Download](#download) · [News](#news) · [See it running](#see-it-running) · [Honest status](#honest-status) · [Documentation](#documentation)
|
||||
[Download](#download) · [News](#news) · [See it running](#see-it-running) · [Known issues](#known-issues) · [Documentation](#documentation)
|
||||
|
||||
**Status: v0.4.0 (2026-09-28) — fully playable, with a small set of known
|
||||
caveats.** The full single-player campaign runs at a steady 60 fps and is
|
||||
completable end to end (all 20 missions, plus the ending-credits sequence,
|
||||
playtested on Windows, Linux and Steam Deck). It is the most complete release
|
||||
to date: **native widescreen** (on by default — undistorted world at your
|
||||
display's aspect, 4:3 menus pillarboxed), a **complete aim system** (N64 or
|
||||
centred PC style, for mouse *and* controller), **in-game key rebinding** with
|
||||
a GEPD-style default layout, **crosshair customization**, **rumble-pak
|
||||
haptics** on supported gamepads, and a **rebuilt options overlay** with
|
||||
fine-tuning rows (frame cap, MSAA, draw/LOD distance, HUD scale). The most
|
||||
common defect classes from earlier releases — particle colour drift, water
|
||||
seams, z-fighting, billboard trees, muzzle flashes, the front-end Nintendo
|
||||
logo, gunshot SFX — are fixed in this version; what remains is a short list,
|
||||
under [Honest status](#honest-status).
|
||||
**Status: v0.5.0 (2026-10-05) — fully playable, with a small set of known
|
||||
caveats.** The single-player campaign and the 2–4 player split-screen
|
||||
multiplayer both run at a steady 60 fps. The single-player campaign is
|
||||
completable end to end (all 20 missions plus the ending-credits sequence),
|
||||
and the 2–4 player split-screen campaign is supported on every multiplayer
|
||||
map; both were playtested on Windows, Linux and Steam Deck. This release
|
||||
adds **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**, **direct loading of
|
||||
emulator save files**, a **scalable HUD overlay**, fullscreen and
|
||||
window-centring controls, and a **fidelity round checked frame-by-frame
|
||||
against the N64 game** (fog, aspect-ratio letterboxing, the Watch menu's own
|
||||
settings). What remains is a short list, under
|
||||
[Known issues](#known-issues).
|
||||
|
||||
**This is a pre-1.0 release, not a finished product** — v1.0 is the target
|
||||
for a polished, feature-complete build; expect missing features and the
|
||||
occasional breaking change until then. See the
|
||||
[README's Roadmap section](https://github.com/jkdansereau/goldeneye-pc-port#roadmap)
|
||||
for direction (PAL/JP support, controller-button rebinding, LAN multiplayer
|
||||
under consideration, and more).
|
||||
|
||||
<p align="center">
|
||||
<img src="media/goldeneye-gh-preview.gif" width="70%"
|
||||
alt="~24 s gameplay montage from live play sessions">
|
||||
<br><em>~24 s gameplay montage from live v0.4.0 play sessions (Runway tank,
|
||||
Dam, Caverns, Aztec, Bunker 2, Surface 2), running in the port.</em>
|
||||
</p>
|
||||
for direction (PAL/JP ROM support, macOS and ARM builds, the remaining small
|
||||
accuracy differences, and post-1.0 opt-in extras).
|
||||
|
||||
## News
|
||||
|
||||
- **2026-10-05** — **v0.5.0**: one options menu with plain-English wording,
|
||||
real-unit sliders and a one-line description per option; the N64 control
|
||||
styles as a per-seat preset; direct emulator-save loading; a scalable HUD
|
||||
overlay; fullscreen and window-centring controls; and a fidelity round
|
||||
checked frame-by-frame against the N64 game. 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) ·
|
||||
[downloads](#download).
|
||||
- **2026-09-28** — **v0.4.0**: native widescreen, a complete aim system for
|
||||
mouse and controller, in-game key rebinding, crosshair customization,
|
||||
rumble-pak haptics, a rebuilt options overlay, and a broad fidelity-fix
|
||||
@@ -85,10 +84,19 @@ without a ROM you already own.
|
||||
|
||||
## See it running
|
||||
|
||||
<p align="center">
|
||||
<img src="img/shots/shot-28.jpg" width="45%" alt="Streets, rendered by the port (v0.4.0 intro attract)">
|
||||
<img src="img/shots/shot-01.jpg" width="45%" alt="Dam, rendered by the port (v0.4.0 intro attract)">
|
||||
</p>
|
||||
<div class="viewer" id="viewer">
|
||||
<div class="vbar">
|
||||
<span class="dots"><i></i><i></i><i></i></span>
|
||||
<span class="lbl">viewer — in-engine captures, 3072×1728</span>
|
||||
<span class="idx" id="vIdx">1 / 12</span>
|
||||
</div>
|
||||
<div class="vstage">
|
||||
<img id="vImg" src="img/shots/shot-01.jpg" alt="in-engine capture">
|
||||
<button class="vbtn prev" id="vPrev" aria-label="previous capture">‹</button>
|
||||
<button class="vbtn next" id="vNext" aria-label="next capture">›</button>
|
||||
</div>
|
||||
<div class="vcap"><span id="vCap">Dam — v0.4.0 intro attract</span></div>
|
||||
</div>
|
||||
|
||||
<details>
|
||||
<summary><strong>Full gallery</strong>: 12 in-engine captures from a v0.4.0 intro-attract pass (3072×1728 widescreen, Sep 2026)</summary>
|
||||
@@ -163,57 +171,98 @@ More captures may land here as playtesting continues.
|
||||
licensed and yours to extend: new video options, input tweaks, your own
|
||||
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 was agentic — Claude Pro plus a local
|
||||
open-weight model on a single RTX 5090, as of August–September 2026. This
|
||||
project is as much a study of *that process* as it is a port: whether
|
||||
current LLMs can carry a codebase like this, and what actually goes wrong
|
||||
along the way. Game codebases have historically been a rough fit for
|
||||
LLMs — large, stateful, hardware-adjacent, unforgiving of a subtly wrong
|
||||
memory layout — more so for older local models, so this is a useful data
|
||||
point on where that stands now, for better or worse. Judge the result for
|
||||
yourself. I'm one person doing this in my spare time, not a team. See
|
||||
[the full write-up](dev/agentic-development.md) for the setup, timeline,
|
||||
handoff workflow, and an honest assessment of what did and didn't work.
|
||||
- **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
|
||||
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 is compiled completely unmodified; the decompilation's
|
||||
control flow is ground truth. The N64's Reality Signal Processor, the graphics
|
||||
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 GBI stream the game emits and translates it
|
||||
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).
|
||||
|
||||
## Honest status
|
||||
## Known issues
|
||||
|
||||
- On Facility, if gas leaks during Ourumov's monologue he can pause for up to
|
||||
~10 s before resuming the scripted shootout — a latent race that exists in
|
||||
the N64 original too (where it softlocks permanently); the port detects and
|
||||
auto-recovers it. (D318)
|
||||
- With native widescreen on, the F10 options overlay stretches with the
|
||||
window instead of pillarboxing like the front-end menus (legible; cosmetic;
|
||||
the F10 *Native widescreen* toggle restores the old stretched frame
|
||||
throughout). The world/HUD widescreen rendering itself is correct. (D335b)
|
||||
- The front-end Rareware logo shows a subtle texture-filtering artifact
|
||||
(the Nintendo logo and legal page are clean). Cosmetic only. (D75)
|
||||
- **`All unlocked` is experimental**: 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. (D387)
|
||||
- No macOS or ARM support. Keyboard/mouse rebinding shipped in v0.4.0;
|
||||
controller-button rebinding is not supported yet.
|
||||
|
||||
The full list, with root causes and fix status: the
|
||||
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 two-agent development case study](dev/agentic-development.md): goal, setup, timeline, the handoff workflow, and an honest assessment of what did and didn't work.
|
||||
- [Development process](dev-process.md): how work was scoped, partitioned, and budgeted across agents; the finding-log discipline.
|
||||
- [Internals](internals.md): architecture, the software RSP-emulation approach, GoldenEye-vs-Perfect-Dark engine differences, the phased plan.
|
||||
- [Porting notes](porting-notes.md): the recurring Nintendo 64 → PC bug classes hit during the port, with fixes.
|
||||
- [Building](building.md): full build and asset-extraction guide.
|
||||
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.
|
||||
|
||||
<script>
|
||||
// console-viewer carousel: one media slot, cycle the captures. Vanilla JS.
|
||||
(function () {
|
||||
var slides = [
|
||||
{ src:"img/shots/shot-01.jpg", cap:"Dam — v0.4.0 intro attract" },
|
||||
{ src:"img/shots/shot-03.jpg", cap:"Runway — v0.4.0 intro attract" },
|
||||
{ src:"img/shots/shot-04.jpg", cap:"Surface — v0.4.0 intro attract" },
|
||||
{ src:"img/shots/shot-09.jpg", cap:"Frigate — v0.4.0 intro attract" },
|
||||
{ src:"img/shots/shot-11.jpg", cap:"Surface 2 — v0.4.0 intro attract" },
|
||||
{ src:"img/shots/shot-16.jpg", cap:"Statue — v0.4.0 intro attract" },
|
||||
{ src:"img/shots/shot-20.jpg", cap:"Archives — v0.4.0 intro attract" },
|
||||
{ src:"img/shots/shot-21.jpg", cap:"Cradle — v0.4.0 intro attract" },
|
||||
{ src:"img/shots/shot-24.jpg", cap:"Aztec — v0.4.0 intro attract" },
|
||||
{ src:"img/shots/shot-27.jpg", cap:"Control — v0.4.0 intro attract" },
|
||||
{ src:"img/shots/shot-28.jpg", cap:"Streets — v0.4.0 intro attract" },
|
||||
{ src:"img/shots/shot-29.jpg", cap:"Jungle — v0.4.0 intro attract" }
|
||||
];
|
||||
var img = document.getElementById("vImg"),
|
||||
cap = document.getElementById("vCap"),
|
||||
idx = document.getElementById("vIdx"),
|
||||
i = 0;
|
||||
function show(n) {
|
||||
i = (n + slides.length) % slides.length;
|
||||
img.src = slides[i].src;
|
||||
cap.innerHTML = slides[i].cap;
|
||||
idx.textContent = (i + 1) + " / " + slides.length;
|
||||
}
|
||||
document.getElementById("vPrev").addEventListener("click", function () { show(i - 1); });
|
||||
document.getElementById("vNext").addEventListener("click", function () { show(i + 1); });
|
||||
document.addEventListener("keydown", function (e) {
|
||||
if (e.key === "ArrowLeft") show(i - 1);
|
||||
else if (e.key === "ArrowRight") show(i + 1);
|
||||
});
|
||||
show(0);
|
||||
})();
|
||||
</script>
|
||||
@@ -0,0 +1,24 @@
|
||||
---
|
||||
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.
|
||||
---
|
||||
|
||||
## Input and aim
|
||||
|
||||
`port/src/input.c` maps **SDL2 keyboard, mouse and gamepad** events into the
|
||||
N64's own controller structs, so the game's input code runs unmodified.
|
||||
|
||||
**Mouse aim** is modelled on the *GEPD* mouse-injector behaviour that the 1964
|
||||
"GoldenEye/Perfect Dark Edition" bundle used — an **independent
|
||||
reimplementation** of that behaviour (not derived code), credited to its
|
||||
author in the README. It comes in two styles, the N64's own aim feel or a
|
||||
centered FPS-style, with per-device sensitivity, smoothing and Y-inversion.
|
||||
|
||||
**Controllers:** modern pads get a dual-stick layout, deadzones,
|
||||
sensitivity, southpaw, trigger thresholds, Rumble-Pak vibration, and
|
||||
PlayStation/Nintendo button names. **Everything is rebindable** — keyboard,
|
||||
mouse and each controller — in the F10 options overlay. Split-screen needs one
|
||||
controller per extra player.
|
||||
|
||||
*More: [internals](internals.md) §7 (input & saves); the aim model's
|
||||
provenance is the D194 lineage in `port/src/input.c`.*
|
||||
|
Before Width: | Height: | Size: 14 MiB |
@@ -1441,6 +1441,10 @@ Some decomp functions write past a local array in edge cases. On the N64, IDO's
|
||||
|
||||
Also the read side: D496 (`generate_language_specific_text_for_weapon` strcat'd onto an uninitialised caller stack buffer for 3+ players; init under `AVOID_UB`).
|
||||
|
||||
## D25. Cross-platform goldens: a fixed frame stem is only comparable at settled gameplay (D521)
|
||||
|
||||
`GE_PCDUMP`/`GE_QUITFRAME` step the port's *sim* frame counter (`port/src/video.c:1406` `++frames`, one per `portRenderGfxTask()` at `port/src/libultra.c:1256`), and retrace messages are dropped at the posting site when the game's queue is nearly full (`port/src/libultra.c:753`), so a slow box stretches wall time without advancing `frames` -- frame stems are comparable, wall time is not. What is NOT comparable is any stem inside the intro flyby, whose settle point is wall-clock paced (D117): on a ~54 fps Linux box the flyby was still running at frame 900 (settles ~830-1130 run to run) while the Windows golden at 900 was settled, which reads as a 92.9% "different scene" delta if reported bare. Tells: over-tol2 % >30% at one stem and 1-7% at the next; phash Hamming in the 80s; a dense dump whose consecutive diffs sit at 80-95% and drop to 1-5% once settled. Never compare across platforms with `GE_DETERM=1` (call-sequenced retraces: stable run to run, different frame -> sim mapping). Before calling a residual band a renderer bug, classify it: a constant ~20 delta confined to a screen band (Archives' top 3 grid rows, all three stems) is systematic and needs a same-sim-state capture pair; a uniform d 3-6 over the whole frame with cell means <= 11 (Cuba) is the dither/quantization noise floor. **RESOLVED 2026-10-05 (D525):** both residual bands were *save state*, not renderer — the pair that closed them was a re-round of the whole linux set with `data/ge007.eep` pinned present (Archives 19.956-24.554% -> 3.590-3.859%, worst cell 50.8 -> 2.31; Streets 1200's 16.0-148.4 cell-mean band -> 3.616-3.983%, worst cell 4.43). The diagnostic tell that separates the two: re-capture one side with the save present and the band collapses to the noise floor, while a renderer defect survives identical sim state.
|
||||
|
||||
## E. Process / method notes
|
||||
|
||||
- **Never `exit()` while another thread may be inside the GL driver (D344).**
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
title: Saves
|
||||
description: How the GoldenEye 007 PC port backs the N64's EEPROM save with a plain file you can back up.
|
||||
---
|
||||
|
||||
## Saves
|
||||
|
||||
GoldenEye's N64 save is **EEPROM-based** — the controller's EEPROM
|
||||
(`osEeprom*`), not a Memory Pak. The port backs it with a **plain file**,
|
||||
`data/ge007.eep` next to the executable, so your progress is a file you can
|
||||
copy to back it up (or move to another machine).
|
||||
|
||||
A few details:
|
||||
|
||||
- The **PFS / Memory-Pak and motor (Rumble Pak) code** is only for accessory
|
||||
detection and is stubbed to report "no accessory," so the game proceeds
|
||||
without a Memory Pak.
|
||||
- **Emulator saves can be imported** — copy a `GOLDENEYE-usa.eep` in as
|
||||
`data/ge007.eep` (see the README's *Using an emulator save*).
|
||||
- The save's **checksum is part of the file format**. The PRNG/CRC the save
|
||||
depends on is kept byte-stable, because changing it would silently
|
||||
invalidate existing saves — a porting-notes finding (D9).
|
||||
- `-fresh` (a launch option) wipes the save and settings for a clean-slate run.
|
||||
|
||||
*More: [internals](internals.md) §7; the checksum finding is
|
||||
[porting notes](porting-notes.md) D9.*
|
||||
@@ -4,7 +4,7 @@ description: What a release actually installs (no networking, no telemetry, no R
|
||||
and how faithfully the port tracks the original N64 game's logic.
|
||||
---
|
||||
|
||||
# Security & fidelity status
|
||||
## 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
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
---
|
||||
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.
|
||||
---
|
||||
|
||||
## The asset pipeline
|
||||
|
||||
The N64 stored the game's assets (models, textures, fonts, level and cutscene
|
||||
data) inside the ROM in on-hardware formats a PC can't use directly. The port
|
||||
converts them into two "sidecar" files it loads at runtime:
|
||||
|
||||
- **`pcmodels`** — the 3D models (a PC layout of the N64's RZ model format).
|
||||
- **`pccg`** — the graphics: textures, fonts and the rest of the ROM data.
|
||||
|
||||
Each is a single concatenated image plus a small `manifest.csv` (name, offset,
|
||||
size) that the loader reads — `port/src/pcmodels.c` and `port/src/pccg.c`.
|
||||
|
||||
**Where they come from.** Two paths produce the same files:
|
||||
|
||||
- **A downloaded build** generates them from *your* ROM on first start, into
|
||||
`data/pcmodels-<region>/` and `data/pccg-<region>/` next to the executable.
|
||||
Delete them at any time — they are made again on the next start.
|
||||
- **Building from source** runs the offline Python converters in `tools_pc/`
|
||||
to produce the same files (see the [Building](building.md) guide, §4).
|
||||
|
||||
This is one of the deliberate differences from the Perfect Dark port, which
|
||||
fixes up the N64 asset formats at load time: GoldenEye's serialized formats
|
||||
are converted **offline** instead.
|
||||
|
||||
*More: [building](building.md) §2 and §4; the loaders are
|
||||
`port/src/pcmodels.c` and `port/src/pccg.c`.*
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
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.
|
||||
---
|
||||
|
||||
## The software RSP
|
||||
|
||||
On the N64, the **Reality Signal Processor (RSP)** is the graphics
|
||||
coprocessor. The R4300 CPU builds a list of graphics commands each frame
|
||||
(the **GBI display list**); the RSP runs a "fast3d" microcode that turns
|
||||
those commands into **RDP** (rasterizer) commands, which the RDP turns into
|
||||
pixels.
|
||||
|
||||
The port does two things to that pipeline:
|
||||
|
||||
- **It emulates the RSP in software.** `port/fast3d/` (~7,000 lines of C++,
|
||||
adapted from the Perfect Dark port) *is* the RSP: it interprets the GBI
|
||||
display list the game builds and, instead of emitting RDP commands, emits
|
||||
**OpenGL** calls directly.
|
||||
- **It bypasses the RDP entirely** — there is no rasterizer to emulate,
|
||||
because the software RSP talks straight to the GPU.
|
||||
|
||||
The game's own R4300 code runs natively and is unaware of the difference: it
|
||||
builds display lists and "starts the RSP" exactly as it would on hardware. A
|
||||
replacement scheduler (`pdsched.c`) catches the "start the RSP" call and runs
|
||||
the software RSP on the render thread.
|
||||
|
||||
GoldenEye's custom commands are handled by the same interpreter: the packed
|
||||
four-triangle `G_TRI4` command and the fade/blend **color-combiner** modes
|
||||
(compiled on the fly from the N64's combiner equation to a GLSL shader by
|
||||
`gfx_cc.cpp`). The N64's own RSP microcode (`rsp/graphics/gmain.s`) is not
|
||||
run on PC but is the reference the software RSP's command decoding is checked
|
||||
against.
|
||||
|
||||
*More: [internals](internals.md) §2 (the reference architecture) and §5
|
||||
(GoldenEye-specific graphics work).*
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
title: Under the Hood
|
||||
description: The technical documentation for the GoldenEye 007 PC port; the architecture, the N64-to-PC bug catalogue, and how the project is developed.
|
||||
---
|
||||
|
||||
## Under the hood
|
||||
|
||||
The technical documentation for the port: how it works under the covers, the
|
||||
catalogue of N64→PC bugs it had to fix, and how the project is developed.
|
||||
(Building and running from source live on the separate
|
||||
[Building](building.md) page.)
|
||||
|
||||
### How it works
|
||||
|
||||
- **[Internals](internals.md)** — the architecture: the software RSP, the
|
||||
port layer, the asset pipeline, and the phased plan. The single reference
|
||||
for what the port is and how it's put together.
|
||||
- **[Porting notes](porting-notes.md)** — the catalogue of N64→PC bugs: the
|
||||
recurring classes of defect and the ground-truth pattern each fix follows.
|
||||
|
||||
### How it's developed
|
||||
|
||||
- **[Development process](dev-process.md)** — the investigation workflow:
|
||||
task budgets, file partitioning, the finding-log discipline, and the rules
|
||||
for what counts as a diagnosis versus a change.
|
||||
- **[Agentic development](dev/agentic-development.md)** — the case study:
|
||||
how the two-AI-agent setup ran, its timeline and numbers, and an honest
|
||||
read on what did and didn't work.
|
||||
|
||||
### Topic notes
|
||||
|
||||
Short, single-topic notes — the quick-orientation version of the longer docs
|
||||
above:
|
||||
|
||||
- **[The software RSP](the-software-rsp.md)** — how the N64's graphics
|
||||
coprocessor is emulated in software.
|
||||
- **[The asset pipeline](the-asset-pipeline.md)** — how ROM assets become the
|
||||
files the port loads at runtime.
|
||||
- **[Framerate and pacing](framerate-and-pacing.md)** — the steady 60 fps,
|
||||
frame pacing and per-frame timing.
|
||||
- **[Input and aim](input-and-aim.md)** — controllers, keyboard, mouse aim
|
||||
and rebinding.
|
||||
- **[Saves](saves.md)** — the file-backed N64 save.
|
||||
@@ -1892,7 +1892,8 @@ static inline bool tri_dbg(void) {
|
||||
const char* a = getenv("GE_D75D");
|
||||
const char* b = getenv("GE_ZF");
|
||||
const char* c = getenv("GE_D303");
|
||||
s_tri_dbg = ((a && *a) || (b && *b) || (c && *c)) ? 1 : 0;
|
||||
const char* d = getenv("GE_D526");
|
||||
s_tri_dbg = ((a && *a) || (b && *b) || (c && *c) || (d && *d)) ? 1 : 0;
|
||||
}
|
||||
return s_tri_dbg != 0;
|
||||
}
|
||||
@@ -1978,6 +1979,125 @@ static void d75d_note_emit_z(uint32_t f, struct LoadedVertex* const* v_arr, int
|
||||
d75d_env[0]=rdp.env_color.r; d75d_env[1]=rdp.env_color.g; d75d_env[2]=rdp.env_color.b; d75d_env[3]=rdp.env_color.a;
|
||||
}
|
||||
|
||||
/* D526 (docs/dev/findings.md §D526): per-triangle draw-state census
|
||||
* probe, text-only. Retained as a generic transparency/texture triage
|
||||
* tool (the D195/D266/D268/D273 bug class; the P13 Dam-ending grate it
|
||||
* targeted, D526, was confirmed faithful N64 behaviour on 1964/GEPD,
|
||||
* so no fix was needed). GE_D526="lo-hi" = sim-frame range (the same
|
||||
* dash syntax as GE_D75D); optional GE_D526BOX="x0 y0 x1 y1" targets
|
||||
* an NDC region (same syntax as GE_ZFBOX; default = on-screen);
|
||||
* GE_D526MAX caps per-triangle lines (default 400). For every emitted
|
||||
* triangle in range whose NDC bbox intersects the region, one line:
|
||||
* frame, NDC bbox, z/w, geometry_mode, other_mode_l/h, combine word,
|
||||
* sampled tile 0 (and 1 in 2-cycle) tmem/fmt/siz, first_tile_index,
|
||||
* prim alpha, vertex alpha min/max, use_alpha/modulate/invisible.
|
||||
* Plus one per-frame line aggregating the in-region tris by
|
||||
* (comb, oml, omh, alpha) signature, so a suspect surface's signature
|
||||
* stands out without enumerating every tri. Zero cost unless
|
||||
* GE_D75D/GE_ZF/GE_D303/GE_D526 is set (folded into the D474 tri_dbg
|
||||
* gate). */
|
||||
static int d526_on = -1;
|
||||
static int d526_lo = -1, d526_hi = 0x7FFFFFFF, d526_max = 400;
|
||||
static uint32_t d526_lines = 0;
|
||||
static float d526_box[4] = { -1.05f, -1.05f, 1.05f, 1.05f };
|
||||
static int d526_box_on = 0;
|
||||
#define D526_SIGS 32
|
||||
static struct { uint64_t comb; uint32_t oml, omh; int alpha; uint32_t cnt; } d526_sigs[D526_SIGS];
|
||||
static int d526_nsigs = 0;
|
||||
static uint32_t d526_sig_frame = 0, d526_sig_total = 0;
|
||||
|
||||
static void d526_flush_sigs(uint32_t f) {
|
||||
if (d526_nsigs == 0) { d526_sig_frame = f; return; }
|
||||
fprintf(stderr, "D526F: f=%u inbox=%u sigs: ", f, d526_sig_total);
|
||||
for (int i = 0; i < d526_nsigs; i++)
|
||||
fprintf(stderr, "(comb=0x%016llx oml=0x%08x omh=0x%08x a=%d)x%u ",
|
||||
(unsigned long long)d526_sigs[i].comb, d526_sigs[i].oml,
|
||||
d526_sigs[i].omh, d526_sigs[i].alpha, d526_sigs[i].cnt);
|
||||
fprintf(stderr, "\n");
|
||||
d526_nsigs = 0; d526_sig_total = 0;
|
||||
d526_sig_frame = f;
|
||||
}
|
||||
static void d526_atexit(void) { d526_flush_sigs(0xFFFFFFFFu); }
|
||||
static void d526_init(void) {
|
||||
if (d526_on >= 0) return;
|
||||
d526_on = 0;
|
||||
const char* e = getenv("GE_D526");
|
||||
if (e && *e) {
|
||||
d526_on = 1;
|
||||
d526_lo = atoi(e);
|
||||
const char* dash = strchr(e, '-');
|
||||
if (dash) d526_hi = atoi(dash + 1);
|
||||
}
|
||||
if (d526_on) {
|
||||
const char* m = getenv("GE_D526MAX");
|
||||
if (m && *m) d526_max = atoi(m);
|
||||
const char* b = getenv("GE_D526BOX");
|
||||
if (b && *b && sscanf(b, "%f %f %f %f",
|
||||
&d526_box[0], &d526_box[1], &d526_box[2], &d526_box[3]) == 4)
|
||||
d526_box_on = 1;
|
||||
atexit(d526_atexit);
|
||||
}
|
||||
}
|
||||
static void d526_note_emit(uint32_t f, struct LoadedVertex* const* v_arr, int use_alpha, int use_modulate) {
|
||||
d526_init();
|
||||
if (!d526_on) return;
|
||||
if (f < (uint32_t)d526_lo || f > (uint32_t)d526_hi) return;
|
||||
if (f != d526_sig_frame) d526_flush_sigs(f);
|
||||
float mnx = 1e9f, mxx = -1e9f, mny = 1e9f, mxy = -1e9f;
|
||||
float zsum = 0.f, wsum = 0.f; int vmin = 255, vmax = -1, ok = 0;
|
||||
for (int i = 0; i < 3; i++) {
|
||||
float w = v_arr[i]->w;
|
||||
if (w <= 0.f) continue;
|
||||
float nx = v_arr[i]->x / w, ny = v_arr[i]->y / w;
|
||||
if (nx < mnx) mnx = nx; if (nx > mxx) mxx = nx;
|
||||
if (ny < mny) mny = ny; if (ny > mxy) mxy = ny;
|
||||
zsum += v_arr[i]->z / w; wsum += w;
|
||||
int a = v_arr[i]->color.a;
|
||||
if (a < vmin) vmin = a; if (a > vmax) vmax = a;
|
||||
ok = 1;
|
||||
}
|
||||
if (!ok) return;
|
||||
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
|
||||
d526_sig_total++;
|
||||
int found = 0;
|
||||
for (int i = 0; i < d526_nsigs; i++) {
|
||||
if (d526_sigs[i].comb == rdp.combine_mode && d526_sigs[i].oml == rdp.other_mode_l &&
|
||||
d526_sigs[i].omh == rdp.other_mode_h && d526_sigs[i].alpha == use_alpha) {
|
||||
d526_sigs[i].cnt++; found = 1; break;
|
||||
}
|
||||
}
|
||||
if (!found && d526_nsigs < D526_SIGS) {
|
||||
d526_sigs[d526_nsigs].comb = rdp.combine_mode;
|
||||
d526_sigs[d526_nsigs].oml = rdp.other_mode_l;
|
||||
d526_sigs[d526_nsigs].omh = rdp.other_mode_h;
|
||||
d526_sigs[d526_nsigs].alpha = use_alpha;
|
||||
d526_sigs[d526_nsigs].cnt = 1;
|
||||
d526_nsigs++;
|
||||
}
|
||||
if (d526_lines++ >= (uint32_t)d526_max) return;
|
||||
const uint32_t fi = rdp.first_tile_index;
|
||||
const uint32_t t0 = fi + gfx_lod_tile_offset(0);
|
||||
const uint32_t omh = rdp.other_mode_h;
|
||||
const int two = ((omh & (3U << G_MDSFT_CYCLETYPE)) == G_CYC_2CYCLE);
|
||||
const uint32_t t1 = two ? fi + gfx_lod_tile_offset(1) : t0;
|
||||
const int inv = (rdp.other_mode_l & (3u << 24)) == ((uint32_t)G_BL_0 << 24) &&
|
||||
(rdp.other_mode_l & (3u << 20)) == ((uint32_t)G_BL_CLR_MEM << 20);
|
||||
fprintf(stderr,
|
||||
"D526: f=%u ndc=(%.3f,%.3f)-(%.3f,%.3f) zw=%.4f gm=0x%08x oml=0x%08x omh=0x%08x comb=0x%016llx fi=%u "
|
||||
"tile0[tmem=%u fmt=%u siz=%u]",
|
||||
f, mnx, mny, mxx, mxy, (wsum > 0.f ? zsum / wsum : -1.f),
|
||||
rsp.geometry_mode, rdp.other_mode_l, omh,
|
||||
(unsigned long long)rdp.combine_mode, fi,
|
||||
rdp.texture_tile[t0].tmem, rdp.texture_tile[t0].fmt, rdp.texture_tile[t0].siz);
|
||||
if (two)
|
||||
fprintf(stderr, " tile1[tmem=%u fmt=%u siz=%u]",
|
||||
rdp.texture_tile[t1].tmem, rdp.texture_tile[t1].fmt, rdp.texture_tile[t1].siz);
|
||||
fprintf(stderr, " pa=%u va=[%d,%d] alpha=%d mod=%d inv=%d 2cyc=%d\n",
|
||||
rdp.prim_color.a, vmin, vmax, use_alpha, use_modulate, inv, two);
|
||||
}
|
||||
|
||||
static void gfx_sp_vertex(size_t n_vertices, size_t dest_index, const Vtx* vertices) {
|
||||
const size_t d75_di0 = dest_index; /* D75 probe: loop below mutates dest_index */
|
||||
SUPPORT_CHECK(n_vertices <= MAX_VERTICES);
|
||||
@@ -2735,6 +2855,7 @@ static void gfx_sp_tri1(uint8_t vtx1_idx, uint8_t vtx2_idx, uint8_t vtx3_idx, bo
|
||||
d75d_note_emit(videoGetFrameCount(), v_arr); // survived all rejection gates -> reaches GL
|
||||
d303_note_emit(videoGetFrameCount(), v_arr); // TEMP D303
|
||||
zf_note_emit(videoGetFrameCount(), v_arr); // TEMP D306/D308
|
||||
d526_note_emit(videoGetFrameCount(), v_arr, use_alpha ? 1 : 0, use_modulate ? 1 : 0); // D526 census
|
||||
d75d_note_emit_z(videoGetFrameCount(), v_arr, (used_textures[0] || used_textures[1]) ? 1 : 0, comb->used_textures[0] ? 1 : 0);
|
||||
}
|
||||
for (int i = 0; i < 3; i++) {
|
||||
|
||||
@@ -0,0 +1,338 @@
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
|
||||
<https://fsf.org/>
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Lesser General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
How to Apply These Terms to Your New Programs
|
||||
|
||||
If you develop a new program, and you want it to be of the greatest
|
||||
possible use to the public, the best way to achieve this is to make it
|
||||
free software which everyone can redistribute and change under these terms.
|
||||
|
||||
To do so, attach the following notices to the program. It is safest
|
||||
to attach them to the start of each source file to most effectively
|
||||
convey the exclusion of warranty; and each file should have at least
|
||||
the "copyright" line and a pointer to where the full notice is found.
|
||||
|
||||
<one line to give the program's name and a brief idea of what it does.>
|
||||
Copyright (C) <year> <name of author>
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along
|
||||
with this program; if not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
Also add information on how to contact you by electronic and paper mail.
|
||||
|
||||
If the program is interactive, make it output a short notice like this
|
||||
when it starts in an interactive mode:
|
||||
|
||||
Gnomovision version 69, Copyright (C) year name of author
|
||||
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
|
||||
This is free software, and you are welcome to redistribute it
|
||||
under certain conditions; type `show c' for details.
|
||||
|
||||
The hypothetical commands `show w' and `show c' should show the appropriate
|
||||
parts of the General Public License. Of course, the commands you use may
|
||||
be called something other than `show w' and `show c'; they could even be
|
||||
mouse-clicks or menu items--whatever suits your program.
|
||||
|
||||
You should also get your employer (if you work as a programmer) or your
|
||||
school, if any, to sign a "copyright disclaimer" for the program, if
|
||||
necessary. Here is a sample; alter the names:
|
||||
|
||||
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
|
||||
`Gnomovision' (which makes passes at compilers) written by James Hacker.
|
||||
|
||||
<signature of Moe Ghoul>, 1 April 1989
|
||||
Moe Ghoul, President of Vice
|
||||
|
||||
This General Public License does not permit incorporating your program into
|
||||
proprietary programs. If your program is a subroutine library, you may
|
||||
consider it more useful to permit linking proprietary applications with the
|
||||
library. If this is what you want to do, use the GNU Lesser General
|
||||
Public License instead of this License.
|
||||
@@ -13,7 +13,6 @@
|
||||
.\tools_pc\audiodebug.ps1 -Fire # + scripted PPK fire pulses
|
||||
.\tools_pc\audiodebug.ps1 -Fire -Trace # + per-sndPlaySfx trace + index histogram
|
||||
.\tools_pc\audiodebug.ps1 -Fire -Trace -Dump # + raw mixed PCM to audiodump.raw
|
||||
.\tools_pc\audiodebug.ps1 -AB -Fire # run BOTH modes, print before/after table
|
||||
.\tools_pc\audiodebug.ps1 -Play # INTERACTIVE playtest, no timeout (D202)
|
||||
.\tools_pc\audiodebug.ps1 -Soak # 5-minute stability soak
|
||||
|
||||
@@ -49,9 +48,8 @@ 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
|
||||
[switch]$Old, # GE_D204_OLD=1: pre-D204 behaviour
|
||||
# NOTE: GE_D204_OLD was removed from the game; -AB is now a no-op.
|
||||
[switch]$AB, # run twice (old, then new) and compare
|
||||
# (the -AB/-Old A/B switches were removed 2026-10-07, D532: GE_D204_OLD no
|
||||
# longer exists in the game, so they were documented no-ops)
|
||||
|
||||
# Plumbing
|
||||
[switch]$NoBuild,
|
||||
@@ -121,7 +119,7 @@ if ($GameArgs) { $gameArgv += @($GameArgs | Where-Object { $_ }) }
|
||||
|
||||
# ----------------------------------------------------------- run one -------
|
||||
function Invoke-AudioRun {
|
||||
param([string]$Tag, [bool]$OldMode)
|
||||
param([string]$Tag)
|
||||
|
||||
$stdout = Join-Path $buildDir "audiodebug_$Tag.log"
|
||||
Remove-Item -ErrorAction SilentlyContinue $stdout,
|
||||
@@ -131,14 +129,13 @@ function Invoke-AudioRun {
|
||||
|
||||
# Set the probes on THIS process; cmd.exe and the game both inherit them.
|
||||
$env:GE_D204 = "1"
|
||||
if ($OldMode) { $env:GE_D204_OLD = "1" } else { Remove-Item -EA SilentlyContinue Env:\GE_D204_OLD }
|
||||
if ($Trace) { $env:GE_AUDIOTRACE = "1" } else { Remove-Item -EA SilentlyContinue Env:\GE_AUDIOTRACE }
|
||||
if ($Dump) { $env:GE_AUDIODUMP = "1" } else { Remove-Item -EA SilentlyContinue Env:\GE_AUDIODUMP }
|
||||
if ($VoiceDump) { $env:GE_VOICEDUMP = "1" } else { Remove-Item -EA SilentlyContinue Env:\GE_VOICEDUMP }
|
||||
if ($MixerTrace) { $env:GE_MIXERTRACE = "1" } else { Remove-Item -EA SilentlyContinue Env:\GE_MIXERTRACE }
|
||||
if ($inputScript) { $env:GE_INPUTSCRIPT = $inputScript } else { Remove-Item -EA SilentlyContinue Env:\GE_INPUTSCRIPT }
|
||||
|
||||
$mode = if ($OldMode) { "GE_D204_OLD (pre-fix)" } else { "current build" }
|
||||
$mode = "current build"
|
||||
$what = if ($Play) { "interactive - play until you close the window" } else { "$Seconds s" }
|
||||
Write-Host "`n>>> run [$Tag] $mode - $what" -ForegroundColor Green
|
||||
if ($inputScript) {
|
||||
@@ -293,26 +290,8 @@ function Show-TraceSummary {
|
||||
}
|
||||
|
||||
# ------------------------------------------------------------------ go -----
|
||||
if ($AB) {
|
||||
$oldLog = Invoke-AudioRun -Tag "old" -OldMode $true
|
||||
$newLog = Invoke-AudioRun -Tag "new" -OldMode $false
|
||||
$a = Get-AudioSummary -LogPath $oldLog -Tag "old"
|
||||
$b = Get-AudioSummary -LogPath $newLog -Tag "new"
|
||||
Show-AudioVerdict $a
|
||||
Show-AudioVerdict $b
|
||||
Write-Host "`n===================== A/B =====================" -ForegroundColor Yellow
|
||||
Write-Host (" {0,-24} {1,12} {2,12}" -f "", "GE_D204_OLD", "current")
|
||||
Write-Host (" {0,-24} {1,12:N3} {2,12:N3}" -f "rt (min, steady)", $a.RtMin, $b.RtMin)
|
||||
Write-Host (" {0,-24} {1,12} {2,12}" -f "queue min", $a.QMin, $b.QMin)
|
||||
Write-Host (" {0,-24} {1,12} {2,12}" -f "queue max", $a.QMax, $b.QMax)
|
||||
Write-Host (" {0,-24} {1,12} {2,12}" -f "dropped blocks", $a.Drop, $b.Drop)
|
||||
Write-Host (" {0,-24} {1,12} {2,12}" -f "largest block", $a.MaxBlock, $b.MaxBlock)
|
||||
Write-Host "`n Both runs used the SAME binary (GE_D204_OLD switches behaviour at" -ForegroundColor DarkGray
|
||||
Write-Host " runtime), so this is free of build-to-build variance." -ForegroundColor DarkGray
|
||||
} else {
|
||||
$log = Invoke-AudioRun -Tag "run" -OldMode ([bool]$Old)
|
||||
$log = Invoke-AudioRun -Tag "run"
|
||||
Show-AudioVerdict (Get-AudioSummary -LogPath $log -Tag "run")
|
||||
}
|
||||
if ($Trace) { Show-TraceSummary }
|
||||
|
||||
Write-Host "`nlogs: build-pc\audiodebug_*.log" -ForegroundColor DarkGray
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
#!/bin/bash
|
||||
# P7 golden capture on Linux (mirrors tools_pc/verify.sh's golden recipe).
|
||||
#
|
||||
# Usage: capture_p7.sh [-b BUILD_DIR] <level_name>:<num> [<level_name>:<num> ...]
|
||||
# BUILD_DIR build dir holding ge007.x86_64, relative to the repo root
|
||||
# (default: build-linux). The repo root is the parent of the
|
||||
# directory this script lives in, so the script can be invoked
|
||||
# from anywhere; no machine-specific path is baked in.
|
||||
# Writes only inside the repo: <repo>/data/ge007.ini + ge007.eep (the
|
||||
# pinned 640x480 ini and the canonical save, re-pinned before every level;
|
||||
# the local save is only snapshotted for the exit restore),
|
||||
# <repo>/ppm (the game's dump dir, removed after each level), and
|
||||
# <repo>/tools_pc/golden/<level>/linux/.
|
||||
# Per-level run logs go to <repo>/cap_<level>.log: scratch, never tracked.
|
||||
# DISPLAY must be inherited from the caller -- the capture needs a real GL
|
||||
# context, and there is deliberately no default display/XAUTHORITY here.
|
||||
#
|
||||
# Recipe: the per-level windows/scripts/seed are READ OUT OF tools_pc/verify.sh
|
||||
# (golden_dump_for / golden_script_for / GOLDEN_SEED) so this tool and the gate
|
||||
# cannot drift apart -- see tools_pc/golden/README.md for what the windows are
|
||||
# and why (D522). GE_QUITFRAME = last requested frame + 2 (orderly quit, D344).
|
||||
#
|
||||
# D523 (SUPERSEDED by D529): the win/ set was captured with a save file, and
|
||||
# the frame depends on the save's CONTENT (D529's A/B: facility 177.47 on the
|
||||
# 2026-10-05 playtest save, 0.0-0.25 on the four other candidates). This tool
|
||||
# installs the CANONICAL save (tools_pc/golden/ge007.eep, in-tree; verify.sh
|
||||
# does the same for the gate) rather than trusting a local eep -- a local
|
||||
# playtest save is exactly this class of drift. (History: the P7 round of
|
||||
# 2026-10-04 ran with a cleared eep; that set was re-captured 2026-10-05, D525.)
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
BUILD_DIR=build-linux
|
||||
if [ "${1:-}" = -b ]; then
|
||||
[ -n "${2:-}" ] || { echo "error: -b needs a BUILD_DIR argument"; exit 2; }
|
||||
BUILD_DIR="$2"; shift 2
|
||||
fi
|
||||
[ "$#" -gt 0 ] || { echo "error: no <level_name>:<num> specs given"; exit 2; }
|
||||
EXE="$ROOT/$BUILD_DIR/ge007.x86_64"
|
||||
[ -x "$EXE" ] || { echo "error: no executable at $EXE (pass -b BUILD_DIR)"; exit 2; }
|
||||
[ -n "${DISPLAY:-}" ] || { echo "error: DISPLAY is unset; the capture needs a real GL context (no default provided)"; exit 2; }
|
||||
VSH="$ROOT/tools_pc/verify.sh"
|
||||
[ -f "$VSH" ] || { echo "error: no tools_pc/verify.sh under $ROOT (the recipe lives there)"; exit 2; }
|
||||
CANON_EEP="$ROOT/tools_pc/golden/ge007.eep"
|
||||
[ -f "$CANON_EEP" ] || { echo "error: canonical golden save missing: $CANON_EEP (D529)"; exit 2; }
|
||||
cd "$ROOT" # the loop's dump/golden paths are root-relative (ppm/, tools_pc/golden/)
|
||||
EEP="$ROOT/data/ge007.eep"
|
||||
EEP_ORIG=$(mktemp); EEP_HAD=0
|
||||
[ -f "$EEP" ] && { EEP_HAD=1; cp "$EEP" "$EEP_ORIG"; }
|
||||
cp "$CANON_EEP" "$EEP"
|
||||
INI="$ROOT/data/ge007.ini"
|
||||
INI_ORIG=""
|
||||
[ -f "$INI" ] && { INI_ORIG=$(mktemp); cp "$INI" "$INI_ORIG"; }
|
||||
cleanup() {
|
||||
[ -n "$INI_ORIG" ] && cp "$INI_ORIG" "$INI" 2>/dev/null
|
||||
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
|
||||
|
||||
# recipe comes from the tracked gate tool, not from this file
|
||||
eval "$(grep -m1 '^GOLDEN_DUMP=' "$VSH")"
|
||||
eval "$(grep -m1 '^golden_dump_for()' "$VSH")"
|
||||
eval "$(grep -m1 '^golden_script_for()' "$VSH")"
|
||||
eval "$(grep -m1 '^GOLDEN_SEED=' "$VSH")"
|
||||
|
||||
for spec in "$@"; do
|
||||
name="${spec%%:*}"; num="${spec##*:}"
|
||||
[ "$name" != "$num" ] || { echo "error: bad spec $spec"; exit 2; }
|
||||
dump=$(golden_dump_for "$name")
|
||||
script=$(golden_script_for "$name")
|
||||
IFS='-:' read -r lo hi step <<< "$dump"
|
||||
last=$(( lo + step * ((hi - lo) / step) ))
|
||||
qf=$(( last + 2 ))
|
||||
printf '[Window]\nWidth = 640\nHeight = 480\n' > "$INI"
|
||||
# D531: re-pin the CANONICAL save before every level (the frame depends on
|
||||
# the save's CONTENT, D529). The D524-era re-pin restored $EEP_ORIG (the
|
||||
# local pre-capture save, or an empty file when none existed), so the
|
||||
# pre-loop canonical install below was silently overwritten from level 1:
|
||||
# the 2026-10-05 box re-round ran under the box's local save, not the
|
||||
# canonical one. EEP_ORIG is kept only for the exit restore.
|
||||
cp "$CANON_EEP" "$EEP"
|
||||
rm -f ge007.crash.log
|
||||
rm -rf ppm
|
||||
t0=$(date +%s)
|
||||
( export GE_PCDUMP="$dump" GE_QUITFRAME="$qf" GE_INPUTSCRIPT="$script" \
|
||||
GE_RSEED="$GOLDEN_SEED"; timeout 300 "$EXE" -level_"$num" ) > "cap_$name.log" 2>&1
|
||||
rc=$?
|
||||
n=0
|
||||
if [ -d ppm ]; then
|
||||
mkdir -p "tools_pc/golden/$name/linux"
|
||||
for f in ppm/*.ppm; do
|
||||
stem=$(basename "$f" .ppm)
|
||||
python3 -c "from PIL import Image; Image.open('$f').save('tools_pc/golden/$name/linux/$stem.png')"
|
||||
n=$((n+1))
|
||||
done
|
||||
rm -rf ppm
|
||||
fi
|
||||
t1=$(date +%s)
|
||||
echo "== $name rc=$rc frames=$n secs=$((t1-t0)) window=$dump"
|
||||
done
|
||||
@@ -52,7 +52,9 @@ def file_generate_crc(data: bytes, buggy: bool):
|
||||
return c1 & 0xFFFFFFFF, c2 & 0xFFFFFFFF
|
||||
|
||||
def main():
|
||||
src = sys.argv[1] if len(sys.argv) > 1 else r"C:\Users\james\Downloads\ge-issue87-artifacts\ge007.eep"
|
||||
if len(sys.argv) < 2:
|
||||
sys.exit("usage: d295_crc.py <ge007.eep> [out.eep]")
|
||||
src = sys.argv[1]
|
||||
dst = sys.argv[2] if len(sys.argv) > 2 else None
|
||||
eep = bytearray(open(src, "rb").read())
|
||||
assert len(eep) == 2048, len(eep)
|
||||
|
||||
@@ -2,17 +2,37 @@
|
||||
|
||||
`<level>/<platform>/frame_NNNNNN.png`: reference `GE_PCDUMP` captures for
|
||||
`tools_pc/verify.sh`, one folder per solo level (all 21, `cuba` = the ending)
|
||||
and per platform (`win` today; `linux` to come).
|
||||
and per platform (`win`, and `linux` — 21 levels x 3 frames on each, captured
|
||||
2026-10-05 at the same stems).
|
||||
|
||||
**Recipe** (2026-10-02 re-base; `verify.sh` applies all of it):
|
||||
- `data/ge007.ini` pinned to ONLY `[Window] Width = 640, Height = 480` (every
|
||||
other option at its compiled-in default; verify.sh backs yours up).
|
||||
- `GE_RSEED=0x0123456789abcdef` (pinned PRNG).
|
||||
- `GE_INPUTSCRIPT="20:START"` (skips the intro flyby; the scripted pad is
|
||||
the only input) and `GE_PCDUMP="900-1500:300"`: frames 900/1200/1500 are
|
||||
settled gameplay on every level. **Cuba** instead: no input
|
||||
the only input). Window per level, exactly as `verify.sh`'s `golden_dump_for`
|
||||
states it (D522 re-base 2026-10-05 — the 900 stem is NOT settled gameplay on
|
||||
every level: it fails the 1% limit on Surface 2 / Streets / Depot / Cradle and
|
||||
is still inside Dam's flyby):
|
||||
`900-1500:300` (900/1200/1500) on the 14 levels where 900 is settled,
|
||||
`1000-1400:200` (1000/1200/1400) on Surface 2 / Streets / Depot / Cradle /
|
||||
Frigate, `1500-1700:100` (1500/1600/1700) on Dam, and **Cuba** with no input
|
||||
(`1:SNONE`) and `300-900:300` (the ending cutscene; it returns to the boot
|
||||
screens before frame 1200).
|
||||
screens before frame 1200). `framediff` walks the CANDIDATE frames, so a
|
||||
sweep's `--dump` must hit the golden stems exactly — never widen a stride.
|
||||
- The gate INSTALLS THE CANONICAL SAVE `tools_pc/golden/ge007.eep` (2 KB, in-tree;
|
||||
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
|
||||
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
|
||||
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).
|
||||
|
||||
**Gate** (two tiers): the structural framediff (cell means, coverage,
|
||||
@@ -21,12 +41,45 @@ with a per-level limit: 1% of pixels, 3% for Jungle and Surface 2, Cuba
|
||||
structural only. Measured run to run: <= 0.57% on 18 levels, Jungle 1.75%,
|
||||
Surface 2 2.33%, Cuba 36%; a global texture-filter switch measured 22%.
|
||||
|
||||
**Cross-platform rule (D521, P7):** a frame stem is comparable across
|
||||
platforms only if it is settled gameplay on *every* platform in the set.
|
||||
The intro flyby is wall-clock paced (D117): a box that cannot hold 60 fps
|
||||
stretches wall time without advancing the sim-frame counter, so the flyby
|
||||
settles at a different frame per box. On the Linux capture box (Mesa /
|
||||
Intel HD 3000; the golden stems rendered in 11.8-15.1 ms) frame 900 is still
|
||||
inside the flyby (it settles between ~830 and ~1130 run to run) while the
|
||||
win/ golden at 900 is settled -- linux/900 vs win/900 is a scene offset
|
||||
(92.863% over tol 2, phash 83 on Dam), NOT a renderer defect. After the 2026-10-05
|
||||
re-round both platforms sit at the same stems and the cross-platform spread is
|
||||
0.348-3.983% over tol 2 (structural tier 21/21 clean, phash <= 6 except cradle
|
||||
at 40; Cuba 23.7-36.3% is structural-tier-only) — informational only. The gate
|
||||
each platform ships is its own captures against its own goldens; never call the
|
||||
cross-platform spread a parity gate. 1200/1500 are
|
||||
settled on both and measure the real driver delta. `GE_DETERM=1` is for
|
||||
Linux-vs-Linux regression runs only (it changes the frame -> sim mapping;
|
||||
against the win/ goldens it measures 15.788% at 900, 14.594% at 1200 and
|
||||
16.171% at 1500, which is expected and must never be read as a delta).
|
||||
|
||||
Regenerate one level after a deliberate visual change: run
|
||||
`tools_pc/verify.sh <level>` twice to confirm the new look is stable, then
|
||||
capture with the recipe above and replace `<level>/<platform>/*.png`.
|
||||
|
||||
## History
|
||||
|
||||
- **D531 (2026-10-07):** `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.
|
||||
Fixed (loop now re-pins the canonical save per level; `cd "$ROOT"` added)
|
||||
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
|
||||
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-
|
||||
identical). The "canonical eep" provenance claim was wrong — see D531.
|
||||
|
||||
- **D168 (2026-09-01):** the `GE_PCDUMP` PPM writer emitted `glReadPixels` rows
|
||||
unreversed, so every capture — and this baseline set — was vertically
|
||||
flipped. Writer fixed (`port/fast3d/gfx_opengl.cpp`); this set regenerated
|
||||
@@ -51,3 +104,42 @@ capture with the recipe above and replace `<level>/<platform>/*.png`.
|
||||
`verify.sh` no longer hard-kills (taskkill of every `ge007.x86_64.exe`,
|
||||
including a maintainer's game); it quits via `GE_QUITFRAME` and only stops
|
||||
its own PID as a watchdog fallback.
|
||||
- **2026-10-04 (P7):** first `<level>/linux/` set captured (21 levels, the
|
||||
recipe above verbatim) on a real-GL Linux box; `verify.sh`'s linux pixcount/
|
||||
framediff skip is now driver-conditional (a `glxinfo` renderer probe, D521)
|
||||
-- llvmpipe/softpipe/swrast still skip, a real driver runs the full pixel
|
||||
gate. Structural framediff linux-vs-win: 19/21 clean; Dam (frame 900, the
|
||||
flyby) and Streets (frame 1200, a diagonal region delta) fail. Exact over-
|
||||
tol2: 1.1-6.6% on 17 levels; Archives 20-24.6% (a constant ~20 delta over
|
||||
the top 3 grid rows only), Cuba 10.7-26.4% (uniform d 3-6, cell means <=
|
||||
11: dither noise floor), Jungle 5.9-6.6%, Streets 1200 9.6%. See D521.
|
||||
- **2026-10-05 (D522/D523 re-base):** the win set's window moved on six levels —
|
||||
Surface 2 / Streets / Depot / Cradle / Frigate to `1000-1400:200`, Dam to
|
||||
`1500-1700:100`. Measured, same build, honest sequential passes: at 900 those
|
||||
levels fail the per-pixel limit (Dam 6.542% @900 worst_cell 12.11, Surface 2
|
||||
3.752%, Streets 1.001%, Depot 1.267%, Cradle 1.754%, Frigate 8.391% on a later
|
||||
run) and Dam's 900 is still flyby (run-to-run 3.6-3.8% over tol 2, maxchan 168);
|
||||
Dam's 1200 looked settled on two runs (0.000%) then diverged 5.761% on a third,
|
||||
so it moved to 1500-1700 (1500/1700 0.000% across three runs, 1600 keeps ~40
|
||||
moving pixels at 0.013%). At the new stems three independent runs agree within
|
||||
tol 2. `verify.sh` now pins the save file (D523). **The `<level>/linux/` set was
|
||||
stale everywhere**, not only on those six levels: P7's 2026-10-04 round ran with
|
||||
`data/ge007.eep` cleared, and D523 measures that save-file *presence* moves ~every
|
||||
level (Archives 21.557% with none, 0.175625 worst_cell with any of four saves).
|
||||
- **2026-10-05 (P7b, linux re-round):** the box re-captured all 21 levels (63 frames,
|
||||
21/21 rc=0, frames=3, 25-29 s each) at the windows `verify.sh` carries, with the
|
||||
save pinned present, on the same real-GL box (Mesa 23.0.3 / Intel HD 3000). The
|
||||
set is now one frame per recipe stem on every level, both platforms. Cross-platform
|
||||
framediff (linux vs win, tol 2): **structural 21/21 PASS** (worst cell mean 4.43
|
||||
on streets; phash <= 6 except cradle at 40, a hash tie with cell means 1.17), exact
|
||||
**0.348-3.983%** on the 20 non-Cuba levels (Cuba 23.7-36.3%, structural tier only)
|
||||
— about half the stale set's 1.142-6.958%. **D521's two residual bands are closed
|
||||
by this measurement:** Archives 19.956-24.554% (cell means to 50.8, rows 0-2) 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) — both were the save-state
|
||||
difference, not a renderer. Dam's 900 (92.863%, phash 83) is out of the set; at
|
||||
1500-1700 Dam measures 2.964-3.443%. **Caveat:** the box's own sweep is crash-detect
|
||||
only — `glxinfo -B` fails there (rc 255, "unable to open display :0" from a non-login
|
||||
ssh shell) so `verify.sh`'s fail-closed probe keeps the pixcount/framediff skip even
|
||||
though the game's readback works; the cross-platform numbers are computed on Windows
|
||||
against the merged linux PNGs. See D525.
|
||||
|
After Width: | Height: | Size: 347 KiB |
|
After Width: | Height: | Size: 345 KiB |
|
After Width: | Height: | Size: 346 KiB |
|
Before Width: | Height: | Size: 334 KiB After Width: | Height: | Size: 344 KiB |
|
Before Width: | Height: | Size: 332 KiB After Width: | Height: | Size: 343 KiB |
|
Before Width: | Height: | Size: 333 KiB After Width: | Height: | Size: 344 KiB |
|
After Width: | Height: | Size: 291 KiB |
|
After Width: | Height: | Size: 291 KiB |
|
After Width: | Height: | Size: 291 KiB |
|
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 278 KiB |
|
Before Width: | Height: | Size: 274 KiB After Width: | Height: | Size: 279 KiB |
|
Before Width: | Height: | Size: 273 KiB After Width: | Height: | Size: 278 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
After Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 169 KiB |
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 169 KiB |
|
Before Width: | Height: | Size: 157 KiB After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
After Width: | Height: | Size: 226 KiB |
|
Before Width: | Height: | Size: 212 KiB After Width: | Height: | Size: 224 KiB |
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 225 KiB |
|
Before Width: | Height: | Size: 211 KiB After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
After Width: | Height: | Size: 235 KiB |
|
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 232 KiB |
|
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 232 KiB |
|
Before Width: | Height: | Size: 218 KiB After Width: | Height: | Size: 232 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
After Width: | Height: | Size: 255 KiB |
|
Before Width: | Height: | Size: 237 KiB After Width: | Height: | Size: 251 KiB |
|
Before Width: | Height: | Size: 238 KiB After Width: | Height: | Size: 251 KiB |
|
Before Width: | Height: | Size: 238 KiB After Width: | Height: | Size: 251 KiB |
|
After Width: | Height: | Size: 223 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
After Width: | Height: | Size: 222 KiB |
|
Before Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 220 KiB |
|
Before Width: | Height: | Size: 213 KiB After Width: | Height: | Size: 219 KiB |
|
After Width: | Height: | Size: 219 KiB |
|
Before Width: | Height: | Size: 213 KiB |
|
After Width: | Height: | Size: 280 KiB |
|
After Width: | Height: | Size: 284 KiB |
|
After Width: | Height: | Size: 281 KiB |
|
Before Width: | Height: | Size: 267 KiB After Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 271 KiB After Width: | Height: | Size: 287 KiB |
|
Before Width: | Height: | Size: 267 KiB After Width: | Height: | Size: 286 KiB |
|
After Width: | Height: | Size: 283 KiB |
|
After Width: | Height: | Size: 284 KiB |
|
After Width: | Height: | Size: 284 KiB |
|
Before Width: | Height: | Size: 253 KiB |
|
Before Width: | Height: | Size: 252 KiB |
|
Before Width: | Height: | Size: 255 KiB After Width: | Height: | Size: 259 KiB |
|
After Width: | Height: | Size: 260 KiB |
|
After Width: | Height: | Size: 260 KiB |
|
After Width: | Height: | Size: 214 KiB |
|
After Width: | Height: | Size: 215 KiB |
|
After Width: | Height: | Size: 215 KiB |
|
Before Width: | Height: | Size: 200 KiB |
|
After Width: | Height: | Size: 210 KiB |
|
Before Width: | Height: | Size: 202 KiB After Width: | Height: | Size: 211 KiB |
|
After Width: | Height: | Size: 211 KiB |