1 Commits
Author SHA1 Message Date
James Dansereau 66e38e27d7 docs: v0.1.0 alpha framing + hygiene pass
Update the public-facing docs to the M-49 state (v0.1.0 alpha, first
full-campaign playtest: 19/21 missions completable, D191 crashes Bunker ii
+ Statue, D193 AI pacing is the top flaw).

- README: lead with what it is + status, research angle second; new
  Quick start section; Status/Download rewritten for the alpha + dual
  platform; "By the numbers" refreshed (~3 weeks, ~320 commits, ~190
  findings, ~49 sessions, 69 PORT files).
- .github/release-notes.md: v0.1.0 title + known-issues rewrite.
- .github/social-preview.html: status/stat lines (regen + re-upload the
  PNG separately).
- docs/index.md, docs/dev/LEVEL-STATUS.md: M-49 status.
- docs/dev/findings.md, docs/dev/GRAPHICS-BACKLOG.md: D191-D197 +
  D148/D170/D173 M-49 escalations.

Hygiene:
- Fix the `&` literal in internals.md H1.
- Repoint two ../README.md links that 404 on the Pages site.
- Standardise British -> American spelling across the public docs
  (behaviour->behavior, colour->color, catalogue->catalog,
  centre->center, etc.). Deep internal logs left untouched.
- .gitignore: document the local-notes convention (docs/HANDOFF.md,
  docs/BACKLOG.md, and a new docs/dev/notes/ drop for roadmaps and
  session plans) so it survives a fresh clone.
2026-09-04 00:05:46 -04:00
14 changed files with 157 additions and 96 deletions

No files matched your search

