forked from Ports/goldeneye-pc-port
Deck/Windows playtest fixes: fog rework (D540) and RSP-style CPU near-plane clip with per-vertex N64 fog (D543), with fog recomputed on near-plane-clipped vertices as the RSP does, so the haze around the player stays steady (D553); colour-combiner clamp for banded walls (D548); rocket and thrown-item stack overflows (D545, D549); menu double-trigger mitigation (D541); Dam ending first-shot camera swivel restored by removing an old port look-at workaround (D552); settings standardisation (D546), live All unlocked (D547), F10 mouse (D544); static window title and the project mark (the in-game crosshair shape, site gold) as window/taskbar icon and favicon (D550). Reference frames re-based on Windows, Linux and Steam Deck (A/B-confirmed); capture_p7.sh runs on Windows. Opt-in update check, off by default (D551: one HTTPS request to GitHub per launch when enabled; winhttp.dll loaded only then; the tag CI job checks the tag against GE007_VERSION). Release notes, ROADMAP, internals and security docs updated; tree-hygiene pass. Refs: D540 D541 D543 D544 D545 D546 D547 D548 D549 D550 D551 D552 D553 D554 D555 D556 D557 D558 D559 D560 D561 D562 D563 D564
203 lines
17 KiB
Markdown
203 lines
17 KiB
Markdown
# AGENTS.md — GoldenEye 007 PC Port
|
||
|
||
## What this is
|
||
|
||
- `n64decomp/007`: WIP decompilation of GoldenEye 007 (N64), byte-matches US/EU/JP ROMs.
|
||
- Active work: **PC port** modelled on the Perfect Dark PC port (same Rare "Indy" engine family).
|
||
- **Reference docs:** `docs/internals.md` — architecture, GE-specific RSP deltas, phased plan (§1–§10). `docs/dev/findings.md` — the `Dxx` finding log (§F + §H). **Look up findings via `docs/dev/findings-index.csv` (label, one-liner, status; regenerate with `tools_pc/gen_findings_index.py`), then read only the specific `## Dxx` entry (multi-pass labels like D202/D176(a) have large sections — grep within the section or read with offset/limit, don't slurp it whole). Never linear-read.** `docs/porting-notes.md` — the recurring N64→PC bug classes (dense; skim the headers, read what's relevant).
|
||
- **Open work / known issues:** `docs/ROADMAP.md` — the single tracker; add new items there, never start a new backlog/roadmap file.
|
||
- **Current status:** the README "Status" section (`docs/dev/LEVEL-STATUS.md` is a historical per-level sweep record). Current task + environment: `docs/HANDOFF.md` (a rolling local working file — may be absent in a fresh clone; fall back to the README "Status" section).
|
||
- **Dispatching subagents?** `docs/dev-process.md` — task budgets/deadlines, file partitioning, pre-flight, the standard brief template. Every investigation subagent reads `docs/porting-notes.md` first and appends to it.
|
||
|
||
## Public site pages (`docs/`) — presentation layer, not a source of truth
|
||
|
||
The repo's homepage and sub-pages are published by GitHub Pages; the local
|
||
preview is `node scratch/build_preview.mjs site` → `scratch/_site/` (served
|
||
with `node scratch/serve_preview.mjs 8777`). The site is a **presentation
|
||
layer over the reference docs, kept in a separate area, and allowed to go
|
||
stale** — never let it poison a work item:
|
||
|
||
- **Pure site pages** — `docs/index.md`, `documentation.md`,
|
||
`the-software-rsp.md`, `the-asset-pipeline.md`, `framerate-and-pacing.md`,
|
||
`input-and-aim.md`, `saves.md`, `security.md`, `fidelity.md` —
|
||
summarise the ROADMAP / README / finding log / release notes. **Do not
|
||
consult them as work information, cross-check against them, or treat a
|
||
mismatch with `docs/ROADMAP.md` or the finding log as a bug** (that is
|
||
the expected, independent-staleness case). Update them only when the task
|
||
says so, and source every fact from the ROADMAP / README / finding log —
|
||
never the other way round. The footer "last updated" date in
|
||
`docs/_layouts/default.html` is the same class of site-only detail.
|
||
- **Shared files** — `docs/internals.md`, `porting-notes.md`,
|
||
`building.md`, `dev-process.md`, `dev/agentic-development.md` are
|
||
authoritative working docs that the site publishes verbatim; edit them
|
||
normally as part of dev work and the site picks up the change for free.
|
||
|
||
Keep the split intact: no working notes migrate into pure site pages, and
|
||
never edit a shared doc merely to make the site read better — if the site is
|
||
stale, fix the site page (or the fact upstream, as its own task).
|
||
|
||
## Non-negotiables
|
||
|
||
1. **N64 build untouched.** `Makefile`, `tools/`, `rsp/`, `ld/` belong to the N64 build. Never modify them for the PC port.
|
||
2. **Game logic is unmodified.** The decomp's control flow and behavior are ground truth for 1:1 fidelity — never change them. All N64 *hardware* dependencies are satisfied by the `port/` layer; if a game file seems to need a behavioral change, stop and check the exception below before assuming the fix belongs in `port/`. **Diagnosis is never restricted — only the fix.** Trace a bug as deep into `src/game` state as the evidence leads, and name the exact struct/field/logic responsible, before deciding which of three buckets it falls in (full framing: `docs/dev-process.md`). **Narrow exception (ABI/layout only):** the 32→64-bit pointer-width transition forces a small class of mechanical, semantics-preserving edits that cannot be isolated in `port/` — **any struct-layout or pointer-width-driven misread caused by 32→64-bit widening**, whether in a ROM-serialized record (a struct with a 32-bit-pointer field misaligns when read as 64-bit) or a **live runtime struct/union** (a raw-byte offset alias into a union arm whose true field shifted because an earlier member in the same union widened — the D209/D210/D255 pattern; see `docs/porting-notes.md` §A1 for the full catalogue and its diagnostic tells). These follow the PD ground-truth pattern (store the embedded address as `u32` and cast to a real pointer at the use site; or read the correctly-named/typed field instead of a raw-offset/mistyped alias), change no logic or behavior, and are each documented in `docs/dev/findings.md` §F/D3x, cross-tagged to §A1 where that pattern applies. **A genuine behavioral difference** — the decomp's byte-identical code, given verified-correct inputs, still diverging from real N64 behavior — is not covered by this exception and requires the rule-2 sign-off procedure (`docs/dev-process.md`) before any `src/game` edit. No other game-code edits are permitted.
|
||
3. **Region macros mirror the Makefile.** `CMakeLists.txt` `REGION_DEFS` must match the N64 Makefile's per-region macro set exactly (finding A1). Divergence = silent branch divergence + link failures.
|
||
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`
|
||
(63 frames × 3 platforms, 189 total, nested per level at
|
||
`tools_pc/golden/<level>/{win,linux,deck}/`; the `deck` set added
|
||
2026-10-05 with maintainer approval), 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 |
|
||
|---|---|
|
||
| `docs/internals.md` | Architecture + RSP deltas + phased plan (§1–§10). Reference, not a linear read. |
|
||
| `docs/dev/findings.md` | The `Dxx` finding log (§F/§H); lookups via `docs/dev/findings-index.csv`. |
|
||
| `CMakeLists.txt` | PC build (parallel to the N64 Makefile). Source list + `REGION_DEFS` live here. |
|
||
| `port/src/` | Shims: `libultra.c` (OS API; also shims the scheduler's hardware leaf calls and runs the software RSP inline — the game's real `src/sched.c` is compiled), `n64stubs.c` (boot/TLB/FPU/rmon), `random.c` (PRNG ported verbatim from `random.s`), `ucode.c` (microcode segment markers), `main.c`, `video.c`, … |
|
||
| `port/fast3d/` | Software RSP (adapted from the PD port). The main Phase 2 work. |
|
||
| `rsp/graphics/gmain.s` | GE's RSP ucode — ground truth for GBI/CC/RM. |
|
||
| `reference/mouse-injector/README.md` | **Stub only** — the vendored GEPD-Edition Mouse Injector source (GPLv2) was removed from the public repo 2026-09-19 (license hygiene: GPL code + prebuilt binary in an MIT project; it was never compiled here). The stub records provenance + how to re-vendor locally (gitignored). The ported mouse-aim model lives in `port/src/input.c` (D194 lineage); design record: `docs/dev/GEPD-INPUT-PLAN.md`. |
|
||
| A local **Perfect Dark PC port** checkout ([fgsfdsfgs/perfect_dark](https://github.com/fgsfdsfgs/perfect_dark)) | **Standing reference** — consult it whenever a work item has a PD analogue (same Rare engine family): port-layer ground truth (`port/fast3d/`, crash/system/video), plus copy candidates `port/src/preprocess/` (N64→PC asset conversion; `filemodel.c` is the D43 near-analogue) and `mixer.c`/`input.c`/`fs.c`. Port-layer files only; same family ≠ identical format — validate per field. Full audit: `docs/internals.md` §2.4. |
|
||
|
||
## Build
|
||
|
||
```sh
|
||
./build-pc.sh ntsc-final # or pal-final / jpn-final
|
||
```
|
||
|
||
Needs CMake + SDL2 + zlib + OpenGL, and must run from the MSYS2 MINGW64 shell
|
||
(see `build-pc.sh` header and `docs/building.md`). ROM goes in `./data/`
|
||
(not distributed); assets must be extracted from it first (`docs/building.md`).
|
||
|
||
**Windows build environment (three recurring failure modes — diagnose in this order):**
|
||
|
||
1. **`Cannot create temporary file in C:\Windows\: Permission denied`** at the
|
||
link step. The PE toolchain (ninja → cmd → gcc/ld) needs a writable
|
||
TMP/TEMP; the msys→native env conversion drops it in non-login shells
|
||
(agent harnesses; the `C:/msys64` tree here was built for a relocated MSYS2
|
||
root — `<msys>` in the placeholder legend — so its path conversion is
|
||
unreliable). `build-pc.sh` now **self-heals**: it
|
||
probes a native child's TMP (via a file — piped `cmd.exe` stdout is
|
||
unreliable under msys console emulation) and, if broken, re-runs
|
||
cmake+build under PowerShell with `TMP`/`TEMP` set natively (the
|
||
native→native boundary passes env through intact; only msys→native is
|
||
broken). If you run cmake/ninja *by hand* from a broken shell: run them
|
||
from cmd/PowerShell with `C:\msys64\mingw64\bin` on PATH.
|
||
**Guard revision (2026-09-28):** the re-exec writes a self-contained
|
||
`.ps1` to a temp file and runs `powershell -File` with plain path/word
|
||
args only (the msys→native argv conversion mangles `$`-bearing
|
||
`-Command` strings; a file write is conversion-free). Inside: PATH is
|
||
built explicitly (never trust the inherited value); TMP is chosen from
|
||
writable candidates (`%LocalAppData%\Temp` first —
|
||
`[System.IO.Path]::GetTempPath()` honours the inherited *broken* TMP,
|
||
observed as `C:\Windows\`); every native step self-logs to
|
||
`build-pc/ge007-native-reexec-{diag,cmake,build}.log`; a null
|
||
`$LASTEXITCODE` ("did not execute") is a distinct failure (exit 2/3) —
|
||
in PS 5.1 a piped native command errors out non-terminating and never
|
||
sets it, so an unguarded `if ($LASTEXITCODE -ne 0)` passes vacuously.
|
||
In `.ps1`, `-DROMID=$Var` is a literal (bare tokens don't expand) — it
|
||
must be `"-DROMID=$Var"`. Launch powershell plainly — `env -i` before it
|
||
breaks the nested cmake launch (verified 2026-09-28).
|
||
**FIXED 2026-09-30 (`fix/build-pc-tmp-reexec`):** the heredoc `.ps1`
|
||
was byte-identical to a hand-extracted copy — not line endings. Three
|
||
real bugs: (a) the TMP probe's inline `cmd //c "... \"path\" ..."` never
|
||
ran (msys re-escapes embedded quotes as `\"`, which cmd rejects), so
|
||
*every* build took the re-exec, and it only tested that TMP existed;
|
||
(b) the `.ps1`'s toolchain dirs were derived from `command -v cmake`,
|
||
which in Git Bash/agent shells is a pip cmake (no MSYS2 toolchain) —
|
||
the hand-run worked because it passed explicit `C:\msys64\...` paths;
|
||
(c) `/tmp` + `cygpath` disagree when Git Bash and MSYS2 `usr/bin` are
|
||
both on PATH, so the `.ps1` could be written to one `/tmp` and
|
||
powershell pointed at the other (it then exits 0 having done nothing).
|
||
Now: the MSYS2 `mingw64/bin` is located and validated (override:
|
||
`GE_MSYS2_ROOT`) and prepended to PATH; the probe is a `.cmd` file that
|
||
tests TMP *writability*; all native-facing files live in the build dir
|
||
with paths from bash's own `pwd -W`. Verified from an agent Git Bash
|
||
shell: fresh + incremental builds, both on the direct path and with a
|
||
forced unwritable TMP (`TMP=C:\Windows\`) through the re-exec.
|
||
**FIXED 2026-09-30 (`fix/build-pc-reexec-noop`): the re-exec exited rc=2
|
||
with the diag log ending `cmake ran LASTEXITCODE=[]`.** From a shell with
|
||
MSYS2 `usr/bin` first on PATH, powershell inherits a stripped ~13-var env
|
||
with `PATHEXT=.CPL` and no ComSpec/TMP/TEMP, so `& cmake.exe` silently
|
||
launches nothing. The `.ps1` now restores PATHEXT and ComSpec. In that
|
||
shell the re-exec itself is legitimately needed (no TMP), so the probe
|
||
is correct. Verified: MSYS2-PATH shell, forced `TMP=C:\Windows\`, and a
|
||
no-change incremental run all exit 0 with a fresh exe.
|
||
**FIXED 2026-10-04 (`fix/build-arch-tag`, D515):** a FRESH build dir
|
||
configured through the re-exec cached `CMAKE_HOST_SYSTEM_PROCESSOR ""` /
|
||
`CMAKE_SYSTEM_PROCESSOR ""` (the stripped env also drops
|
||
`PROCESSOR_ARCHITECTURE`, which CMake reads for the host processor — same
|
||
class as PATHEXT/ComSpec), so `cmake/TargetArch.cmake` produced an empty
|
||
arch tag: `Target arch: (64bit=FALSE)`, binary named `ge007..exe`
|
||
(a correct 64-bit build; only the name was wrong — found during T7, D513).
|
||
The `.ps1` now restores `PROCESSOR_ARCHITECTURE` from the machine
|
||
environment (fallback `AMD64`; `SystemRoot`/`windir` likewise) and logs
|
||
the four vars to the diag log; `cmake/TargetArch.cmake` falls back to
|
||
`${CMAKE_C_COMPILER} -dumpmachine` when `CMAKE_SYSTEM_PROCESSOR` is empty
|
||
and FATAL_ERRORs instead of emitting an untagged binary. Existing build
|
||
dirs are unaffected (value already cached); a dir configured before the
|
||
fix picks up x86_64 on its next in-place reconfigure.
|
||
2. **`cannot open output file ge007.x86_64.exe: Permission denied`.** A
|
||
**running** `ge007.x86_64.exe` locks the output file (Windows rule; you
|
||
can't relink over a live PE). Check with `Get-Process | Where-Object {
|
||
$_.ProcessName -like '*ge007*' }` and close the game before rebuilding.
|
||
An agent must not kill the user's game process to make a link succeed.
|
||
3. **`cc1.exe: ... libmpfr-6.dll: cannot open shared object file`.** The
|
||
calling PATH lacks `C:\msys64\mingw64\bin` (the gcc driver finds cc1 via
|
||
its own directory, but the child needs the mingw DLL dir on PATH). In an
|
||
msys shell: `export PATH="/c/msys64/mingw64/bin:/c/msys64/usr/bin:$PATH"`
|
||
before building.
|
||
|
||
## Verification ritual (after any build-affecting change)
|
||
|
||
1. **Undefined symbols.** Every symbol referenced by the compiled set (see `CMakeLists.txt`: `SRC_GAME`, `SRC_ENGINE`, `SRC_LIBAUDIO`, `SRC_LIBULTRARE_AUDIO`, `SRC_LIBULTRARE_DATA`, `SRC_GU`, `SRC_PORT*`) must be defined exactly once in the compiled set or in `port/`. Symbols that live in EXCLUDED files (`libultra/io/*`, `libultrare/io/*` except `vitbl.c`, `libultra/os/*`, `libultrare/os/*`, `sched.c`, `rmon.c`, `vi.c`, `src/*.s`) must be provided by `port/src/libultra.c`, `n64stubs.c`, `random.c`, or `ucode.c`.
|
||
2. **Duplicates.** No symbol defined twice across the compiled set (watch `sp_*` stacks, `rmon*`, `os*` shims, segment markers).
|
||
3. **Syntax.** Every touched file must parse; `./build-pc.sh` is the final word.
|
||
|
||
Run `/linkcheck` for this sweep. Record new findings in `docs/dev/findings.md` §F/§H style (next `Dxx` label after the last used) and add the label to the §F index.
|
||
|
||
## Phase status
|
||
|
||
Do not keep a status summary here — it goes stale. Open work, known issues
|
||
and decisions owed: **`docs/ROADMAP.md`** (the single tracker). Current
|
||
release state: the README "Status" section.
|