* fix(D176a): sky scroll/tiling too fast -- perspective-correct tc interpolation skyPortRenderPoly (Path B, D176(a) black-sky fix) placed sky quad verts under a plain w=1 ortho projection, so fast3d's GPU pipeline interpolated texture coords linearly in screen space across the primitive. The source S/T data (unk20/unk24) is computed upstream from a world-space projection without a perspective divide -- exactly like the real N64 RDP's G_TRI_SHADE_TXTR coefficient block, which relies on real perspective- correct interpolation to divide it back out per pixel. A single sky quad's near (top) and horizon-grazing (bottom) vertices can differ up to ~38x in camera-space w. Under linear interpolation that smears the entire S/T delta evenly across every pixel instead of concentrating it near the horizon, producing a uniformly dense aliased 'hatching' over the whole sky -- reported as 'scrolls too fast / tiles too visibly'. Fix: build a custom projection matrix that gives each vertex its real camera-space w (already computed upstream, SkyRelated38.unk0c) while keeping the same screen-space x,y, so the GPU's normal perspective divide recovers the intended position but interpolates tc (and colour) with real 1/w weighting -- the same pipeline every other textured draw in the game already depends on. No port/fast3d changes; #ifdef PORT only, N64 path untouched. Verified: tools_pc/verify.sh bunker1 PASS (no regression on a non-sky level); -level_22 (Statue) and -level_41 (Cradle) GE_PCDUMP captures show the expected before/after change (uniform parallel aliasing -> a proper perspective-converging fan), and Cradle's sky is no longer black at default FOV. Defect 2 (horizon-band tessellation) and the FOV-coupled black-sky-past-frustum symptom are unchanged / still open. docs/dev/findings.md: new D176(a) subsection + summary-row update. docs/porting-notes.md: generalized the screen-space-overlay quirk to call out the w=1-vs-real-w interpolation pitfall. * chore(D176a): regen findings index * docs(D176a/D227): M-93 - human playtest of PR #43 confirms Cradle black-sky fix + partial Statue scroll improvement; new split-sky residual filed as D227 * docs(D227): M-94 - visual evidence + a concrete mechanistic hypothesis for the sky seam (per-primitive wScale quantization), not yet fixed Headless -level_22 capture (this shell can do offscreen GE_PCDUMP capture via the game binary directly, run from the repo root with the MSYS2 mingw64 PATH set -- the earlier GUI-launch flakiness in this session was specific to a live window, not this path) shows a real, static-frame crease in the sky's cloud texture. Screenshot committed: docs/img/bugs/d227-sky-seam-statue.png. Hypothesis: skyPortRenderPoly (D176(a)'s fix) computes wScale per call, and skyRenderTri/skyRenderFull issue up to 5 separate calls per frame (one per triangle in the sky fan). The position math is exact for any wScale (re-verified algebraically), but Vtx.ob[] is s16, so two triangles sharing an edge can quantize a logically-shared vertex to two slightly different values depending on which triangle's independently- chosen wScale computed it -- crack at the shared edge, reads as "two regions with a different pattern." Not yet confirmed (didn't isolate camera motion from sky-scroll motion to test the "moves at a different rate" half of the report) and not fixed -- documented the fix direction (one wScale shared across the whole fan, not recomputed per triangle) for whoever picks this up next. Cleaned up capture scratch files after pulling the one evidence shot. * docs(D227): M-94 addendum - static-camera test confirms the sky genuinely animates over time, independent of camera motion * fix(D227): M-95 - one shared wScale across the whole sky fan instead of one per triangle, closes the visible seam skyRenderTri/skyRenderFull draw up to 5 triangles per frame from a shared pool of up to 5 vertices (built once, then fan-triangulated across 1-3 calls). D176(a)'s fix had each call compute its own wScale from only its own 2-4 verts; two triangles sharing a vertex could each pick a different wScale, and since Vtx.ob[] is s16, quantize that logically-shared vertex to two different values depending on which triangle's call computed it -- a crack at the shared edge (D227, the "two sky halves" report). Fix: skyPortBeginFan()/skyPortEndFan() (new, #ifdef PORT) compute one wScale across the whole fan and skyPortRenderPoly uses it when set, falling back to the old per-call computation otherwise. N64 path unchanged (these are PORT-only additions around existing #ifdef PORT code, no #else branch touched). Verified: - Clean build, no new/duplicate symbols. - Full verify.sh sweep: 21/21 PASS, both with and without this change. - Same-frame before/after screenshots at -level_22 frame 345 (docs/img/bugs/d227-sky-seam-statue{,-fixed}.png): visible kink in the cloud streaks in the before shot, gone in the after shot, same exact frame. - bunker1's nonzero framediff worst_cell is unaffected by this change (reproduced identically pre-fix) -- confirmed pre-existing stale golden-baseline issue, unrelated to sky.c. - Cradle's black-rectangle artifact is also confirmed pre-existing (reproduced identically pre-fix) -- the separate, already-documented FOV-coupled black-sky-past-frustum-edge symptom (D218/D222 family), not touched by this fix. Not re-verified: whether the "moves at a different rate" half of the original report is also resolved, and the aspect-ratio-artifact alternate theory was never independently ruled out. Human eyeball still owed before merge, same as every other rendering fix landed this session without interactive display access. Also generalized the quirk (per-call vs per-shared-vertex-group scale quantization) to docs/porting-notes.md alongside the existing D176(a) entry. * docs(D227): M-96 - human playtest on Statue: seam/crack STILL PRESENT despite M-95 fix The M-95 wScale-sharing fix only verified the static per-primitive quantization theory (single-frame screenshot diff); it explicitly left the 'two halves move at different rates' temporal theory from the original report unverified. This playtest is that missing verification, and it says the seam is still visible in live play. D227 NOT resolved. PR #43 stays in draft pending investigation of the temporal/rate theory -- do not merge on the M-95 fix alone. * docs(D227): M-96 addendum - N64 sky should be near-static, this build scrolls extremely fast New data point from the same Statue playtest session, separate from the seam/crack: the user reports the intended N64 scroll rate is near-static, stronger than M-93's 'still reads too fast'. Bears on D176(a)'s fix magnitude itself, not just the D227 seam -- flagged as a possible shared root cause (wrong per-frame tc delta could drive both symptoms), not yet investigated. * fix(D227): M-98 - share sky S/T fold across whole fan, not per-triangle skyPortBeginFan() (M-95) shared wScale across a whole sky fan so shared vertices quantize position consistently, but skyPortRenderPoly's S/T fold (foldS/foldT -- floorf(minS/64)*64, keeps texel coords in s16 range) was still computed per-call, from only that triangle's own 2-4 verts. Same bug class as the wScale fix, one field over, never fixed -- which is why M-96's human playtest still saw the seam after M-95. Two triangles in one fan can floor a shared vertex's S/T to different multiples of 64 texels; nominally invisible (GL_REPEAT period 64), but each triangle bakes (S-fold)*32 into its own s16 Vtx buffer, so two folds 64 texels apart produce tc values 2048 apart -- a real crack at the shared edge. Fix: extend skyPortBeginFan()/skyPortEndFan() to also compute one shared fold across the whole fan, threaded into skyPortRenderPoly the same way as wScale. #ifdef PORT only, no game-logic change. Also measured (per M-96/M-97's ask): cross-correlated 11 consecutive frames at a static camera -> ~7 px/tick horizontal scroll at 640px capture width, a full screen-width sweep every ~1.5s. Confirms "extremely fast" quantitatively; root cause of that magnitude is not yet found (accumulator + vertex geometry are unmodified N64 logic, so if it's genuinely wrong the bug is in how the Path B substitution maps texel-ticks to screen pixels, not the tick rate itself -- next step spec'd in findings.md). Verified: build clean; -level_22 frame 345 (same frame M-95's before/after used) shows no visible crease vs. the pre-M-95 reference screenshot; verify.sh bunker1 PASS, worst_cell unchanged (known stale golden baseline, not a regression). Human eyeball still owed on both the seam-close and the scroll-speed question before PR #43 leaves draft. * docs(D176a): M-98 - Cradle side of PR #43 checked; tower starburst is Defect 2 Checked the Cradle side of the D227 fold-sharing fix (M-97 left this outstanding). No regression -- verify.sh cradle PASS -- but found a much more visible pre-existing artifact while there: looking up the suspension tower near spawn, roughly half the sky renders as a sharp radiating starburst instead of clouds. Confirmed pre-existing (pixel-identical with sky.c reverted to pre-M-98) and confirmed static, not the D227 "moves at different rates" symptom (pixel-identical 30 ticks apart at a fixed camera). This is the already-known, already-open D176(a) Defect 2 (horizon-band tessellation) -- the code's own fold-logic comment already predicts exactly this failure shape for a triangle spanning >1000 texels near the horizon, which looking straight up the tower produces. Not fixed (tessellation is separate, bigger work); logged with a concrete repro + screenshot so it's no longer "inconclusive." * docs(D227): M-99 - human playtest negative (seam persists, view-angle-dependent); headless repro blocked Human playtest after M-98's fold-sharing fix: the seam is still present on Statue and now confirmed to move/change with mouse/view position -- also reported still bad on Cradle and present on Surface. M-98's fix was real but is not the dominant mechanism live play is hitting, same pattern as M-95 before it. Ruled out this pass: the cross-quad water/cloud texture-scale mismatch theory, on the two IsWater==0 levels actually tested (Statue, Cradle) -- untested on any IsWater==1 level, which is now the top ranked candidate for next session. Blocked on: headless camera-pan repro via GE_INPUTSCRIPT does not work -- confirmed neither analog-stick nor C-button tokens rotate the camera, so this needs either a human playtest or a debug hook into the real mouselook path before it can be chased further headlessly. Full ranked candidate list + environment notes for next session in findings.md and (session-local) docs/HANDOFF.md. * docs(D227): M-100 - root cause PROVEN: s16 texture-coordinate overflow The sky seam is not a crack, not a quantization mismatch, and not view-dependence. One sky quad spans ~30,000 texels in S and T, but Vtx.tc is S10.5 in an s16 = +/-1024 texels, so 7 of its 8 texture coordinates overflow and wrap to arbitrary values. The fold cannot help against a span 29x the representable range. Unblocked by a user-supplied emulator-vs-ours screenshot pair on Surface: the real symptom is a radial starburst, not a crack, which is a different mechanism from what M-94..M-98 were chasing. M-95 (shared wScale) and M-98 (shared S/T fold) each fixed a real second-order bug; neither could touch this one. Both stay - per-fan consistency is exactly what will make subdivision seamless. Measured with a new env-gated GE_D227V probe (GE_D176 style) on the Cradle frame-350 camera. All four w are positive, so no projective pole. The quad is split gSP2Triangles(0,1,3, 0,3,2) on the 0-3 diagonal, and vertices 0 and 3 are exactly the two that keep one coordinate in range - hence "two halves with different patterns, split by a line", the original D227 report verbatim. Apparent view-dependence needs no view-dependent mechanism: S/T depend on view, so the wrap point moves. Confirmed by a diagnostic build: baking tc at 1-texel instead of 1/32-texel granularity (32x range, deliberately wrong scale) makes the starburst and the split completely disappear. No second defect behind it. D227 == D176(a) "Defect 2" == M-98's "Cradle tower starburst". M-98 misfiled that capture as unrelated, which is what made D227 look headlessly-unreproducible in M-99 - it reproduces statically. Fix not implemented: subdivide until span < ~960 texels/primitive. The subdivision math and cut placement are worked out in the finding (S is linear in w, so cut uniformly in w), but the vertex budget is an open decision - dynAllocateVertices is an unchecked 50 KB/frame pool and pure tessellation wants ~40% of it. * docs(D227): M-100 addendum - RDP tile shift (option D) dominates; fast3d already implements it shifts/shiftt is the RDP's native precision-vs-range knob and gfx_pc.cpp implements it fully including the left-shift case. Emitting tc at 32/2^k units with shifts = 16-k buys 2^k range with no tessellation, no fast3d change and no vertex-budget cost. k=5 covers the measured 29,830-texel span outright. * fix(D227): M-100b - sky texcoords are S10.5, not texels; drop M-82's stray *32 Fixes the sky seam/starburst, the over-tiling, and the "scrolls too fast" complaint - all three were the same bug. M-82 ("D176(a) Defect 1") read unk20/unk24 as texel counts and baked tc as (S - fold) * 32 to convert texels into tc's S10.5 units. They are not texel counts. skyRender sets them as worldpos * 0.1f and the N64 path hands them to the RDP coefficient block unscaled (skyRenderTri: S * unk34 * sp368), so they are already in the RDP's native S10.5 1/32-texel units. That stray *32 made every sky texture coordinate 32x too large, producing three symptoms previously tracked as separate bugs: 1. D227 - spans of ~30,000 texels against an s16 tc holding +/-1024, so 7 of a quad's 8 coordinates overflowed and wrapped to arbitrary values. The quad splits on the 0-3 diagonal and verts 0 and 3 are exactly the two that keep one coordinate in range, hence "two halves with different patterns" - the original D227 report verbatim. 2. Over-tiling - ~466 repeats of the 64-texel cloud per quad, not ~15. 3. M-96's "the N64 sky is nearly static, ours races", measured at ~7 px/tick in M-98. g_SkyCloudOffset advances 1 unit/tick = 1/32 texel, not 1 texel. This is why M-98's re-derivation of the tick path kept coming back clean: the accumulator was never wrong, its unit was. Fix: tc = (S - fold) 1:1, with the fold rebased to a whole tile period (SKY_TC_WRAP = 64 texels * 32 = 2048 units). The ~30,000-unit span then fits an s16 unaided - which is also why the original N64 code needs no tessellation here: it never had a range problem to solve. Kept: M-95's shared wScale and M-98's shared fold (both real bugs; per-fan consistency keeps a fan's shared vertices bit-identical). Also kept as a safety valve, since only ~4% of s16 headroom remains on the observed span: skyPortPickShift/skyPortCaptureTile/skyPortEmitTileShift, which trade tc precision for range via the RDP's own tile shifts/shiftt (port/fast3d already implements it, so no fast3d change). skyPortCaptureTile replays the exact G_SETTILE texSelect emitted with only the shift fields patched, so it cannot drift from texSelect's format/line/mask derivation. Also adds the env-gated GE_D227V per-vertex probe (GE_D176 style) that measured all of this. Verified: -level_41 Cradle frame 350 (the M-98 starburst repro) now shows soft cloud bands at the correct scale with no starburst, diagonal split or moire; GE_D227V confirms kS=kT=0 and max tc 31,513 of 32,767. -level_22 Statue frame 345 clean. verify.sh bunker1 PASS (worst_cell 10.32, the known stale-golden-baseline number). Human play-test still owed. * D229: file green/pulsating water; decouple D227's tc safety valve from it New finding D229: on IsWater levels (Frigate, Surface 2) the water renders green and pulses blue<->green on an interval. Pre-existing, proven by A/B: built plain main with none of the D227 work and captured -level_26 frame 340 - same green, only the tiling density differs. D229 is independent of D227. The gun half of the same report was NOT a bug: the branch was 2 commits behind main and those 2 commits were exactly the D217 fixes (#47 texpool alignment, #49 CI cache palette-content key). After merging main the user confirms the gun is good on Frigate. Mechanism so far: the water quad is sky.c's IsWater block1 (Frigate confirmed IsWater=1 via GE_D176, which also settles M-99's ranked item 1). sub_GAME_7F09343C binds tile 0 and tile 1 to the SAME tmem, offsets tile 1 via gDPSetTileSize(1, 90, 150, 0, 0), and cross-fades TEXEL0/TEXEL1 with a PRIM_LOD_FRAC driven by sinf(). That sine is the reported pulsing, so one texunit resolves blue and the other green. Ruled out the shade path by measurement (it spans dark blue to pale yellow, never green; sampled pixels have R and B crushed). Not yet root-caused. Code change here is only the decoupling: skyPortBeginFan takes allowShift and the water quad passes FALSE, leaving that path byte-identical to its pre-D227 behaviour. The water quad is a two-texunit draw sharing one set of vertex tc, so a tc rescale needs a matching shift on both tiles - and shifting both makes them sample identically, which flattens the cross-fade. An intermediate build that did so turned the water from pulsating into constantly green, observed live. Also fixes skyPortCaptureTile to capture after all tile setup (not just after texSelect) and to handle both tiles, so a future shift cannot clobber a later re-declaration. Cradle sky re-verified unaffected (frame 350 still correct). bunker1 golden framediff reports REGRESSION at 55.02, but plain main scores WORSE at 73.24 - the baseline is stale relative to main's D217 texture fixes, the re-baseline owed since M-83/M-84. Not caused by this branch. * docs(D227/D229): regenerate findings index; mark D227 FIXED The generated findings-index.csv was hand-edited during the main merge and when D229 was filed, so CI's 'Check generated dev indexes are current' failed on PR #43. Regenerated with tools_pc/gen_findings_index.py. Also updates the D227 row's status to lead with FIXED (M-100b) rather than the M-94 'not yet fixed' text, so the index reflects the user-confirmed fix. * docs: register GE_D227V in the env-probe index CI's 'Check generated dev indexes are current' also runs gen_env_probes.py --check, which flags any GE_* probe with live sites that has no row in docs/dev/GE-ENV-PROBES.md. GE_D227V (added in M-100 to measure the sky texcoord overflow) had none.
GoldenEye 007 PC Port
A native PC port of GoldenEye 007 (Rare, 1997, Nintendo 64), compiled from the GoldenEye 007 decompilation: the original N64 game running from reconstructed source, not the Xbox 360 remaster. The first alpha, v0.1.0, is out for Windows and Linux and runs the full campaign with rough edges (see Status).
It's also a case study in AI-agent collaboration on a large, low-level codebase: two coding agents, driven by one person part-time, porting ~230 translation units of unmodified big-endian MIPS game code to a 64-bit desktop. See Background.
Technically, it follows the architecture of the
Perfect Dark PC port, the same
Rare "Indy" engine family, one hardware generation apart. The unmodified game
C sources are compiled for the host; the N64's Reality Signal Processor (RSP)
is emulated in software; every other hardware surface (video, audio, input,
timers, save storage) is shimmed in a dedicated port/ layer.
Important
You must supply your own GoldenEye 007 ROM. This repository contains no Nintendo code or assets, and no ROM. Nothing here is distributable as a playable game — see Requirements and Legal.
In-engine, running in the port — Bunker 1 and Dam attract views, and a
~15 s clip (the clip itself has no audio track): mission dossier →
Facility → Silo → Jungle → Archives.
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:
- Download the Windows or Linux bundle from Releases and unpack it.
- Make a
data/folder next to the executable and drop the ROM in asge007.ntsc-final.z64. - Run the one-time asset step:
python3 prepare-assets/prepare-assets.py(Python 3.8+, stdlib only). - Launch the executable from that folder.
Building from source instead: see Building. Read the Status caveats first — this is an early alpha.
Contents
- Quick start
- Background
- How this differs from the other GoldenEye PC projects
- Status
- Download
- Requirements
- Building · Windows (MSYS2) · Linux
- Running · Default controls
- How it works
- Project layout
- Documentation
- Credits
- Legal
- License
Background
The port used two agents handing work back and forth through shared written notes (a running handoff doc), directed by one person part-time:
- a local open-weight model —
unsloth/Qwen3.8-27B-GGUF:UD-Q4_K_XLon a single RTX 5090, driven mainly through the pi coding agent — which did the groundwork: the CMake build, the boot chain, the software-RSP integration, the offline asset-conversion pipeline, and the first rendered frames; - Claude (via Claude Code — mostly Sonnet 5, with Opus 5 as an escalation tier for the hardest bugs), which joined for a collaborative phase covering the 21-level load/render/no-crash sweep, the ABI/layout finding catalog, the SDL input layer, and the front-end flow.
In short: ~3.5 weeks, one person part-time, two agents, ~430 commits, 200+
root-caused bugs logged. The full write-up (timeline, the handoff mechanism,
commit/effort breakdown, and an honest "what worked / what didn't") lives in
docs/dev/agentic-development.md. The
workflow itself: docs/dev-process.md. To cite this
project or its findings, use CITATION.cff (GitHub's "Cite
this repository" menu).
How this differs from the other GoldenEye PC projects
This is a native port of the original 1997 Nintendo 64 game, built from its actual reconstructed source code, the same lineage as the Perfect Dark PC port. The other well-known "GoldenEye on PC" projects are something different: they machine-translate the shipped binary of the unreleased Xbox 360 XBLA remaster, a different game on a different codebase with no shared code with this one.
| This project | GoldenEye-Recomp / Steam Deck build | |
|---|---|---|
| What it ports | The original Nintendo 64 game (1997) | The Xbox 360 XBLA HD remaster (built ~2007, never released) |
| How | Decompilation-based source port — human-reconstructed C, compiled for the host; game logic runs as written | Static binary recompilation — the shipped machine code is auto-translated to C; no source-level understanding |
| Lineage | GoldenEye 007 decompilation + Perfect Dark PC port engine family | Xbox 360 "…Recompiled" static-recompilation family |
| Renderer | Software RSP → OpenGL | Hardware (Vulkan) |
| Status | WIP alpha; see Status | Playable full game, higher frame rates, online multiplayer |
| Why it exists | A case study in AI-agent collaboration on a hard low-level codebase | A polished, playable PC release of the remaster |
If you just want to play GoldenEye on PC today, use one of the recompilation projects: they are finished and this is not. What's here is the other half, getting the original game running from source.
Status
Alpha — playable, not polished. It runs the full single-player campaign with no known crashes, but some front-end 3D models and cutscenes are broken, in-level music doesn't play yet, and input has known rough edges. A full end-to-end re-confirmation that all 21 missions are completable start to finish is still owed (see below). Treat it as an early alpha of the porting work, not a finished way to play GoldenEye.
Phase 3 of 4 (audio + input) is in progress; Phase 2 (rendering) is essentially done. First alpha: v0.1.0. The port boots, renders, and plays through the front end and the campaign, but is not finished or fully stable.
Working
- Boot → Rare/Nintendo logos → gun-barrel → cast intro, fully rendered.
- Front end: main menu → mission select → difficulty → briefing → mission start.
- All 21 solo missions load, render, and are crash-free. A v0.1.0-era
full-campaign playtest found two crashing levels (Bunker ii, Statue, one
root cause) and an AI-pacing bug that blocked the final level (Cradle) from
finishing; both were subsequently root-caused, fixed, and playtest-verified
(D191, D193) — a full re-playthrough confirming all 21 missions completable
start to finish on the current build is still owed. See
docs/dev/LEVEL-STATUS.md. - Software RSP (fast3d): textured world geometry, skeletal characters, HUD,
the GE-specific color-combiner / render modes and
G_TRI4. - Input: keyboard + mouse (with mode-aware mouse-look) and SDL game
controllers, mapped onto the N64 pad. Tunable via
ge007.ini. - File-backed EEPROM saves.
- Audio — software mixer (libultra audio layer → SDL, adapted from the PD port). In-level sound effects play; in-level music does not play yet (tracked as D77). Front-end/menu audio is unaffected.
- Windows and Linux (
x86_64). Windows is the primary development and playtest path. The Linux build was built and smoke-tested (boots, renders, passes the level sweep) at v0.1.0; it has not been actively maintained or retested since.
Not yet working
- In-level music — silent; see above (D77).
- Cutscenes — frequently glitch: skipped, wrong camera, misplaced or hovering actors, wrong timing (D148/D160).
- Some front-end 3D models — the spinning Nintendo logo, and the MISSION COMPLETE / mode-select models — are mispositioned or absent. (The gun-barrel Bond intro renders correctly.)
- Outdoor levels' sky rendering is still being fixed (was solid black; now
partially fixed with residual defects); assorted other cosmetic defects are
tracked in
docs/dev/GRAPHICS-BACKLOG.md. - No macOS or ARM support; no controller rebinding UI; no widescreen.
Download
Pre-built Windows and Linux x86_64 bundles are published under
Releases, starting with v0.1.0. Each contains the engine
executable, its runtime libraries, and the one-time prepare-assets tool —
no ROM and no game assets. See Quick start for the four
steps to get it running, Requirements for accepted ROMs, and
Status for the alpha caveats.
You can also build it yourself; see Building.
Requirements
You need a GoldenEye 007 (Nintendo 64) ROM that you legally own, in
big-endian (.z64) format, matching one of:
| Region | ROMID | ROM filename (in data/) |
SHA-1 |
|---|---|---|---|
| NTSC-U (US) | ntsc-final |
ge007.ntsc-final.z64 |
abe01e4aeb033b6c0836819f549c791b26cfde83 |
| PAL (EU) | pal-final |
ge007.pal-final.z64 |
167c3c433dec1f1eb921736f7d53fac8cb45ee31 |
| NTSC-J (JP) | jpn-final |
ge007.jpn-final.z64 |
2a5dade32f7fad6c73c659d2026994632c1b3174 |
US is the recommended and best-tested version.
The port also relies on the decompilation's asset-extraction step, which pulls the level, model, texture and music data out of your ROM at build time. That step, too, requires your ROM and is part of Building.
Building
Prerequisites: CMake >= 3.16, a C/C++ toolchain, SDL2, zlib, OpenGL, Python 3,
plus the decompilation's own build dependencies (an IRIX MIPS toolchain via
qemu-irix, used only for the one-time asset extraction). See
docs/building.md for the full walkthrough and the
asset-extraction details.
Windows (MSYS2)
# in the MINGW64 shell
pacman -S mingw-w64-x86_64-toolchain mingw-w64-x86_64-SDL2 \
mingw-w64-x86_64-zlib mingw-w64-x86_64-cmake \
mingw-w64-x86_64-python make git
git clone https://github.com/jkdansereau/goldeneye-pc-port.git
cd goldeneye-pc-port
# 1. extract assets from your ROM (see docs/building.md)
# 2. build the port
./build-pc.sh ntsc-final # or pal-final / jpn-final
Linux
Built and smoke-tested at v0.1.0; not actively maintained or retested since — the steps below are the intended path, not a continuously verified one.
sudo apt install build-essential cmake python3 libsdl2-dev zlib1g-dev libgl1-mesa-dev
git clone https://github.com/jkdansereau/goldeneye-pc-port.git
cd goldeneye-pc-port
# extract assets (docs/building.md), then:
./build-pc.sh ntsc-final
The executable is written to build-pc/ge007.x86_64 (on Windows,
build-pc/ge007.x86_64.exe).
Running
- Create a
data/directory in the repo root. - Put your ROM in it, named as in the table above
(e.g.
data/ge007.ntsc-final.z64). - Run the executable from the repo root:
./build-pc/ge007.x86_64.
Configuration is written to ge007.ini on first run.
Default controls
| Action | Keyboard / mouse | Controller |
|---|---|---|
| Move / strafe | W A S D / arrows |
Left stick |
| Aim / look | Mouse | Right stick |
| Fire (Z) | Left mouse / LCtrl |
Right trigger |
| Aim mode (R) | Right mouse / LShift |
Left trigger |
| Use / accept (A) | Space / E / X |
A / X |
| Reload / cancel (B) | R / F |
B / Y / RB |
| L trigger | Q |
LB |
| Start | Enter / Tab |
Start |
Mouse sensitivity, Y-inversion and the aim/turn split are tunable in the
[Input] section of ge007.ini.
How it works
The R4300 game code in src/ is compiled completely unmodified; the
decompilation's control flow is treated as ground truth. Everything that
would touch N64 hardware is redirected into port/:
port/fast3d/— a software RSP. It interprets the GBI display list the game builds each frame and emits OpenGL, bypassing the RDP. Adapted from the Perfect Dark port and extended for GoldenEye's custom GBI.port/src/gesched.c— replaces the RCP scheduler; instead of poking RSP registers it drives the software RSP directly.port/src/libultra.c— single-threaded shims for the libultra OS API (threads, messages, timers, PI/SI/AI/VI) over the host.port/src/{video,audio,input,fs,romdata,config}.c— the SDL2 / OpenGL / filesystem backends.
The 32→64-bit transition forces a small, cataloged class of mechanical ABI-only edits to ROM-serialized structs (pointer-width reconciliation); these change no behavior and are documented individually.
Where it diverges from the Perfect Dark port: GoldenEye's N64 serialized asset
formats (level setup, models, backgrounds) are converted offline by a set
of Python "sidecar" converters in tools_pc/, rather than fixed up at load
time. See docs/internals.md and
docs/porting-notes.md.
Project layout
CMakeLists.txt PC build (parallel to the decomp's Makefile, which is untouched)
build-pc.sh configure + build helper
src/ include/ the decompilation (game + libultra) - compiled unmodified
port/
fast3d/ software RSP -> OpenGL
src/ port layer (main, OS shims, video, audio, input, fs, ...)
include/ port-facing headers
tools/ Makefile the N64 build + asset extraction (from the decomp; do not modify)
tools_pc/ PC-port helper + analysis scripts
docs/ see below
Documentation
Key docs are also published as a site: https://jkdansereau.github.io/goldeneye-pc-port/.
| Doc | What's in it |
|---|---|
docs/building.md |
Full build + asset-extraction guide. |
docs/internals.md |
Architecture, the RSP-emulation approach, GE-vs-PD engine differences, the phased plan. |
docs/porting-notes.md |
The recurring N64→PC bug classes hit during the port, with fixes. |
docs/dev/agentic-development.md |
The research angle: the two-agent setup, timeline, handoff workflow, and an assessment of what did and didn't work. |
docs/dev-process.md |
The investigation workflow in detail — budgets, file partitioning, the finding-log discipline. |
docs/dev/ |
The raw engineering record: the full finding log, per-level status, graphics backlog, playtest matrices, and docs/dev/game-behavior-reference.md (a secondary-sourced playtest reference for how the retail game is meant to behave — repo-only, code is ground truth). |
docs/SetupGuide.md, docs/StyleGuide.md |
Inherited from the decompilation this repo forks. |
Credits
This port is a thin layer on a large amount of other people's work.
Prior work it is built on
- The GoldenEye 007 decompilation — years of effort by Larry Ficken ("kholdfuzion") and the project's contributors; plus zoinkity's GoldenEye documentation, which the decomp started from. This port is a fork of that repository.
- The Perfect Dark PC port
(Ryan Dwyer and contributors) — the reference architecture for this port and
the source of the
fast3dsoftware RSP. - The Perfect Dark decompilation — the sibling decomp the PD port is built on.
Vendored / adapted code
port/fast3d/— the software RSP, adapted from the PD port. It originates with the Ship of Harkinian / libultraship fast3d (© Emill, MaikelChan; MIT — seeport/fast3d/LICENSE.txt), which itself descends from sm64-port's fast3d and audio mixer.port/fast3d/glad/— OpenGL loader generated by glad (David Herberth, MIT).- The decompilation toolchain:
ido-static-recomp(Emill / decompals),qemu-irixandrabbitizer(n64decomp). - SDL2 and zlib.
Methodology
- Chris Lewis, "Decompiling a Nintendo 64 Game in 84 Days" (the Snowboard
Kids decompilation write-up) — the agent-workflow practices in
docs/dev-process.mdare adapted from it.
Tools and models used to develop the port
- Qwen 3.8 (Alibaba Qwen team), run locally as the
unsloth/Qwen3.8-27B-GGUFUD-Q4_K_XLquant; Unsloth (the GGUF quantisation and Unsloth Desktop, used as the local model server). - pi — the local coding-agent harness.
- Claude / Claude Code (Anthropic).
Legal
This is a non-commercial fan preservation/research project, in the same category as the many other N64 decompilation and native-port repositories on GitHub. It follows the same conventions they do:
- No ROM and no game assets are distributed — not in this repository and not in any release. Textures, audio, models, level data and in-game text are extracted from a ROM you already own, on your machine, at build time.
- The repository is a fork of the public
GoldenEye 007 decompilation and inherits
its contents unmodified (see
NOTICEfor what that includes). - No official logos, box art, or marketing assets are used. "GoldenEye 007", "007", "James Bond" and related marks belong to their respective owners (Nintendo, Microsoft/Rare, MGM, Danjaq, EON Productions).
- Pre-built binaries published under Releases contain
only the engine (the
port/layer plus the compiled decompilation, with no game data of any kind), bundled with permissively-licensed runtime libraries (SDL2, zlib, the MinGW runtime; their licenses travel in the download). Any build, yours or ours, is useless without a ROM you supply.
This project is not affiliated with, endorsed by, or sponsored by Nintendo, Rare, Microsoft, MGM, Danjaq, EON Productions, or any rights holder in GoldenEye or James Bond. If you are a rights holder with a concern, open an issue and it will be addressed.
License
The original work in this repository, the port layer (port/), the PC build
system, tools_pc/, and the documentation, is released under the MIT License;
see LICENSE. Everything inherited from the upstream decompilation
is covered by NOTICE, not by that license.