+12 -7
View File
@@ -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.** > ⚠️ **Research alpha. Playable, not polished.**
> The intro and menus render and all 21 solo missions load, but: > The full single-player campaign runs. In a full-campaign playtest on this
> - **no audio** (Phase 3, not started); > 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 > - some front-end 3D models are broken (the spinning Nintendo logo, the
> MISSION COMPLETE / mode-select models); > MISSION COMPLETE / mode-select models);
> - outdoor levels render with a **black sky**; > - 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 > If you just want to *play* GoldenEye on PC today, use one of the Xbox 360
> on PC, use one of the Xbox 360 recompilation projects instead — see the > recompilation projects instead — see the
> [project README](https://github.com/jkdansereau/goldeneye-pc-port#how-this-differs-from-the-other-goldeneye-pc-projects). > [project README](https://github.com/jkdansereau/goldeneye-pc-port#how-this-differs-from-the-other-goldeneye-pc-projects).
### Downloads ### Downloads
+3 -3
View File
@@ -124,12 +124,12 @@
<div class="rep">REPORT:</div> <div class="rep">REPORT:</div>
<div class="rows"> <div class="rows">
<div class="statusline">Status: <span class="status">PHASE 2 OF 4 &mdash; RENDERING</span></div> <div class="statusline">Status: <span class="status">ALPHA v0.1.0 &mdash; PHASE 2 OF 4</span></div>
<div class="stats"> <div class="stats">
<span class="k">Statistics:</span><span>21 / 21 solo missions load &amp; 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>~230 game-code units, compiled unmodified</span>
<span></span><span>~17k lines C/C++ &nbsp;&middot;&nbsp; ~6k Python (port layer + tools)</span> <span></span><span>~17k lines C/C++ &nbsp;&middot;&nbsp; ~6k Python (port layer + tools)</span>
<span></span><span>162 bugs root-caused &nbsp;&middot;&nbsp; built in ~2 weeks, part-time</span> <span></span><span>~190 bugs root-caused &nbsp;&middot;&nbsp; built in ~3 weeks, part-time</span>
</div> </div>
</div> </div>
+8
View File
@@ -68,6 +68,14 @@ build/*
build-*/* build-*/*
notes/* 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/) # PC port (see CMakeLists.txt / port/)
build-pc/ build-pc/
tools_pc/*.exe tools_pc/*.exe
+80 -52
View File
@@ -1,21 +1,25 @@
# GoldenEye 007 PC Port # GoldenEye 007 PC Port
[![CI](https://github.com/jkdansereau/goldeneye-pc-port/actions/workflows/ci.yml/badge.svg)](https://github.com/jkdansereau/goldeneye-pc-port/actions/workflows/ci.yml) [![CI](https://github.com/jkdansereau/goldeneye-pc-port/actions/workflows/ci.yml/badge.svg)](https://github.com/jkdansereau/goldeneye-pc-port/actions/workflows/ci.yml)
![status](https://img.shields.io/badge/status-Phase_2_of_4_(rendering)-orange) ![status](https://img.shields.io/badge/status-alpha_v0.1.0_(Phase_2_of_4)-orange)
![tested on](https://img.shields.io/badge/tested_on-Windows_x86--64-blue) ![tested on](https://img.shields.io/badge/tested_on-Windows_%2B_Linux_x86--64-blue)
![license](https://img.shields.io/badge/license-MIT-green) ![license](https://img.shields.io/badge/license-MIT-green)
![built with](https://img.shields.io/badge/built_with-coding_agents-8A2BE2) ![built with](https://img.shields.io/badge/built_with-coding_agents-8A2BE2)
**A work-in-progress native PC port of _GoldenEye 007_ (Rare, 1997, Nintendo 64), **A native PC port of _GoldenEye 007_ (Rare, 1997, Nintendo 64), compiled from
built on the [GoldenEye 007 decompilation](https://github.com/n64decomp/007).** the [GoldenEye 007 decompilation](https://github.com/n64decomp/007) — the
It is first and foremost a **research project on AI-agent collaboration in a original N64 game running from reconstructed source, not the Xbox 360 remaster.**
large, unfamiliar, low-level codebase** — how far two coding agents (a local 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 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 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 to run on a 64-bit desktop. See [Background](#background).
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).
Technically, it follows the architecture of the Technically, it follows the architecture of the
[Perfect Dark PC port](https://github.com/fgsfdsfgs/perfect_dark) — the same [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 &rarr; Archives.</em> Jungle &rarr; Archives.</em>
</p> </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 ## Contents
- [Quick start](#quick-start)
- [Background](#background) - [Background](#background)
- [How this differs from the other GoldenEye PC projects](#how-this-differs-from-the-other-goldeneye-pc-projects) - [How this differs from the other GoldenEye PC projects](#how-this-differs-from-the-other-goldeneye-pc-projects)
- [Status](#status) - [Status](#status)
@@ -73,7 +91,7 @@ It used two agents:
- **Claude** (via **Claude Code** — mostly **Sonnet 5**, with **Opus 5** as an - **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 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 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: 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 | | Metric | Value |
|---|---| |---|---|
| Timeline | ~2 weeks, one person part-time | | Timeline | ~3 weeks, one person part-time |
| Commits | ~223 | | Commits | ~320 |
| Root-caused bugs logged | 162 (`D1`–`D169`; some later merged or withdrawn) | | Root-caused bugs logged | ~190 (`D1`–`D196`; some later merged or withdrawn) |
| Handoff sessions | ~33 | | Handoff sessions | ~49 |
| ABI edits to game code | 63 files, all `#ifdef PORT` | | ABI edits to game code | 69 files, all `#ifdef PORT` |
| Port layer / tooling | ~17k lines C/C++ · ~6k lines Python | | Port layer / tooling | ~17k lines C/C++ · ~6k lines Python |
| Effort split (milestone-weighted) | ~60% Claude · ~40% local model | | 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 8** — the whole intro renders
- **Day 13** — all 21 solo missions run crash-free - **Day 13** — all 21 solo missions run crash-free
- **Day 14** — front end playable end to end - **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": Full write-up, timeline chart, and an honest "what worked / what didn't":
[`docs/dev/agentic-development.md`](docs/dev/agentic-development.md). The [`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 | | **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 | | **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) | | **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 | | **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 **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 ## Status
> [!WARNING] > [!WARNING]
> **Not playable yet.** This is a work-in-progress research port. The intro and > **Alpha — playable, not polished.** This is a work-in-progress research port.
> menus render and all 21 solo levels load, but it is **not** a complete or > It runs the full single-player campaign, but there is **no audio**, some
> reliably playable game: there is **no audio**, the front-end 3D models and > front-end 3D models and cutscenes are broken, AI characters move too slowly,
> some text are broken, input has known rough edges, and you should expect > input has known rough edges, and two levels (Bunker ii, Statue) still crash.
> crashes and glitches once you are past the level intro. Treat it as a > Treat it as an early alpha of the porting work, not a finished way to play
> technical demo of the porting work, not a way to play GoldenEye. > GoldenEye.
**Phase 2 of 4 (rendering).** The port boots, renders, and is playable through **Phase 2 of 4 (rendering). First alpha: [v0.1.0](../../releases).** The port
the front end into the early game. It is not finished and it is not stable. boots, renders, and plays through the front end and the campaign. It is not
finished and it is not fully stable.
**Working** **Working**
- Boot → Rare/Nintendo logos → gun-barrel → cast intro, fully rendered. - Boot → Rare/Nintendo logos → gun-barrel → cast intro, fully rendered.
- Front end: main menu → mission select → difficulty → briefing → mission start. - Front end: main menu → mission select → difficulty → briefing → mission start.
- **All 21 solo missions load, render, and survive an unattended play window - **All 21 solo missions load and render.** In a full-campaign playtest on the
without crashing** (see [`docs/dev/LEVEL-STATUS.md`](docs/dev/LEVEL-STATUS.md)). 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, - Software RSP (fast3d): textured world geometry, skeletal characters, HUD,
the GE-specific color-combiner / render modes and `G_TRI4`. the GE-specific color-combiner / render modes and `G_TRI4`.
- Input: keyboard + mouse (with mode-aware mouse-look) and SDL game - Input: keyboard + mouse (with mode-aware mouse-look) and SDL game
controllers, mapped onto the N64 pad. Tunable via `ge007.ini`. controllers, mapped onto the N64 pad. Tunable via `ge007.ini`.
- File-backed EEPROM saves. - 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** **Not yet working**
- **Audio** — not implemented (Phase 3). The game runs silent. - **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 - Some front-end 3D models — the spinning Nintendo logo, and the MISSION
COMPLETE / mode-select models — are mispositioned or absent. (The COMPLETE / mode-select models — are mispositioned or absent. (The
gun-barrel Bond intro renders correctly.) gun-barrel Bond intro renders correctly.)
- Assorted cosmetic defects (some text layout, a few incomplete asset - Outdoor levels render with a **black sky**; assorted other cosmetic defects
conversions) are tracked in are tracked in
[`docs/dev/GRAPHICS-BACKLOG.md`](docs/dev/GRAPHICS-BACKLOG.md) and parked [`docs/dev/GRAPHICS-BACKLOG.md`](docs/dev/GRAPHICS-BACKLOG.md).
below crash/level work. - No macOS or ARM support; no controller rebinding UI; no widescreen.
- **Platform testing is Windows-only.** All development and playtesting has
been on `x86_64` Windows (MSYS2/MINGW64). The CMake build has Linux and macOS Next up: audio (Phase 3), the AI-pacing fix, and the two remaining level
paths and is *intended* to be portable, but the port has had **zero crashes. Cosmetic defects are tracked in
functional testing on Linux or macOS** — the Linux build is provided as-is [`docs/dev/GRAPHICS-BACKLOG.md`](docs/dev/GRAPHICS-BACKLOG.md).
and unverified at runtime. No ARM testing; no controller rebinding UI; no
widescreen.
## Download ## Download
There are no pre-built binaries yet. The project is Phase 2 WIP with the Pre-built **Windows** and **Linux** `x86_64` bundles are published under
caveats in [Status](#status) above — nothing runs end to end — so the only way [Releases](../../releases), starting with **v0.1.0**. Each contains the engine
to try it is to build it yourself; see [Building](#building). You supply your executable, its runtime libraries, and the one-time `prepare-assets` tool —
own ROM (see [Requirements](#requirements)) and drop it in `data/` before **no ROM and no game assets**. See [Quick start](#quick-start) for the four
building. Pre-release **Windows x86-64** bundles (engine executable plus steps to get it running, [Requirements](#requirements) for accepted ROMs, and
runtime DLLs, no ROM or game assets) may be published under [Status](#status) for the alpha caveats.
[Releases](../../releases) once the port is playable.
You can also build it yourself; see [Building](#building).
## Requirements ## Requirements
@@ -293,9 +322,9 @@ touch N64 hardware is redirected into `port/`:
- **`port/src/{video,audio,input,fs,romdata,config}.c`** — the SDL2 / OpenGL / - **`port/src/{video,audio,input,fs,romdata,config}.c`** — the SDL2 / OpenGL /
filesystem backends. 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 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 Where it diverges from the Perfect Dark port: GoldenEye's N64 serialized asset
formats (level setup, models, backgrounds) are converted **offline** by a set 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", - No official logos, box art, or marketing assets are used. "GoldenEye 007",
"007", "James Bond" and related marks belong to their respective owners "007", "James Bond" and related marks belong to their respective owners
(Nintendo, Microsoft/Rare, MGM, Danjaq, EON Productions). (Nintendo, Microsoft/Rare, MGM, Danjaq, EON Productions).
- **If** pre-built binaries are published under [Releases](../../releases) in - **Pre-built binaries** published under [Releases](../../releases) contain
the future, they will contain only the engine — the `port/` layer plus the only the engine — the `port/` layer plus the compiled decompilation, with no
compiled decompilation, with no game data of any kind — bundled with game data of any kind — bundled with permissively-licensed runtime libraries
permissively-licensed runtime libraries (SDL2, zlib, the MinGW runtime; their (SDL2, zlib, the MinGW runtime; their licenses travel in the download). Any
licenses travel in the download). No such release exists yet, and any build — build — yours or ours — is useless without a ROM you supply.
yours or ours — is useless without a ROM you supply.
This project is **not affiliated with, endorsed by, or sponsored by** Nintendo, This project is **not affiliated with, endorsed by, or sponsored by** Nintendo,
Rare, Microsoft, MGM, Danjaq, EON Productions, or any rights holder in Rare, Microsoft, MGM, Danjaq, EON Productions, or any rights holder in
+3 -3
View File
@@ -13,8 +13,8 @@ Stages:
files the port loads at runtime. **Required to run.** files the port loads at runtime. **Required to run.**
You need a GoldenEye 007 N64 ROM you legally own (`.z64`, big-endian). See the 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 [Requirements table in the README](https://github.com/jkdansereau/goldeneye-pc-port#requirements)
versions and hashes. 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) | | `-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) | | `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). [`dev/GE-ENV-PROBES.md`](dev/GE-ENV-PROBES.md).
+2 -2
View File
@@ -1,7 +1,7 @@
# How this port is developed # How this port is developed
Most of this port's work is not writing code — it is **diagnosis**: figuring 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 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 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. 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/) [*"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 (Snowboard Kids, 2026), an AI-assisted decompilation that ran several times
faster than its predecessor. That project bit-matches compiler output; 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. project-structure lessons carry over directly.
## 1. Every investigation task gets a visible budget ## 1. Every investigation task gets a visible budget
+1 -1
View File
@@ -1,7 +1,7 @@
# GE_* environment probes — consolidated list # GE_* environment probes — consolidated list
Every `getenv("GE_…")` in the tree, from a grep of `src/` + `port/` on 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 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 `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 for is closed* — the probe is strip-candidate scaffolding, not that it does
+8 -6
View File
@@ -14,17 +14,19 @@ once the game is playable start-to-finish without crashes.
| **D76** | 2D disclaimer screen only partially drawn. | §F D76. | | **D76** | 2D disclaimer screen only partially drawn. | §F D76. |
| **D77** | No audible music on PC. | Phase 3 (audio). §F D77. | | **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. | | ~~**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. | | **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. | | **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. | | **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). | | **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). | | **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. | | **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. | **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. | | **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" 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). | | **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). | | **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) ## Resolved (for reference)
+10
View File
@@ -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 0x140000000). "render %" = `tools_pc/pixcount.py` non-clear on the last
captured frame. Binary at `f2beae4b` + this session's build. 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 **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 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% crash no longer reproduces on a bare `-level_54` boot (Cuba rendered 92.5%
+5
View File
@@ -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 | | 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 | | 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) | | 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 | | 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. | | 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). | | 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). |
+11 -11
View File
@@ -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 Community-documented account of how the retail N64 game is *supposed* to
behave: the combat/AI/stealth model, difficulty scaling, per-level objective behave: the combat/AI/stealth model, difficulty scaling, per-level objective
sets, timers, weapon data, and the original game's known quirks and glitches. sets, timers, weapon data, and the original game's known quirks and glitches.
**Status of this document.** This is secondary source material, not ground **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 `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, 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. regression.
**Sources** (subjective commentary stripped; mechanical/factual data only): **Sources** (subjective commentary stripped; mechanical/factual data only):
@@ -20,7 +20,7 @@ regression.
Distilled from `goldeneye_007_reference.md` (the full raw walkthrough dump); Distilled from `goldeneye_007_reference.md` (the full raw walkthrough dump);
the walkthrough's per-stage tactical routes were dropped as not 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. never alert.
- **Security cameras**: if a camera acquires the player it raises an 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 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 - **Retreating** breaks pursuit — most guards stop following after a set
distance, letting the player reload / heal / reposition. 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, - 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 even point-blank — it must path around the obstacle. **Exception:** a guard
@@ -97,7 +97,7 @@ Relative to the lower tiers:
## 3. Player state ## 3. Player state
| System | Behaviour | | System | Behavior |
|---|---| |---|---|
| Health | restored by health packs; no regen. 00 Agent pool ≈ 8 KF7 hits (§1). | | 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. | | 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 ## 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.) 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 | pistol | 7 | 800 | default sidearm |
| PP7 (Silenced) | pistol | 7 | 800 | no alert on fire | | PP7 (Silenced) | pistol | 7 | 800 | no alert on fire |
@@ -284,7 +284,7 @@ noted. **All Guns** cheat unlocks everything.
| Object | Rule | | Object | Rule |
|---|---| |---|---|
| Red alarm boxes | shoot to complete "neutralize all alarms"; a guard reaching one spawns reinforcements | | 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 | | 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 | | 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) | | 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 Ourumov has no objective use, but "firing" the key (action button) near the
rocket opens the giant launch doors. Otherwise both items are inert. rocket opens the giant launch doors. Otherwise both items are inert.
- **Cut content left in levels**: the Dam has the unused geometry of a - **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 Alec/Trevelyan leads nowhere; the Facility's starting vent has a second path
blocked purely cosmetically and shootable ceiling glass. blocked purely cosmetically and shootable ceiling glass.
+5 -3
View File
@@ -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 part-time through ~230 translation units of unmodified big-endian MIPS game
code, handing work back and forth through shared written notes. code, handing work back and forth through shared written notes.
**Status:** Phase 2 of 4 (rendering). Boots, renders the full intro and front **Status:** Phase 2 of 4 (rendering); first alpha **v0.1.0** released. Runs the
end, loads all 21 solo missions. No audio yet; not playable end to end. 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. - **[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. - **[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. - **[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. - **[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. - **[Building](building.md)** — full build and asset-extraction guide.
+4 -3
View File
@@ -1,15 +1,16 @@
# GoldenEye 007 — PC Port: architecture &amp; plan # GoldenEye 007 — PC Port: architecture & plan
> This began as the pre-implementation research note and is kept as the > This began as the pre-implementation research note and is kept as the
> architecture reference. Sections 1–9 describe the design; section 10 lists > architecture reference. Sections 1–9 describe the design; section 10 lists
> external references. The phased plan in section 8 is largely done through > 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 > [`dev/LEVEL-STATUS.md`](dev/LEVEL-STATUS.md); for the blow-by-blow finding
> log see [`dev/findings.md`](dev/findings.md). > log see [`dev/findings.md`](dev/findings.md).
This document captures the research done to plan a PC port of This document captures the research done to plan a PC port of
GoldenEye 007, based on the decompilation this repository is forked from 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 approach used by the
[Perfect Dark PC port](https://github.com/fgsfdsfgs/perfect_dark), [Perfect Dark PC port](https://github.com/fgsfdsfgs/perfect_dark),
which ports the same Rare "Indy" engine (a later revision) to modern which ports the same Rare "Indy" engine (a later revision) to modern
+5 -5
View File
@@ -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 from ROM bytes misaligns; allocating it N64-sized overruns adjacent
state. state.
- Symptom: garbage pointer deref, corrupted neighbour field, delayed - Symptom: garbage pointer deref, corrupted neighbor field, delayed
fault far from the cause. fault far from the cause.
- Fix: store embedded ROM addresses as `u32`, cast at use site (PD ground - Fix: store embedded ROM addresses as `u32`, cast at use site (PD ground
truth); or give real inline storage / `sizeof()`-based alloc under 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 logic). 2D pixel space is `viGetX()` x `viGetY()`. Return NULL when the
overlay is closed so the frame is byte-for-byte unchanged. 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). nothing in fast3d; must emit `G_CLEAR_DEPTH_EXT` (D105).
- LOD / detail mip tiles: fast3d fabricates a crop when detail textures - LOD / detail mip tiles: fast3d fabricates a crop when detail textures
are off → force base tile (D107). are off → force base tile (D107).
- **A CI-format tile drawn with the TLUT disabled (`G_TT_NONE`) is not a - **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 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 `import_texture()` dispatched purely on `tile.fmt` and did a palette lookup
against a stale `rdp.palette` → garbage (D161, GE Depot ceiling = blue 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. 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". distinguish "the decoder is wrong" from "the source bytes are garbage".
`GE_TEXRAW=1` (D183) dumps the bytes as handed to `import_texture_*`. The `GE_TEXRAW=1` (D183) dumps the bytes as handed to `import_texture_*`. The
cheap offline test on such a dump: compute the mean vertical 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 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 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 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 wall-clock on PC, and GE is a variable-timestep sim
(`frametiming.c waitForNextFrame` → `deltaFrames` = 60 Hz ticks of real (`frametiming.c waitForNextFrame` → `deltaFrames` = 60 Hz ticks of real
time elapsed per render), so "frame N" differs 15–40 % between runs. Use 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 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` refresh `tools_pc/golden/`) — NOT an exact compare. A `GE_DETERM=1`
fixed-tick mode was assessed not-narrow (redesigns retrace/tick fixed-tick mode was assessed not-narrow (redesigns retrace/tick