forked from Ports/goldeneye-pc-port
Compare commits
1
Commits
4bc46d3434
...
v0.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
66e38e27d7 |
No files matched your search
@@ -1,15 +1,20 @@
|
||||
## GoldenEye 007 PC Port — alpha
|
||||
## GoldenEye 007 PC Port — v0.1.0 alpha
|
||||
|
||||
> ⚠️ **Very early research alpha. Not a playable game.**
|
||||
> The intro and menus render and all 21 solo missions load, but:
|
||||
> - **no audio** (Phase 3, not started);
|
||||
> ⚠️ **Research alpha. Playable, not polished.**
|
||||
> The full single-player campaign runs. In a full-campaign playtest on this
|
||||
> build, **19 of 21 missions were completable start to finish**. Known issues:
|
||||
> - **no audio** (Phase 3, not started) — the game runs silent;
|
||||
> - **two levels crash mid-mission** — Bunker ii and Statue (one root cause, D191);
|
||||
> - **AI characters move too slowly**, which breaks Cradle (the final level)
|
||||
> via Trevelyan's scripted behavior (D193);
|
||||
> - **cutscenes frequently glitch** — skipped, wrong camera, misplaced actors (D148/D160);
|
||||
> - some front-end 3D models are broken (the spinning Nintendo logo, the
|
||||
> MISSION COMPLETE / mode-select models);
|
||||
> - outdoor levels render with a **black sky**;
|
||||
> - expect crashes and glitches once past the level intro.
|
||||
> - mouse aim and some textures/transparency have rough edges.
|
||||
>
|
||||
> This is a technical demo of the porting work. If you want to *play* GoldenEye
|
||||
> on PC, use one of the Xbox 360 recompilation projects instead — see the
|
||||
> If you just want to *play* GoldenEye on PC today, use one of the Xbox 360
|
||||
> recompilation projects instead — see the
|
||||
> [project README](https://github.com/jkdansereau/goldeneye-pc-port#how-this-differs-from-the-other-goldeneye-pc-projects).
|
||||
|
||||
### Downloads
|
||||
|
||||
@@ -124,12 +124,12 @@
|
||||
<div class="rep">REPORT:</div>
|
||||
|
||||
<div class="rows">
|
||||
<div class="statusline">Status: <span class="status">PHASE 2 OF 4 — RENDERING</span></div>
|
||||
<div class="statusline">Status: <span class="status">ALPHA v0.1.0 — PHASE 2 OF 4</span></div>
|
||||
<div class="stats">
|
||||
<span class="k">Statistics:</span><span>21 / 21 solo missions load & render</span>
|
||||
<span class="k">Statistics:</span><span>19 / 21 solo missions completable in a full playthrough</span>
|
||||
<span></span><span>~230 game-code units, compiled unmodified</span>
|
||||
<span></span><span>~17k lines C/C++ · ~6k Python (port layer + tools)</span>
|
||||
<span></span><span>162 bugs root-caused · built in ~2 weeks, part-time</span>
|
||||
<span></span><span>~190 bugs root-caused · built in ~3 weeks, part-time</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -68,6 +68,14 @@ build/*
|
||||
build-*/*
|
||||
notes/*
|
||||
|
||||
# Local working / planning notes — rolling drafts, not published, not tracked.
|
||||
# docs/HANDOFF.md and docs/BACKLOG.md are the long-standing pair (also in
|
||||
# .git/info/exclude); docs/dev/notes/ is the drop for everything else
|
||||
# (roadmaps, session plans, scratch triage).
|
||||
docs/HANDOFF.md
|
||||
docs/BACKLOG.md
|
||||
docs/dev/notes/
|
||||
|
||||
# PC port (see CMakeLists.txt / port/)
|
||||
build-pc/
|
||||
tools_pc/*.exe
|
||||
|
||||
@@ -1,21 +1,25 @@
|
||||
# GoldenEye 007 PC Port
|
||||
|
||||
[](https://github.com/jkdansereau/goldeneye-pc-port/actions/workflows/ci.yml)
|
||||
-orange)
|
||||

|
||||
-orange)
|
||||

|
||||

|
||||

|
||||
|
||||
**A work-in-progress native PC port of _GoldenEye 007_ (Rare, 1997, Nintendo 64),
|
||||
built on the [GoldenEye 007 decompilation](https://github.com/n64decomp/007).**
|
||||
It is first and foremost a **research project on AI-agent collaboration in a
|
||||
large, unfamiliar, low-level codebase** — how far two coding agents (a local
|
||||
**A native PC port of _GoldenEye 007_ (Rare, 1997, Nintendo 64), compiled from
|
||||
the [GoldenEye 007 decompilation](https://github.com/n64decomp/007) — the
|
||||
original N64 game running from reconstructed source, not the Xbox 360 remaster.**
|
||||
The first alpha, **[v0.1.0](../../releases)**, is out for Windows and Linux: it
|
||||
boots, renders the full intro and front end, and runs all 21 solo missions — in
|
||||
a full-campaign playtest 19 of the 21 were completable start to finish (two
|
||||
levels still crash). **There is no audio yet** and rough edges remain; this is
|
||||
Phase 2 of 4. See [Status](#status) and [Background](#background).
|
||||
|
||||
It is also a **research project on AI-agent collaboration in a large,
|
||||
unfamiliar, low-level codebase** — how far two coding agents (a local
|
||||
open-weight model and Claude), driven by one person part-time, can be pushed
|
||||
through ~230 translation units of unmodified big-endian MIPS game code and made
|
||||
to run on a 64-bit desktop. **Current status: Phase 2 of 4 (rendering).** The
|
||||
port boots, renders the full intro and front end, and loads all 21 solo
|
||||
missions; there is no audio yet and it is not a playable game. See
|
||||
[Status](#status) and [Background](#background).
|
||||
to run on a 64-bit desktop. See [Background](#background).
|
||||
|
||||
Technically, it follows the architecture of the
|
||||
[Perfect Dark PC port](https://github.com/fgsfdsfgs/perfect_dark) — the same
|
||||
@@ -38,8 +42,22 @@ timers, save storage) is shimmed in a dedicated `port/` layer.
|
||||
Jungle → Archives.</em>
|
||||
</p>
|
||||
|
||||
## Quick start
|
||||
|
||||
You supply your own **NTSC-U GoldenEye 007 N64 ROM** (`.z64`, big-endian) — no
|
||||
ROM or game asset is included or distributed. Then:
|
||||
|
||||
1. Download the Windows or Linux bundle from [Releases](../../releases) and unpack it.
|
||||
2. Make a `data/` folder next to the executable and drop the ROM in as `ge007.ntsc-final.z64`.
|
||||
3. Run the one-time asset step: `python3 prepare-assets/prepare-assets.py` (Python 3.8+, stdlib only).
|
||||
4. Launch the executable from that folder.
|
||||
|
||||
Building from source instead: see [Building](#building). Read the
|
||||
[Status](#status) caveats first — this is an early alpha.
|
||||
|
||||
## Contents
|
||||
|
||||
- [Quick start](#quick-start)
|
||||
- [Background](#background)
|
||||
- [How this differs from the other GoldenEye PC projects](#how-this-differs-from-the-other-goldeneye-pc-projects)
|
||||
- [Status](#status)
|
||||
@@ -73,7 +91,7 @@ It used two agents:
|
||||
- **Claude** (via **Claude Code** — mostly **Sonnet 5**, with **Opus 5** as an
|
||||
escalation tier for the hardest bugs), which joined on 27 Aug 2026 for a
|
||||
collaborative phase: the 21-level load/render/no-crash sweep, the ABI/layout
|
||||
finding catalogue, the SDL input layer, and the front-end flow.
|
||||
finding catalog, the SDL input layer, and the front-end flow.
|
||||
|
||||
The two **handed work back and forth** through shared written artifacts:
|
||||
|
||||
@@ -99,11 +117,11 @@ mid-problem, the local model picked the task up from that state and continued.
|
||||
|
||||
| Metric | Value |
|
||||
|---|---|
|
||||
| Timeline | ~2 weeks, one person part-time |
|
||||
| Commits | ~223 |
|
||||
| Root-caused bugs logged | 162 (`D1`–`D169`; some later merged or withdrawn) |
|
||||
| Handoff sessions | ~33 |
|
||||
| ABI edits to game code | 63 files, all `#ifdef PORT` |
|
||||
| Timeline | ~3 weeks, one person part-time |
|
||||
| Commits | ~320 |
|
||||
| Root-caused bugs logged | ~190 (`D1`–`D196`; some later merged or withdrawn) |
|
||||
| Handoff sessions | ~49 |
|
||||
| ABI edits to game code | 69 files, all `#ifdef PORT` |
|
||||
| Port layer / tooling | ~17k lines C/C++ · ~6k lines Python |
|
||||
| Effort split (milestone-weighted) | ~60% Claude · ~40% local model |
|
||||
|
||||
@@ -114,6 +132,7 @@ asset converters — then both agents ran the debugging phase together:
|
||||
- **Day 8** — the whole intro renders
|
||||
- **Day 13** — all 21 solo missions run crash-free
|
||||
- **Day 14** — front end playable end to end
|
||||
- **Week 3** — v0.1.0 alpha; a full-campaign playtest completes 19 of 21 missions
|
||||
|
||||
Full write-up, timeline chart, and an honest "what worked / what didn't":
|
||||
[`docs/dev/agentic-development.md`](docs/dev/agentic-development.md). The
|
||||
@@ -135,7 +154,7 @@ remaster* — a different game, a different codebase, no shared code with this.
|
||||
| **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 |
|
||||
| **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** | WIP, Phase 2 — renders and loads all 21 levels; no audio; not playable end to end | Playable full game, higher frame rates, online multiplayer |
|
||||
| **Status** | WIP, Phase 2, alpha v0.1.0 — runs all 21 levels, 19/21 completable in a full playthrough; no audio; rough edges | Playable full game, higher frame rates, online multiplayer |
|
||||
| **Why it exists** | A [case study in AI-agent collaboration](#background) on a hard low-level codebase; the port is the target, not a product | A polished, playable PC release of the remaster |
|
||||
|
||||
**If you just want to play GoldenEye on PC today, use one of the recompilation
|
||||
@@ -146,54 +165,64 @@ that work was driven by AI agents.
|
||||
## Status
|
||||
|
||||
> [!WARNING]
|
||||
> **Not playable yet.** This is a work-in-progress research port. The intro and
|
||||
> menus render and all 21 solo levels load, but it is **not** a complete or
|
||||
> reliably playable game: there is **no audio**, the front-end 3D models and
|
||||
> some text are broken, input has known rough edges, and you should expect
|
||||
> crashes and glitches once you are past the level intro. Treat it as a
|
||||
> technical demo of the porting work, not a way to play GoldenEye.
|
||||
> **Alpha — playable, not polished.** This is a work-in-progress research port.
|
||||
> It runs the full single-player campaign, but there is **no audio**, some
|
||||
> front-end 3D models and cutscenes are broken, AI characters move too slowly,
|
||||
> input has known rough edges, and two levels (Bunker ii, Statue) still crash.
|
||||
> Treat it as an early alpha of the porting work, not a finished way to play
|
||||
> GoldenEye.
|
||||
|
||||
**Phase 2 of 4 (rendering).** The port boots, renders, and is playable through
|
||||
the front end into the early game. It is not finished and it is not stable.
|
||||
**Phase 2 of 4 (rendering). First alpha: [v0.1.0](../../releases).** The port
|
||||
boots, renders, and plays through the front end and the campaign. It is not
|
||||
finished and it is not fully stable.
|
||||
|
||||
**Working**
|
||||
|
||||
- Boot → Rare/Nintendo logos → gun-barrel → cast intro, fully rendered.
|
||||
- Front end: main menu → mission select → difficulty → briefing → mission start.
|
||||
- **All 21 solo missions load, render, and survive an unattended play window
|
||||
without crashing** (see [`docs/dev/LEVEL-STATUS.md`](docs/dev/LEVEL-STATUS.md)).
|
||||
- **All 21 solo missions load and render.** In a full-campaign playtest on the
|
||||
packaged Windows build, **19 of 21 were completable start to finish**; Bunker
|
||||
ii and Statue crash mid-level (one root cause, tracked as D191). See
|
||||
[`docs/dev/LEVEL-STATUS.md`](docs/dev/LEVEL-STATUS.md).
|
||||
- Software RSP (fast3d): textured world geometry, skeletal characters, HUD,
|
||||
the GE-specific color-combiner / render modes and `G_TRI4`.
|
||||
- Input: keyboard + mouse (with mode-aware mouse-look) and SDL game
|
||||
controllers, mapped onto the N64 pad. Tunable via `ge007.ini`.
|
||||
- File-backed EEPROM saves.
|
||||
- **Windows and Linux** (`x86_64`). Windows is the primary development and
|
||||
playtest path; the Linux build boots, renders, and passes the level sweep,
|
||||
with far less human playtime.
|
||||
|
||||
**Not yet working**
|
||||
|
||||
- **Audio** — not implemented (Phase 3). The game runs silent.
|
||||
- **AI pacing** — scripted and combat AI characters travel to their
|
||||
destinations noticeably slower than on N64. This breaks Cradle (the final
|
||||
level) via Trevelyan's scripted progression. Top post-alpha fix (D193).
|
||||
- **Cutscenes** — frequently glitch: skipped, wrong camera, misplaced or
|
||||
hovering actors, wrong timing (D148/D160).
|
||||
- Some front-end 3D models — the spinning Nintendo logo, and the MISSION
|
||||
COMPLETE / mode-select models — are mispositioned or absent. (The
|
||||
gun-barrel Bond intro renders correctly.)
|
||||
- Assorted cosmetic defects (some text layout, a few incomplete asset
|
||||
conversions) are tracked in
|
||||
[`docs/dev/GRAPHICS-BACKLOG.md`](docs/dev/GRAPHICS-BACKLOG.md) and parked
|
||||
below crash/level work.
|
||||
- **Platform testing is Windows-only.** All development and playtesting has
|
||||
been on `x86_64` Windows (MSYS2/MINGW64). The CMake build has Linux and macOS
|
||||
paths and is *intended* to be portable, but the port has had **zero
|
||||
functional testing on Linux or macOS** — the Linux build is provided as-is
|
||||
and unverified at runtime. No ARM testing; no controller rebinding UI; no
|
||||
widescreen.
|
||||
- Outdoor levels render with a **black sky**; assorted other cosmetic defects
|
||||
are tracked in
|
||||
[`docs/dev/GRAPHICS-BACKLOG.md`](docs/dev/GRAPHICS-BACKLOG.md).
|
||||
- No macOS or ARM support; no controller rebinding UI; no widescreen.
|
||||
|
||||
Next up: audio (Phase 3), the AI-pacing fix, and the two remaining level
|
||||
crashes. Cosmetic defects are tracked in
|
||||
[`docs/dev/GRAPHICS-BACKLOG.md`](docs/dev/GRAPHICS-BACKLOG.md).
|
||||
|
||||
## Download
|
||||
|
||||
There are no pre-built binaries yet. The project is Phase 2 WIP with the
|
||||
caveats in [Status](#status) above — nothing runs end to end — so the only way
|
||||
to try it is to build it yourself; see [Building](#building). You supply your
|
||||
own ROM (see [Requirements](#requirements)) and drop it in `data/` before
|
||||
building. Pre-release **Windows x86-64** bundles (engine executable plus
|
||||
runtime DLLs, no ROM or game assets) may be published under
|
||||
[Releases](../../releases) once the port is playable.
|
||||
Pre-built **Windows** and **Linux** `x86_64` bundles are published under
|
||||
[Releases](../../releases), starting with **v0.1.0**. Each contains the engine
|
||||
executable, its runtime libraries, and the one-time `prepare-assets` tool —
|
||||
**no ROM and no game assets**. See [Quick start](#quick-start) for the four
|
||||
steps to get it running, [Requirements](#requirements) for accepted ROMs, and
|
||||
[Status](#status) for the alpha caveats.
|
||||
|
||||
You can also build it yourself; see [Building](#building).
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -293,9 +322,9 @@ touch N64 hardware is redirected into `port/`:
|
||||
- **`port/src/{video,audio,input,fs,romdata,config}.c`** — the SDL2 / OpenGL /
|
||||
filesystem backends.
|
||||
|
||||
The 32→64-bit transition forces a small, catalogued class of mechanical
|
||||
The 32→64-bit transition forces a small, cataloged class of mechanical
|
||||
ABI-only edits to ROM-serialized structs (pointer-width reconciliation); these
|
||||
change no behaviour and are documented individually.
|
||||
change no behavior and are documented individually.
|
||||
|
||||
Where it diverges from the Perfect Dark port: GoldenEye's N64 serialized asset
|
||||
formats (level setup, models, backgrounds) are converted **offline** by a set
|
||||
@@ -395,12 +424,11 @@ GitHub. It follows the same conventions they do:
|
||||
- No official logos, box art, or marketing assets are used. "GoldenEye 007",
|
||||
"007", "James Bond" and related marks belong to their respective owners
|
||||
(Nintendo, Microsoft/Rare, MGM, Danjaq, EON Productions).
|
||||
- **If** pre-built binaries are published under [Releases](../../releases) in
|
||||
the future, they will contain only the engine — the `port/` layer plus the
|
||||
compiled decompilation, with no game data of any kind — bundled with
|
||||
permissively-licensed runtime libraries (SDL2, zlib, the MinGW runtime; their
|
||||
licenses travel in the download). No such release exists yet, and any build —
|
||||
yours or ours — is useless without a ROM you supply.
|
||||
- **Pre-built binaries** published under [Releases](../../releases) contain
|
||||
only the engine — the `port/` layer plus the compiled decompilation, with no
|
||||
game data of any kind — bundled with permissively-licensed runtime libraries
|
||||
(SDL2, zlib, the MinGW runtime; their licenses travel in the download). Any
|
||||
build — yours or ours — is useless without a ROM you supply.
|
||||
|
||||
This project is **not affiliated with, endorsed by, or sponsored by** Nintendo,
|
||||
Rare, Microsoft, MGM, Danjaq, EON Productions, or any rights holder in
|
||||
|
||||
+3
-3
@@ -13,8 +13,8 @@ Stages:
|
||||
files the port loads at runtime. **Required to run.**
|
||||
|
||||
You need a GoldenEye 007 N64 ROM you legally own (`.z64`, big-endian). See the
|
||||
[Requirements table in the README](../README.md#requirements) for accepted
|
||||
versions and hashes.
|
||||
[Requirements table in the README](https://github.com/jkdansereau/goldeneye-pc-port#requirements)
|
||||
for accepted versions and hashes.
|
||||
|
||||
---
|
||||
|
||||
@@ -157,5 +157,5 @@ runtime. `ge007.ini` is written under `data/` on first launch.
|
||||
| `-level_NN` | boot straight into a solo level (e.g. `-level_09` = Bunker 1) |
|
||||
| `GE_PCDUMP="first-last:step"` | dump rendered frames as PPM (debugging) |
|
||||
|
||||
More diagnostic switches are catalogued in
|
||||
More diagnostic switches are cataloged in
|
||||
[`dev/GE-ENV-PROBES.md`](dev/GE-ENV-PROBES.md).
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
# How this port is developed
|
||||
|
||||
Most of this port's work is not writing code — it is **diagnosis**: figuring
|
||||
out which N64 hardware or ABI behaviour a piece of unmodified game code is
|
||||
out which N64 hardware or ABI behavior a piece of unmodified game code is
|
||||
silently relying on, and satisfying it in the `port/` layer without touching
|
||||
the game's logic. A lot of that diagnosis is done with LLM coding agents, and
|
||||
the workflow below is what makes that productive rather than chaotic.
|
||||
@@ -14,7 +14,7 @@ It is adapted from Chris Lewis's write-up
|
||||
[*"Decompiling a Nintendo 64 Game in 84 Days"*](https://blog.chrislewis.au/decompiling-a-nintendo-64-game-in-84-days/)
|
||||
(Snowboard Kids, 2026), an AI-assisted decompilation that ran several times
|
||||
faster than its predecessor. That project bit-matches compiler output;
|
||||
this one matches runtime behaviour, so the mechanics differ — but the
|
||||
this one matches runtime behavior, so the mechanics differ — but the
|
||||
project-structure lessons carry over directly.
|
||||
|
||||
## 1. Every investigation task gets a visible budget
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# GE_* environment probes — consolidated list
|
||||
|
||||
Every `getenv("GE_…")` in the tree, from a grep of `src/` + `port/` on
|
||||
2026-08-31 (master `31d036da`). All are **env-gated**: unset = zero behaviour
|
||||
2026-08-31 (master `31d036da`). All are **env-gated**: unset = zero behavior
|
||||
change, so they are safe to leave in a release build (they only cost a
|
||||
`getenv` on the guarded path). "Dead" below means *the finding it was built
|
||||
for is closed* — the probe is strip-candidate scaffolding, not that it does
|
||||
|
||||
@@ -14,17 +14,19 @@ once the game is playable start-to-finish without crashes.
|
||||
| **D76** | 2D disclaimer screen only partially drawn. | §F D76. |
|
||||
| **D77** | No audible music on PC. | Phase 3 (audio). §F D77. |
|
||||
| ~~**D116 (HUD)**~~ | ~~proportional-font strings + ammo counter mirrored~~ | **CLOSED — NOT A BUG (M-33, §F D168).** Same capture-orientation artifact as D114. |
|
||||
| **D148** | Level-end cinematics don't play. Dam: hitting the exit trigger should show Bond rappelling down the dam (the bungee jump); instead it cuts straight to the mission-complete report. | Scripted cutscene / cinematic-camera + Bond rappel anim. Found M-27 during the first full Dam→Facility playthrough. Progression is unaffected — the report and next briefing load fine. **M-31 (§F D160):** static-traced the Dam end ailists (`ai_24`→`ai_17`, `camera_switch`→`CutsceneRecord` type-46 lookup). Diagnostic `GE_D160=1` shipped (`c95713f5`) — user runs Dam to the exit with it set; the trace pins whether the cutscene AI runs, and whether the propDef-index walk (D122/D132 family) hands `camera_switch` a bogus cutscene record. Leading hypothesis: propDef `sizepropdef` stride desync (medium confidence), not D75 anim-model. |
|
||||
| **D148** | Level-end cinematics don't play. Dam: hitting the exit trigger should show Bond rappelling down the dam (the bungee jump); instead it cuts straight to the mission-complete report. **Broader (M-49 full playthrough): cutscenes are buggy in many places across the game**, not just the Dam ending — the user reports frequent cutscene glitches (skipped, wrong camera, misplaced/hovering actors, timing). Treat D148/D160 as the tracking entry for the whole cutscene-reliability problem, not just Dam. | Scripted cutscene / cinematic-camera + Bond rappel anim. Found M-27 during the first full Dam→Facility playthrough. Progression is unaffected — the report and next briefing load fine. The widespread nature (M-49) points at a systemic cause: the propDef/`sizepropdef` stride desync (D122/D132 family) that misreads `CutsceneRecord` lookups, and/or D75 anim-model + D173 actor-position bugs showing up wherever a cutscene puts a character on screen, and/or the D155/D156/D193 timing family driving cutscene camera/anim pacing. **M-31 (§F D160):** static-traced the Dam end ailists (`ai_24`→`ai_17`, `camera_switch`→`CutsceneRecord` type-46 lookup). Diagnostic `GE_D160=1` shipped (`c95713f5`) — user runs Dam to the exit with it set; the trace pins whether the cutscene AI runs, and whether the propDef-index walk (D122/D132 family) hands `camera_switch` a bogus cutscene record. Leading hypothesis: propDef `sizepropdef` stride desync (medium confidence), not D75 anim-model. |
|
||||
| **D149** | Front-end MISSION COMPLETE / mode-select 3D models (dossier, wallets) render as garbled full-screen geometry or not at all — one of the compiled sub-DLs (seg5+0x9ee4) is malformed, D146 now skips it instead of aborting. | D75 family. §F D146. |
|
||||
| **D170** | Named-NPC "flee" scripted behaviour looks wrong on PC: on Silo, Ourumov breaks into a full run the instant he alerts ("Ourumov: Kill him!") rather than the N64 walk/retreat; developer reports Trevelyan behaves the same way on Cradle. | **Observed, not investigated** (M-34, from a Silo playtest capture; user bug report — no fix wanted yet). Could be a genuine ailist / `chrTick` divergence, OR the D155/D156 wall-clock `deltaFrames` locomotion-anim speed-up (a catch-up spike playing the walk cycle at 2–3×). **Verify first:** capture the same beat against N64 reference footage before assuming it diverges — Ourumov may be scripted to flee. If it's real, suspect the frametiming clamp (§F D155/D156) or the Cradle-family AI path (§F D123). |
|
||||
| **D170** | Named-NPC "flee" scripted behavior looks wrong on PC: on Silo, Ourumov breaks into a full run the instant he alerts ("Ourumov: Kill him!") rather than the N64 walk/retreat; developer reports Trevelyan behaves the same way on Cradle. **M-49 full playthrough: escalated — Cradle (`-level_51`) is "bugged out completely" from Trevelyan's behavior; the final level's scripted progression breaks. Now folded into / superseded by §F D193 (NPC AI locomotion too slow).** | **Observed, not investigated** (M-34, from a Silo playtest capture; user bug report — no fix wanted yet). Could be a genuine ailist / `chrTick` divergence, OR the D155/D156 wall-clock `deltaFrames` locomotion-anim speed-up (a catch-up spike playing the walk cycle at 2–3×). **Verify first:** capture the same beat against N64 reference footage before assuming it diverges — Ourumov may be scripted to flee. If it's real, suspect the frametiming clamp (§F D155/D156) or the Cradle-family AI path (§F D123). |
|
||||
| **D171** | Silo missile-shaft: a large translucent grey triangle sweeps across the lower frame while panning down the shaft (~3 s of the capture). Looks like a stray poly / near-plane clip fan, same family as D106 (portal near-plane min>max). | **Observed, not investigated** (M-34, Silo capture). Cosmetic, intermittent, tied to camera angle in the open vertical shaft. |
|
||||
| **D172** | Bullet-impact / blood particle spray renders magenta or cyan instead of dark red (visible on guards hit at close range on Silo). | **Observed, not investigated** (M-34, Silo capture). A channel-order / CC-mode error in the particle-sprite path (magenta = R+B, cyan = G+B) — not the D64/D120 blood-decal family (those are crash/hang + missing decals, not colour). Likely a texture-format or env-colour decode in `blood_animation.c` / the fast3d sprite path. |
|
||||
| **D172** | Bullet-impact / blood particle spray renders magenta or cyan instead of dark red (visible on guards hit at close range on Silo). | **Observed, not investigated** (M-34, Silo capture). A channel-order / CC-mode error in the particle-sprite path (magenta = R+B, cyan = G+B) — not the D64/D120 blood-decal family (those are crash/hang + missing decals, not color). Likely a texture-format or env-color decode in `blood_animation.c` / the fast3d sprite path. |
|
||||
| **D178** | Objectives not listed correctly on the pre-mission briefing screen (the mission overview shown before starting a level). | **M-36 triage: the objective lines are BLANK, not wrong.** `GE_STARTMENU=10` Dam briefing renders the header fine ("OHMSS / Agent: James Bond / Mission 1: Arkangelsk / Part i: Dam / PRIMARY OBJECTIVES:") then just the bullet "a." with no text and no b/c/d. → **D143/D151 blank-text family** (`langGet()` → NULL → silent blank), *not* a propDef walk / difficulty decode. **Family B** in `docs/dev/M-36-TRIAGE.md` — find which lang bank the front-end briefing flow fails to load. Re-confirm the exact shape in a *real* briefing (the `GE_STARTMENU` boot skips real save state). Close relatives: D151 (watch objective text blank — type-35/23 `text` slot u16→u32 fix, M-28; interactive re-verify was still pending), D143 (briefing/objective text blank via `langGet` NULL), D157 (objective difficulty byte offset — could make difficulty-gated objectives appear/disappear wrongly). The pre-start briefing is a *different surface* from the in-level watch, so D151's fix may not cover it. **Next:** ask user which level + what exactly shows vs expected; compare against `docs/dev/LEVEL-OBJECTIVES.md` (regenerated post-D157); if lines are blank → D143/D151 family (`langGet` NULL guard = silent blank), if wrong objectives listed → propDef type-23 record walk / difficulty decode. |
|
||||
| **D175** | In-game stutter / brief hang during normal play — observed opening a door on Runway (mission 3, `-level_35`) and also on Surface. No backtrace captured (gdb attach missed the window; it self-recovered). | **Observed, not investigated** (user QA report). Matches known transient-hang classes rather than looking new: D155 (post-slow-load catch-up spiral — clamped), D156 (anim NaN loop — guarded, prints one-shot stderr diag if it fires), D134 (SP/DP task-done event swallowed — fixed M-22, Runway was an original victim), D147/D152 (audio `s_imLock` steal = ~2 s hiccup). Door-open specifically = new room first visible → one-off texture-import/frame spike (likely benign). **Next:** reproduce under `tools_pc/debug.ps1` with the box idle; check console for the D156 stderr diag; if it recurs, attach gdb mid-hang and pattern-match `bt` (import_texture = benign slow frame; model.c:3131 spin = D156; pthread_mutex_lock on s_imLock = D147/D152). |
|
||||
| **D176** | Surface exterior renders wrong (two co-located defects). **(a)** Sky is solid **black** — the skybox / sky gradient does not draw at all (reference: warm sunset cloud gradient). **(b)** The perimeter **tree-line backdrop renders as grey TV-static / comb interlacing** instead of snow-laden pines — the geometry is present and roughly the right silhouette but every tree texture is scrambled noise. Ground, tunnel and the dish/bunker building draw fine. | **Observed, screenshots committed** (M-35, user QA; `docs/img/bugs/d176-surface-exterior-our-build.png` vs `-reference.png`). Level is the shared Surface exterior — user reached it Dam→Surface on Agent so this is **Surface (i) `-level_36`** (confirm it also affects `-level_43`). **(a)** black sky: **M-36 partial (findings.md "D176(a)").** Env data is correct (`Sky.Clouds=1`, `CloudRGB=240,120,30`, `SkyImageId=0`); `skyRender` runs the cloud path and classifies corners correctly (switch case 12), but `skyRenderTri`/`skyRenderFull` emit nothing — not even the `env->RGB` fill shows. Not root-caused: next is probing `sub_GAME_7F097388` projected coords, verifying `skywaterimages[0]` resolves on PC (`image_bank.c` GIMG offset `0xFB4`), and a fast3d sky-tri trace. `GE_D176=1` probe shipped. **M-37: ROOT-CAUSED** — `skyRenderTri`/`skyRenderFull` build geometry *exclusively* as `G_RDPHALF_*` immediates, which `gfx_pc.cpp` deliberately no-ops (identical no-op in the PD port); fix = decode the RDPHALF tri-raster stream in fast3d (~1 session, genuine new code). **M-42: `-level_22` (Statue) confirmed a clean headless repro** — bare boot, frame ~360, upper half pure black; use it to verify the decoder. `-level_36`/`-43` intro cams face down (not a sky test without `GE_INPUTSCRIPT`). **(b)** tree/rock static: **M-36 triage — NOT the D159 family.** `texSwapAltRowBytes` is a full `#ifdef PORT` no-op (`image.c:2199`), it cannot cause this. Real cause: `port/fast3d/gfx_pc.cpp` `import_texture_*` assume `line_size_bytes == full_image_line_size_bytes` (`SUPPORT_CHECK`; rgba16's is commented out *"trips ... with a garbage size"* `:658`) → non-PoT / sub-rect tiles get a flat linear read → diagonal row shear = grey static. **Family A** in `docs/dev/M-36-TRIAGE.md`. Fix: honour `line_size_bytes` as the real source pitch. Same defect as D182(2). |
|
||||
| **D174** | "No blood effect" — user reports no visible blood when shooting guards / into surfaces. | **User QA report; likely D120, unverified.** Decals: the D120 `PointUsage[]` converter fix (M-30) was never interactively verified and only takes effect after a full sidecar regen (`d43 && d69 && d88 --regen`) — check whether the playtest build had regenerated sidecars before concluding the fix failed. Spray: D172 observed the particle spray *drawing* (wrong colour, magenta/cyan) on Silo — if the user sees no spray either, that's new info (different weapon/range, or the D172 decode is worse than "wrong colour"). First step: BUNKER1 firefight with regenerated sidecars; if decals still absent, instrument the `chr.c:3328` decal walk. |
|
||||
| **D173** | Third-person Bond model (the player-representing figure shown at level start) spawns too high — floats well above the ground "a lot of the time" while the actual player spawn is correct. Same symptom on the Cuba end credits: JB stands ~6 ft in the air above Natalya. | **Observed, not investigated** (user QA report). D75 animated-model family but a *position* defect, not absence — M-33 confirmed the gun-barrel Bond renders fine upright. Suspects: (a) intro/credits puppet chr spawn position vs the model's base transform; (b) anim root-motion accumulation (`modelSetAnimFrame2WithChrStuff`, D156 area) offsetting the rendered joints above the body origin; (c) a `coord3d`/packed-float decode in the intro camera/model setup. "Player spawning is OK" localises it to the render-side model, not `g_CurrentPlayer` state. |
|
||||
| **D182** | SELECT FILE screen degrades after navigating away and back (main menu → file select, or backing out of file select and re-entering). On the fresh screen the 007 gun-barrel spiral / Bond silhouette renders behind the file boxes; after a round-trip **the Bond image stops drawing entirely** and the whole background gains the **comb / interlacing** artifact (horizontal line scramble). File boxes, text and the Copy/Erase icons still draw. | **Observed, screenshot committed** (M-35, user QA; `docs/img/bugs/d182-file-select-backout-20260902.png`). Two symptoms, likely linked: **(1)** background 3D image stops rendering after re-entry → a front-end model/DL whose `render_pos` / segment binding is only valid on first construction (D149 / D75 `constructor_menuXX` family — the dossier/wallet models have the same "renders once" behaviour). **(2)** interlacing = **M-36 triage: Family A**, the `import_texture_*` `line` vs `full_image_line` shear (see D176(b) / `docs/dev/M-36-TRIAGE.md`) — **not** the D159 `texSwapAltRowBytes` path (that is a full PORT no-op). Open question: why only *after* a round-trip — first import may land `line == width`, re-import may not, or the tile descriptor is stale on re-entry. Repro: `GE_STARTMENU` to file-select, back out with ESC/B, re-enter; `GE_DTEX` on the spiral texture across both visits. Shares defect (2) with D176(b). |
|
||||
| **D174** | "No blood effect" — user reports no visible blood when shooting guards / into surfaces. | **User QA report; likely D120, unverified.** Decals: the D120 `PointUsage[]` converter fix (M-30) was never interactively verified and only takes effect after a full sidecar regen (`d43 && d69 && d88 --regen`) — check whether the playtest build had regenerated sidecars before concluding the fix failed. Spray: D172 observed the particle spray *drawing* (wrong color, magenta/cyan) on Silo — if the user sees no spray either, that's new info (different weapon/range, or the D172 decode is worse than "wrong color"). First step: BUNKER1 firefight with regenerated sidecars; if decals still absent, instrument the `chr.c:3328` decal walk. |
|
||||
| **D173** | Third-person Bond model (the player-representing figure shown at level start) spawns too high — floats well above the ground "a lot of the time" while the actual player spawn is correct. Same symptom on the Cuba end credits: JB stands ~6 ft in the air above Natalya. | **Confirmed still present across the full 21-level playthrough (M-49).** **Observed, not investigated** (user QA report). D75 animated-model family but a *position* defect, not absence — M-33 confirmed the gun-barrel Bond renders fine upright. Suspects: (a) intro/credits puppet chr spawn position vs the model's base transform; (b) anim root-motion accumulation (`modelSetAnimFrame2WithChrStuff`, D156 area) offsetting the rendered joints above the body origin; (c) a `coord3d`/packed-float decode in the intro camera/model setup. "Player spawning is OK" localises it to the render-side model, not `g_CurrentPlayer` state. |
|
||||
| **D182** | SELECT FILE screen degrades after navigating away and back (main menu → file select, or backing out of file select and re-entering). On the fresh screen the 007 gun-barrel spiral / Bond silhouette renders behind the file boxes; after a round-trip **the Bond image stops drawing entirely** and the whole background gains the **comb / interlacing** artifact (horizontal line scramble). File boxes, text and the Copy/Erase icons still draw. | **Observed, screenshot committed** (M-35, user QA; `docs/img/bugs/d182-file-select-backout-20260902.png`). Two symptoms, likely linked: **(1)** background 3D image stops rendering after re-entry → a front-end model/DL whose `render_pos` / segment binding is only valid on first construction (D149 / D75 `constructor_menuXX` family — the dossier/wallet models have the same "renders once" behavior). **(2)** interlacing = **M-36 triage: Family A**, the `import_texture_*` `line` vs `full_image_line` shear (see D176(b) / `docs/dev/M-36-TRIAGE.md`) — **not** the D159 `texSwapAltRowBytes` path (that is a full PORT no-op). Open question: why only *after* a round-trip — first import may land `line == width`, re-import may not, or the tile descriptor is stale on re-entry. Repro: `GE_STARTMENU` to file-select, back out with ESC/B, re-enter; `GE_DTEX` on the spiral texture across both visits. Shares defect (2) with D176(b). |
|
||||
| **D197** | Character face/head textures wrap wrong — the face texture repeats around the side of the head "and continues weird" instead of clamping to the front. Seen on **Silo** (M-49 playtest), intermittent ("sometimes"). Reads as **`G_TX_CLAMP` not honoured** for the character-skin tile in fast3d (→ `GL_REPEAT`, so the face UV region tiles around the skull), or wrong `shifts`/`shiftt` / LOD-tile selection on the head texture, or the D167 non-PoT wrap-period issue (head textures are often non-PoT) hitting skinned models. | **Observed, not investigated** (M-49, user bug report — needs a capture + which chr/level). Distinct from D176(b)/D182(2) (those are *bg wall* pitch shear). Related: RC3/D167 (`Video.WrapFix`, non-PoT wrap period) + the `texWriteTileLods` `shifts=shiftt=lod` note in findings §F "D176(b) → 4. candidate 2". First checks: does `gfx_pc.cpp` map N64 `cms/cmt` `G_TX_CLAMP` → `GL_CLAMP_TO_EDGE` for character DLs; is the head tile getting the base tile vs an LOD tile (D107); try `Video.WrapFix=1` on a repro. |
|
||||
| **D195** | Transparency / alpha-blended surfaces render wrong — reported on **Control** (Severnaya bunker basement — Natalya's console room; heavy on glass partitions and the translucent projected/holographic wall displays). Exact failure mode not yet pinned by the reporter (M-49 playtest): candidates are alpha-blended geometry drawing fully opaque, translucent panels drawing invisible, or draw-order/sort so translucent surfaces occlude what's behind. | **Observed, not investigated** (M-49, user bug report — needs a `GE_PCDUMP` capture + which surfaces). fast3d render-mode / combiner path. Same neighborhood as D161 (CI8 tile + `G_TT_NONE` → garbage), D172 (particle-sprite channel order), D176(b) (texture pitch shear) — but this is a *blend/alpha* defect, not a texture-decode one. Note D128 already touched Control's `specialportalarray` (portal struct stride) — check whether the affected surfaces are portal-adjacent. First checks: does `gfx_pc.cpp` honour `G_RM_*` XLU render modes + `G_AC_THRESHOLD`/`G_CC` alpha for these DLs; is `gDPSetRenderMode` `w1` being parsed (cf. findings §F "zbuf-clear w1 scribble"); is Z-write-vs-blend ordering right for XLU surfaces. Get the reporter to say: opaque-where-should-be-clear, invisible, or wrong edges? |
|
||||
| **1P weapon viewmodel** | In-level first-person weapon models don't draw at all (no gun visible while playing). | Already documented — **AUDIT-M6 #5** (`docs/dev/AUDIT-M6-player-offsets.md`, "prime suspect") + §F D115 "still open (MED)"; M-8 handoff listed it NOT STARTED with a plan: instrument `gunfire.c:600-661` setup + `field_87F` gating first, then check `weaponModel.render_pos` arena lifetime and `weaponRwPool[192]` sizing. Note M-32b's "render_pos/dynAllocate RULED OUT" probe was the **front-end gun-barrel** path (`title.c`), not this in-level path — candidate #5 still stands. Distinct from the watch-page "dark/faint weapon model" (D75, D141 note). |
|
||||
|
||||
## Resolved (for reference)
|
||||
|
||||
@@ -7,6 +7,16 @@ auto-injected, D121), `GE_PCDUMP="80-260:40"`, ~24 s watchdog then
|
||||
0x140000000). "render %" = `tools_pc/pixcount.py` non-clear on the last
|
||||
captured frame. Binary at `f2beae4b` + this session's build.
|
||||
|
||||
**M-49 (2026-09-04) — full-campaign playtest, v0.1.0 win64 bundle.**
|
||||
User played every solo mission on the packaged build (fresh save). **19 / 21
|
||||
completable start to finish.** Only two crashes in the whole game — **Bunker ii
|
||||
(`-level_27`) and Statue (`-level_22`)**, same fault PC `0x1400801b1`
|
||||
(`modelGetNodeRwData` node-tree walk), one root cause = **D191** (pointer-width
|
||||
/ byte-swap family, findings §F). This retires the old "expect crashes past the
|
||||
level intro" framing for 19/21 levels. Biggest non-crash complaint: **D193** —
|
||||
AI locomotion too slow, breaks Cradle's scripted progression. v0.1.0 tagged off
|
||||
this state.
|
||||
|
||||
**M-30 re-sweep (2026-08-31, build after D157/D120/RC2): 21 / 21 PASS — no
|
||||
crash logs.** First all-green sweep incl. Cuba (54) — the D129 credits-path
|
||||
crash no longer reproduces on a bare `-level_54` boot (Cuba rendered 92.5%
|
||||
|
||||
@@ -391,6 +391,11 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
|
||||
| D188 | **Linux `-O2` boot crash: `_Printf` passes `&args` of a by-value `va_list` param (M-48).** On the x86-64 System V ABI `va_list` is an array type → a by-value `va_list` parameter decays to a pointer, so `_Printf`'s `_Putfld(&x, &args, …)` handed `_Putfld` the address of its own local pointer slot, not the arg list; `_Putfld`'s `va_arg(*args, …)` then walked garbage. Latent at `-Og`, SIGSEGV at `-O2` (PR #9) — crash path `bossInitMainthreadData` (`boss.c:247`, D121 per-level `-m` sprintf) → `sprintf("-level_%c%c %s", …)` → `_Putfld` @ `xprintf.c`. Same UB class as D187. Windows x64 `va_list` is a scalar so `&args` worked there (Windows build unaffected — `-level_09` framediff 3/3). Fix: `#ifdef PORT` — `_Printf` takes `va_list *`, threads it straight to `_Putfld`; `src/sprintf.c` (sole PC caller; `rmon.c` excluded) passes `&args` of a real object. N64 build untouched (`#ifdef PORT`; file byte-matches ROM). | FIXED (`src/libultrare/libc/xprintf.c`, `src/sprintf.c`, `#ifdef PORT`); PR #22 |
|
||||
| D189 | **Linux `-fstack-protector` aborts on the decomp's latent stack-buffer overruns (M-48) — FIXED.** Two hit on `-level_09`: (1) `sub_GAME_7F0B1DDC` (`stan.c`) — local `StandTile *tileStack[39]`, bail check `if (cat >= 41)` runs only once per outer iteration so `cat` reaches `40 + pointCount` and the append writes past the end; BUNKER1 prop room-list setup legitimately visits ~40 tiles (confirmed in GDB: 30 valid distinct tile ptrs and climbing; `standTileStart=0x701ae160`, so **not** a pointer-truncation bug — the earlier "blob >4 GiB" triage premise was wrong). (2) `bondviewCalcIntroSwirlCamera` (`bondview2.c`) — `f32 pointbuf[10]` written `[-3 .. 11]` via `&pointbuf[i*3]`, i∈[-1..2]. Both harmless on N64 + the MinGW build (no stack protector); only Ubuntu gcc's `-fstack-protector-strong` default makes them fatal `__stack_chk_fail`. **Fix:** `-fno-stack-protector` globally (`CMakeLists.txt`) to match the N64/MinGW builds, + `tileStack[64]` under `#ifdef PORT` for the one cleanly-bounded case, + `stan.c:2225` link-resolve moved to the D177 `PORT_PTRADD` macro for consistency. A *hardened* Linux build would need the whole overrun class swept. **Verified:** Windows `-level_09` framediff 3/3; Linux `-O2` `-level_09` runs to frame 2100+ no crash. porting-notes.md §C. | FIXED (`CMakeLists.txt`, `src/game/stan.c` `#ifdef PORT`); PR #23 |
|
||||
| D190 | **Linux `-level_45` SIGSEGV at load in `add_ptr_to_objective` (`objective.c:64`) (M-48, open).** `objective_ptrs[objective->menu] = objective;` faults — `objective` wild or `objective->menu` a garbage index → OOB write. Almost certainly the D122/D126/D132 propDef-stride / ROM-serialized-struct-widening family (`struct objective_entry` / the type-23/type-1E setup-text record chain read at the wrong stride on PC). Found in the M-48 Linux level sweep: **5/6 spot-checked levels (`-level_09/20/27/34/37`) load + render fine; `-level_45` is the one failure.** Not a v0.1.0-alpha blocker (alpha is explicitly "expect crashes"). Windows: `-level_45` load status not re-checked this session. | OPEN — not investigated (M-48) |
|
||||
| D191 | **Windows `-O2` release-bundle crash: SIGSEGV in `modelGetNodeRwData` (`src/game/model.c:478`) — the `root->Parent` node-tree walk (M-49, user playtests of the v0.1.0 win64 bundle).** **Two instances, identical fault PC `0x1400801b1`:** (1) fresh save, killed the first guard right after escaping the cell in Bunker ii; (2) Statue (`-level_49`?), right after the Trevelyan meeting cutscene. `ge007.crash.log` (auto-dump; symbols intact in the shipped exe, `addr2line` resolves): PC = `modelGetNodeRwData` at `model.c:478` — the `switch (root->Opcode & 0xff)`, i.e. `root` (a `ModelNode *`) is bad; `addr2line` puts it in the inlined self-recursion at `:532`, so the `while (root->Parent) { root = root->Parent; … }` walk (`model.c:527-540`) handed a garbage node into the recursive call. Instance 1: `Rdx = 0x70267bc000000000` — a `~0x70266xxx` heap address in the **high 32 bits, zero low half** = 32-bit pointer field read as 64-bit (or a byte-swapped `u32` pair). Instance 2: `Rdx = 0`, FAULT ADDR `0x0` — `root` (or `root->Parent`) resolved to NULL. Both: `Rcx=Rdi` = the `~0x7008xxxx`/`~0x7007xxxx` object ptr, `R8 = 0x1d` (29). So the `Parent` chain in some model contains a **truncated or NULL link** — pointer-width / byte-swap family (D3x, D119 `weapons_held[]->chr` type-pun, D122/D126/D132 propDef-stride). NOT weapon-fire-specific — common factor is `modelGetNodeRwData` running on a character/cutscene model whose node tree has a bad `Parent`. Crash logs saved: `scratchpad/crashlogs/`. **Repro aid:** shipped exe has full symbols; `debug-crash.ps1` in the bundle dir runs it under gdb, `GE_D51=1` → `d52rw.log` rwdata-pool trace. The crash.c EBP backtrace breaks immediately (`#01: 0x1d`) — need a real `bt` + `p *root` + `p *Objinst` at the fault. Not yet reproduced under a debugger; no fix. | OPEN — not investigated (M-49). 2 field instances (Bunker, Statue). Awaiting a gdb backtrace + `d52rw.log`. |
|
||||
| D193 | **NPC AI locomotion is too slow — characters don't move to their destinations at the original game's rate (M-49, user playtest; clarified after the FULL 21-level playthrough — "the biggest overall flaw").** Not (primarily) an animation-playback bug — the *travel speed* of scripted/AI movement is wrong: guards, named NPCs (Trevelyan, Ourumov) and friendlies (Natalya) all take noticeably longer to run/walk to their goal positions than on N64. General, not chr-specific. **Leading hypothesis — the D117/D134/D155/D156 wall-clock-timing family:** `chr.c` / `chrai.c` integrate movement as `pos += speed * g_GlobalTimerDelta` and step sim loops `while (i < g_ClockTimer)` / `-= g_ClockTimer` (`chr.c:1437,1446,1491-1502,1862,2359,2569`; `chrai.c:76`). On the port `osGetCount()` is free-running wall-clock (D117), `g_GlobalTimerDelta`/`g_ClockTimer` are derived from it, and D155's `FRAMETIMING_PORT_MAX_CATCHUP = 6` clamps `nextFrameTime`. If the effective per-second sum of `g_GlobalTimerDelta` (or the `g_ClockTimer` tick count) runs low — quantization, the catch-up clamp biting during normal play, or a units mismatch (`g_JP_GlobalTimerDelta` vs `g_GlobalTimerDelta`, `chr.c:1435/1437`) — every delta-scaled AI motion integrates slow while **player** movement (input-driven per frame) still feels right, which is exactly the reported split. Related: **D170** (Ourumov/Trevelyan "flee" looks wrong — flagged there as possibly the same locomotion-speed issue rather than an AI divergence). Also possible: `chrGoToBond/Chr(SPEED_RUN/WALK/SPRINT)` speed enum → path-follow step, or pathfinding advancing ≤1 waypoint per frame-quantized tick. **Instances seen (M-49 full playthrough):** (a) Facility — the post-scripted-scene beat with Ourumov + the soldier squad: the soldiers **walk in super slowly and take a long time to path into firing positions** before they start shooting (this is regular squad combat AI, not a named-NPC script — good evidence the slowdown is general delta-scaled `chr` locomotion / path-following, not one ailist); (b) the named-NPC flee behaviour on Silo (Ourumov) and Cradle (Trevelyan) — see D170; (c) **Natalya (the escort NPC) always walks really slow** — on every escort level (Bunker ii, Statue, Control, Archives) she trails far behind, forcing the player to wait. Escort follow-AI is the same `chrGoToChr`/path-follow path as (a). **WORST CASE — Cradle (`-level_51`) is "bugged out completely" due to Trevelyan's behaviour (M-49).** The final level is Trevelyan-fleeing-and-scripted end to end (he runs the antenna cradle, climbs, drops to the cradle floor, the helicopter beat); if his locomotion timing / path-following is off he never hits the trigger points and the level's scripted progression stalls or goes haywire. This makes D193 effectively a **can't-properly-finish-the-game** bug, not just cosmetic sluggishness. **Next:** log `g_GlobalTimerDelta` + `g_ClockTimer` per frame during normal play and check the 1-second sum vs the N64's ~60; time a guard's run across a known distance vs reference footage; then watch Trevelyan's chr state / current AI command on Cradle. | OPEN — root-cause hypothesis (timing family), not investigated (M-49). **Highest-impact playtest finding — breaks Cradle.** |
|
||||
| D194 | **Mouse input needs another tuning pass — RMB aim over-sensitive + sensitivity couples to frame/sim rate (M-49, user playtest).** Two parts. **(a) Aim mode (RMB) is near bang-bang, not proportional** (`port/src/input.c:697-707`): any per-poll delta past `AIM_MOVE_THRESH` emits stick `m = clamp(61 + |Δ|·(MouseAimSpeed/100)·AIM_GAIN, 61, 60+AimBand)` = `[61,80]` — with the M-29 defaults (`MouseAimSpeed=16`, `AIM_GAIN=4.0`, `AimBand=20`) `m` is already 61 (fast) at ~1.5 px/poll and saturates at 80 by ~30 px/poll, so the usable proportional band is tiny and sits near the top of GE's aim range → "way too sensitive," turn rate barely tracks how fast you actually move the mouse. Needs a real response curve (low floor, wider proportional band, maybe a gamma), not a threshold+clamp. **(b) Overall sensitivity correlates with movement/frame rate** (user's words: "correlated with movement speed, does not feel good"): `input.c` emits raw stick counts **per controller poll** with no `g_GlobalTimerDelta` / polls-per-frame normalization (hipfire yaw `sx += edx·(MouseTurnSpeed/100)·6.0` at `:709`; aim as above). GE integrates the stick scaled by its sim delta, and the port's poll cadence vs render/sim cadence drifts with scene load — so effective sensitivity shifts when the framerate does (which reads as "when I'm moving"). Fix direction: accumulate mouse delta and convert to stick using a fixed reference dt (or feed the game a dt-consistent value), so a given hand motion = a given view rotation regardless of fps. Lineage: D118a (hipfire pitch digital vs analog), D165 (menu pointer), D166 (hipfire pitch pulses), D180-B3 (`MouseAimSpeed` 25→16). Config knobs exist (`Input.MouseAimSpeed/AimBand/MouseTurnSpeed/HipfirePitchSpeed`) but the *shape* of the mapping is the problem. | OPEN — root-caused, not fixed (M-49) |
|
||||
| D196 | **OS mouse cursor stays visible after closing the F10 options overlay in click-to-lock mode (M-49, user playtest).** `Input.MouseCaptureMode=1`. Open F10 overlay → `optionsOverlayToggle()` (`optionsoverlay.c:391`) calls `inputSuspendForOverlay()` which forces `SDL_ShowCursor(SDL_ENABLE)` + drops the grab. While open, `inputComputePad(0)` early-returns at `input.c:512` (overlay owns controller 0) **before** `reconcileGrab()` / any cursor-visibility call. On close: the next poll runs `reconcileGrab(menuMode)` (`input.c:522`) which re-grabs + hides the cursor **only if `captureArmed && !menuMode`** — i.e. only if you had already clicked-to-lock in a stage before opening F10. If you opened the overlay from a menu, or in a stage you hadn't clicked-locked yet, `want` computes 0, `mouseGrabbed` stays 0, and **nothing calls `applyCursorVisibility()`** — so the cursor `inputSuspendForOverlay()` made visible never gets re-hidden (correct free-but-focused state per `applyCursorVisibility()` `input.c:842-846` is *hidden*). **Fix (port-only, ~1 line):** call `applyCursorVisibility()` right after the `reconcileGrab(menuMode)` at `input.c:522` (self-heals every poll once the overlay-open early-return stops firing), or add an `inputResumeFromOverlay()` that runs `reconcileGrab` + `applyCursorVisibility` and call it from `optionsOverlayToggle()` on close. D180/D184/D165 input lineage. Minor. | OPEN — root-caused, 1-line fix identified, not applied (M-49) |
|
||||
| D192 | **Front-end mission/level-select grid pointer still can't reach the outer cells — but only under `Input.MouseCaptureMode=0` (legacy always-grab) (M-49, user playtest).** D169 was marked FIXED (M-33) by replacing the hard-coded 320×240 pointer clamp with the live `getPlayer_c_screenwidth/height/left/top()` rect; the feel-check was flagged "owed" on both D169 and D180 and never done. It now reproduces: with `MouseCaptureMode=0` the menu pointer runs the relative-delta P-controller path (`port/src/input.c:659-690`), which clamps target+estimate to `getPlayer_c_screen*` — but that accessor returns `g_CurrentPlayer->c_screenwidth` (`src/game/bondview.c:882`), which in the front end is the ~320×240 stage viewport, **not** the front end's actual 440×330 (`front.c:8570` `viSetViewSize(440,330)`). So the effective clamp is still ~300/220 while the mission-select grid's outer split points sit at 317 / 235.5 (`front.c:516/519/3180/3194`) — outer columns + bottom row unreachable, P-controller winds up and never settles. `MouseCaptureMode=1` (the shipped default, click-to-lock) writes `cursor_h_pos`/`cursor_v_pos` directly from the absolute OS cursor (`input.c:629-657`) and *feels* fine, though it shares the bad `hiH` so the last few px at the right/bottom edge may still be short. **Not a v0.1.0 blocker** — default mode is acceptable, always-grab is opt-in via INI, front-end roughness is already in the release notes. **Fix (port-only, small):** in the `menuMode` branch, when `current_menu` is a front-end menu use the real front-end rect (hard-code `front.c`'s cursor clamp `[20,420]×[20,310]`, or read the front-end viewport global) instead of routing through `g_CurrentPlayer`. Confirm with one `GE_INPUTLOG=1` capture in mission-select — sweep the mouse to each corner, watch where `cursor=(x,y)` plateaus. D165 · D169 · D180 lineage. | OPEN — root-caused, not fixed (M-49) |
|
||||
| D152+ | D152 addendum (M-31): static audit of every compiled-audio `osSetIntMask(OS_IM_NONE)` — **all balanced**, no unbalanced early-return. Real fixes: `sndSetSfxSlotVolume` now holds the mask across its list walk (matches its twin `sndDeactivateAllSfxByFlag`) + `sndApplyVolumeAllSfxSlot` batches the slot loop under one recursive hold (kills the fade-out lock-acquire storm); `portThreadWrapper` calls `imThreadExitRelease()` on thread exit (kills the "transient thread acquired `OS_IM_NONE` and died" leak + the pthread-id-reuse re-wedge). Steal-lock kept as backstop. | FIXED (`src/snd.c`, `port/src/libultra.c`, `#ifdef PORT`) — fade-out repro playtest-gated, not headless-verified |
|
||||
| RC3 · D167 | **Non-power-of-two texture wrap period (`docs/dev/TEXTURE-GLITCH-ANALYSIS.md` §6 RC3 — "textures repeat oddly", residual Depot-ceiling noise after D161).** The N64 RDP masks the texel coordinate of a wrapping render tile at `1<<mask`, and GE sets `mask = texDimensionToMask(dim) = ceil(log2(dim))` (`src/game/tex.c:361`), so a non-PoT tile (Depot's 65×65 / 96×48 / 56×56 room surfaces) repeats at the **next power of two**, not at its image size the way GL `GL_REPEAT` does → the pattern is squashed/stretched and the seam lands in the wrong place. fast3d never stored `masks`/`maskt` at all (`gfx_dp_set_tile` dropped them) and wrapped purely at the uploaded image dimension. **Fix (`port/fast3d/gfx_pc.cpp`, behind the existing `Video.WrapFix` knob, default OFF):** store `masks`/`maskt` on the tile; in the hoisted per-texunit pre-wrap block in `gfx_sp_tri1` (D74 block — already lifted out of the vertex loop, indexed by texunit `t` not vertex `i`), when the tile is WRAP (no CLAMP/MIRROR bit) and `1<<mask != tex_width`, fold the UV at the N64 period `1<<mask` and clamp the `[dim, 1<<mask)` overflow band (which is a TMEM smear on console, no real texels) to the last texel so it reads as an edge streak instead of a bogus early image restart. `GE_WRAPFIX=0/1` env override added (env wins over the ini, matching `Debug.FrameDump`). **Per-level captures (`-level_09`/`-30`/`-34`/`-20`, WrapFix OFF vs ON, `GE_PCDUMP` 6-frame windows):** no crashes, 6/6 frames each; on settled/comparable frames Silo is ~pixel-identical (phash 0–11), Facility 180–260 pixel-identical, Depot shows small localized texel changes on ceiling/wall cells (dmean 5–9, no structural break); the large per-run deltas are all the D117 intro-camera-pan nondeterminism, not the fix. Default kept **OFF** — no regression, but a headless structural diff can't confirm the Depot ceiling actually looks *better*; needs a human eyeball with `Video.WrapFix=1`. Default-off is byte-identical to golden (all new behaviour is inside `if (g_wrap_fix)`). D74's dead in-vertex-loop wrap block was already reworked/hoisted at M-30; this only adds the mask-period trigger. Confidence: **medium** (mechanism correct; overflow-band handling is an approximation, not exact TMEM-smear emulation; visual win unconfirmed). porting-notes.md §D. | KNOB ADDED, default OFF (`port/fast3d/gfx_pc.cpp`). Needs user visual check on Depot. |
|
||||
| D168 | **`GE_PCDUMP` / F12 PPM captures were vertically flipped — the entire source of the bogus D114/D116 "HUD/text X-mirror" (M-33, developer-confirmed).** `gfx_opengl_dump_bound_fbo` (`port/fast3d/gfx_opengl.cpp`) wrote `glReadPixels` output straight to a P6 PPM. GL framebuffer origin is bottom-left; PPM P6 is top-row-first — so every capture (and the `tools_pc/golden/` set, and every screenshot pasted into the finding log since M-6) was upside-down. On real hardware / an actual screen the game renders correctly (developer confirmed). The successive D114→D116 "shared fast3d mirror" investigations — each of which found *every probed stage clean yet the output "mirrored"* — were reading an inverted capture and pattern-matching upside-down asymmetric content (text, guards, the Nintendo logo) as "mirrored". Fix: emit PPM rows bottom-to-top. `tools_pc/golden/*.png` flipped in place to match (see `tools_pc/golden/README.md`); regenerate from a real run when convenient. **Not runtime-verified this session** (no ROM / toolchain in the migrated tree — see the migration note) — needs a fresh capture to confirm text reads normally. Confidence: **high** (mechanism is unambiguous; developer has hardware confirmation the screen is correct). porting-notes.md §D2. | FIXED (`port/fast3d/gfx_opengl.cpp`); D114 / D116 reclassified as capture-orientation artifacts (below). |
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
# GoldenEye 007 — game-behaviour reference
|
||||
# GoldenEye 007 — game-behavior reference
|
||||
|
||||
Community-documented account of how the retail N64 game is *supposed* to
|
||||
behave: the combat/AI/stealth model, difficulty scaling, per-level objective
|
||||
sets, timers, weapon data, and the original game's known quirks and glitches.
|
||||
|
||||
**Status of this document.** This is secondary source material, not ground
|
||||
truth. The decompiled game code in `src/` is authoritative for behaviour (see
|
||||
truth. The decompiled game code in `src/` is authoritative for behavior (see
|
||||
`AGENTS.md`). Use this doc to know *what correct looks like* when playtesting
|
||||
the port — which objectives a level must expose, what a timer value should be,
|
||||
whether a weird behaviour you just saw is an original-game quirk or a port
|
||||
whether a weird behavior you just saw is an original-game quirk or a port
|
||||
regression.
|
||||
|
||||
**Sources** (subjective commentary stripped; mechanical/factual data only):
|
||||
@@ -20,7 +20,7 @@ regression.
|
||||
|
||||
Distilled from `goldeneye_007_reference.md` (the full raw walkthrough dump);
|
||||
the walkthrough's per-stage tactical routes were dropped as not
|
||||
behaviour-defining.
|
||||
behavior-defining.
|
||||
|
||||
---
|
||||
|
||||
@@ -78,11 +78,11 @@ Relative to the lower tiers:
|
||||
never alert.
|
||||
- **Security cameras**: if a camera acquires the player it raises an alert
|
||||
that **spawns black-clad guards** armed with DD44s and KF7s. Shoot the
|
||||
camera centre for a one-shot kill.
|
||||
camera center for a one-shot kill.
|
||||
- **Retreating** breaks pursuit — most guards stop following after a set
|
||||
distance, letting the player reload / heal / reposition.
|
||||
|
||||
### AI line-of-sight & animation quirks (exploitable, i.e. defining behaviour)
|
||||
### AI line-of-sight & animation quirks (exploitable, i.e. defining behavior)
|
||||
|
||||
- A guard behind a **wall, low barrier or railing cannot shoot** the player,
|
||||
even point-blank — it must path around the obstacle. **Exception:** a guard
|
||||
@@ -97,7 +97,7 @@ Relative to the lower tiers:
|
||||
|
||||
## 3. Player state
|
||||
|
||||
| System | Behaviour |
|
||||
| System | Behavior |
|
||||
|---|---|
|
||||
| Health | restored by health packs; no regen. 00 Agent pool ≈ 8 KF7 hits (§1). |
|
||||
| Body armour | separate absorbing layer; fixed pickup locations (e.g. by the Jungle drone guns, start of Antenna Cradle). Nearly absent on 00 Agent. |
|
||||
@@ -223,10 +223,10 @@ scientist casualties.
|
||||
|
||||
## 7. Weapons — data table
|
||||
|
||||
Clip / max-capacity / behaviour. (Balance and capacity numbers to cross-check
|
||||
Clip / max-capacity / behavior. (Balance and capacity numbers to cross-check
|
||||
against the decomp's weapon tables.)
|
||||
|
||||
| Weapon | Type | Clip | Max | Behaviour notes |
|
||||
| Weapon | Type | Clip | Max | Behavior notes |
|
||||
|---|---|---|---|---|
|
||||
| PP7 | pistol | 7 | 800 | default sidearm |
|
||||
| PP7 (Silenced) | pistol | 7 | 800 | no alert on fire |
|
||||
@@ -284,7 +284,7 @@ noted. **All Guns** cheat unlocks everything.
|
||||
| Object | Rule |
|
||||
|---|---|
|
||||
| Red alarm boxes | shoot to complete "neutralize all alarms"; a guard reaching one spawns reinforcements |
|
||||
| Security cameras | destroy (centre = one-shot); acquisition raises the black-clad alert |
|
||||
| Security cameras | destroy (center = one-shot); acquisition raises the black-clad alert |
|
||||
| Sentry guns | fixed turrets (Bunker 2 striped corridor, Depot); destroy to pass |
|
||||
| Drone guns | remote turrets (Jungle, Control Center, Water Caverns, Aztec); some pairs must be destroyed together |
|
||||
| Locks / grates | shootable (e.g. the 4-lock vent grate at the end of Surface) |
|
||||
@@ -344,7 +344,7 @@ faithful; if you see one while testing, it is probably *not* a port bug.
|
||||
Ourumov has no objective use, but "firing" the key (action button) near the
|
||||
rocket opens the giant launch doors. Otherwise both items are inert.
|
||||
- **Cut content left in levels**: the Dam has the unused geometry of a
|
||||
cancelled boat mission out on the water; the Train's rear region past
|
||||
canceled boat mission out on the water; the Train's rear region past
|
||||
Alec/Trevelyan leads nowhere; the Facility's starting vent has a second path
|
||||
blocked purely cosmetically and shootable ceiling glass.
|
||||
|
||||
|
||||
+5
-3
@@ -17,8 +17,10 @@ unfamiliar, low-level codebase**: two coding agents — a local open-weight mode
|
||||
part-time through ~230 translation units of unmodified big-endian MIPS game
|
||||
code, handing work back and forth through shared written notes.
|
||||
|
||||
**Status:** Phase 2 of 4 (rendering). Boots, renders the full intro and front
|
||||
end, loads all 21 solo missions. No audio yet; not playable end to end.
|
||||
**Status:** Phase 2 of 4 (rendering); first alpha **v0.1.0** released. Runs the
|
||||
full single-player campaign — 19 of 21 missions completable start to finish in
|
||||
a full-campaign playtest, two levels still crash mid-mission. No audio yet; AI pacing
|
||||
and cutscenes are rough.
|
||||
|
||||
- **[Project repository and README](https://github.com/jkdansereau/goldeneye-pc-port)** — build instructions, requirements, status, how it compares to the Xbox 360 recompilation projects.
|
||||
|
||||
@@ -28,5 +30,5 @@ end, loads all 21 solo missions. No audio yet; not playable end to end.
|
||||
- **[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.
|
||||
- **[Game-behaviour reference](dev/game-behavior-reference.md)** — how the retail N64 game is meant to behave: combat/AI model, difficulty scaling, per-level objectives, timers, weapon data, and the original game's known quirks.
|
||||
- **[Game-behavior reference](dev/game-behavior-reference.md)** — how the retail N64 game is meant to behave: combat/AI model, difficulty scaling, per-level objectives, timers, weapon data, and the original game's known quirks.
|
||||
- **[Building](building.md)** — full build and asset-extraction guide.
|
||||
+4
-3
@@ -1,15 +1,16 @@
|
||||
# GoldenEye 007 — PC Port: architecture & plan
|
||||
# GoldenEye 007 — PC Port: architecture & plan
|
||||
|
||||
> This began as the pre-implementation research note and is kept as the
|
||||
> architecture reference. Sections 1–9 describe the design; section 10 lists
|
||||
> external references. The phased plan in section 8 is largely done through
|
||||
> Phase 2 — for current status see the [README](../README.md) and
|
||||
> Phase 2 — for current status see the
|
||||
> [README](https://github.com/jkdansereau/goldeneye-pc-port#status) and
|
||||
> [`dev/LEVEL-STATUS.md`](dev/LEVEL-STATUS.md); for the blow-by-blow finding
|
||||
> log see [`dev/findings.md`](dev/findings.md).
|
||||
|
||||
This document captures the research done to plan a PC port of
|
||||
GoldenEye 007, based on the decompilation this repository is forked from
|
||||
([`n64decomp/007`](https://github.com/n64decomp/007)). It is modelled on the
|
||||
([`n64decomp/007`](https://github.com/n64decomp/007)). It is modeled on the
|
||||
approach used by the
|
||||
[Perfect Dark PC port](https://github.com/fgsfdsfgs/perfect_dark),
|
||||
which ports the same Rare "Indy" engine (a later revision) to modern
|
||||
|
||||
@@ -31,7 +31,7 @@ N64-sized hole / hardcoded byte count, is **larger on x86-64**. Reading it
|
||||
from ROM bytes misaligns; allocating it N64-sized overruns adjacent
|
||||
state.
|
||||
|
||||
- Symptom: garbage pointer deref, corrupted neighbour field, delayed
|
||||
- Symptom: garbage pointer deref, corrupted neighbor field, delayed
|
||||
fault far from the cause.
|
||||
- Fix: store embedded ROM addresses as `u32`, cast at use site (PD ground
|
||||
truth); or give real inline storage / `sizeof()`-based alloc under
|
||||
@@ -403,13 +403,13 @@ through a converter or a runtime bswap fixup reads scrambled.
|
||||
logic). 2D pixel space is `viGetX()` x `viGetY()`. Return NULL when the
|
||||
overlay is closed so the frame is byte-for-byte unchanged.
|
||||
|
||||
- Z buffer cleared by pointing the colour image at it + fill-rect → does
|
||||
- Z buffer cleared by pointing the color image at it + fill-rect → does
|
||||
nothing in fast3d; must emit `G_CLEAR_DEPTH_EXT` (D105).
|
||||
- LOD / detail mip tiles: fast3d fabricates a crop when detail textures
|
||||
are off → force base tile (D107).
|
||||
- **A CI-format tile drawn with the TLUT disabled (`G_TT_NONE`) is not a
|
||||
palette texture** — the N64 RDP feeds the raw TMEM texel straight into the
|
||||
colour pipe, i.e. a CI8+`G_TT_NONE` tile behaves as I8. fast3d's
|
||||
color pipe, i.e. a CI8+`G_TT_NONE` tile behaves as I8. fast3d's
|
||||
`import_texture()` dispatched purely on `tile.fmt` and did a palette lookup
|
||||
against a stale `rdp.palette` → garbage (D161, GE Depot ceiling = blue
|
||||
speckle). Fix: when `rdp.palette_fmt == G_TT_NONE`, route CI4/CI8 → I4/I8.
|
||||
@@ -455,7 +455,7 @@ through a converter or a runtime bswap fixup reads scrambled.
|
||||
distinguish "the decoder is wrong" from "the source bytes are garbage".
|
||||
`GE_TEXRAW=1` (D183) dumps the bytes as handed to `import_texture_*`. The
|
||||
cheap offline test on such a dump: compute the mean vertical
|
||||
neighbour-difference at every candidate row pitch — a correctly-pitched real
|
||||
neighbor-difference at every candidate row pitch — a correctly-pitched real
|
||||
image has a sharp minimum at its true pitch (≈0.2–0.7 on a 0–15 nibble
|
||||
scale), a pitch/shear bug has the minimum at a *different* pitch, and genuine
|
||||
noise data is flat (~3.7) at every pitch. That three-way split settles
|
||||
@@ -674,7 +674,7 @@ and turns every such overrun into a fatal `*** stack smashing detected ***`
|
||||
wall-clock on PC, and GE is a variable-timestep sim
|
||||
(`frametiming.c waitForNextFrame` → `deltaFrames` = 60 Hz ticks of real
|
||||
time elapsed per render), so "frame N" differs 15–40 % between runs. Use
|
||||
`tools_pc/framediff.py <ppmdir>` (structural: 16×12 grid mean-colour +
|
||||
`tools_pc/framediff.py <ppmdir>` (structural: 16×12 grid mean-color +
|
||||
non-clear-% + aHash, `--mask X0,Y0,X1,Y1` for the HUD, `--update` to
|
||||
refresh `tools_pc/golden/`) — NOT an exact compare. A `GE_DETERM=1`
|
||||
fixed-tick mode was assessed not-narrow (redesigns retrace/tick
|
||||
|
||||
Reference in new issue
Block a user