forked from Ports/goldeneye-pc-port
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
172 lines
15 KiB
Markdown
172 lines
15 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.
|
|
|
|
## 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`, 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 `D:/M/msys64`,
|
|
so its 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.
|