- v0.4.0 status framing (the most complete release to date; the common
defect classes fixed; a short list of known caveats) and the pre-1.0
note, on both the README and the Pages home page.
- Dated release news section with links to the GitHub release tags.
- Keyword-reinforced meta descriptions; site masthead tagline.
- Accuracy: options section list per the shipped overlay, Xbox 1.1 (Jinx)
controller roles (D394), Select opens the overlay, Deck 2x MSAA / 250%
preset, background stats, footer last-updated stamp.
11 rows per page now (was 13) so the bottom line of the paper is always
free for the page hint -- page 1 of a multi-page section shows
'Down: next page' after all (a full 13-row page had no room and no
hint, reported from the Deck). Hint wording drops the confusing
chevrons: 'Down: next page' / 'Up: previous page'.
Per user reports on the v0.4.0 front screen: the X/Y axis look
sensitivity (controller) labels ran into the fixed 232px slider track.
Bars now start 10px past each row's measured label (rows too long for
any track show the value only); drag hit-test and A-hold mapping use
the same per-row origin. Page 2 of a multi-page section now carries an
explicit dim 'Page p/N' marker right of the title plus a bottom hint
('Down: more rows >' / 'Up: previous page <') wherever the page has
room below; the (p/N) title suffix is retired. GE_FRONTOPTS_WIDTHS
one-shot label-metrics log added (same env-gated diagnostic
convention).
INPUT (17 visible rows) outgrew the fixed 440x330 paper: the 18px pitch
was sized before the D356 profile row, so a full page's last row landed
at y=335 (off the bottom of the screen) and rows past the silent 14-row
cap (Crouch mode, Reset to defaults, Bindings...) were unreachable from
the front screen. Sections now page at 13 rows (the largest that fits);
the vertical stepper crosses page boundaries in both directions and the
title carries a (p/N) marker. F10 overlay untouched (it scrolls its own
window). Headless verification (SkipIntro boot + scripted D-pad stepping
+ GE_FRONTNAVLOG): page 1 -> page 2 down and back up, all 17 rows
reachable. GE_FRONTOPTIONS_AUTO test hook added (diagnostic-only, same
convention as GE_FRONTOPTIONS_SECTION). Findings D406 + index; release
notes: options section list corrected to the shipped Input/Gameplay/HUD/
Graphics/Audio/Display and the D406 line added.
Plain declarative prose throughout the overview and section intros - no
product-page register, no first-person winks. Structure carries the period
feel: top anchor-nav line (middot separators), a dated news line for the
release, --- section rules, and a last-updated footer stamp.
- Docs site: drop jekyll-theme-cayman for a hand-rolled _layouts/default.html
(masthead band + middot text nav + footer with last-updated stamp and legal
line; head re-plumbed with {% seo %}, viewport, favicon); head-custom.html
fully rewritten to the chosen design tokens - charcoal/off-white/amber,
Libre Baskerville headings over system sans, square corners, 1px borders,
flat (no gradients/shadows), mermaid wrapper re-skinned.
- docs/index.md: v0.4.0 status (native widescreen, aim system, rebinding,
crosshair, rumble, rebuilt overlay); Honest status replaced by the v0.4.0
release-notes known issues; body anchor-nav line; legal footer moved to
layout.
- security-and-fidelity-status.md: front matter so {% seo %} titles it.
- README.md: v0.4.0 truth pass (status, Working/Known issues, Deck preset
2x MSAA / 250% per code, background stats 981 commits / 283 findings,
comparison table, GEPD-default controls paragraph, earlier builds) plus
markdown-native motifs matching the site: top anchor-nav line, --- section
rules, last-updated footer stamp, license badge in the amber accent.
- release-notes: SFTP sideload mention (leftover from the Deck session).
Port-layer only: resolve_msaa_color_buffer clamps msaa_level to the driver's
GL_MAX_SAMPLES (pre-fix: unallocated multisample renderbuffers -> black
screen when MSAA is set beyond driver support); the resolve/copy paths now
bind current_framebuffer ? framebuffers[i].fbo : 0 (the default framebuffer
is GL name 0, not framebuffers[0]). Affects both Windows (opengl32) and
Linux builds. Code file from the PR head (fc092d99); docs in house style,
label rebased D323 -> D405 (collision with our G_TL_LOD finding).
Port-layer only (port/src/input.c, optionsoverlay.c); zero src/game edits.
All new knobs off/neutral at default = byte-identical at defaults.
New controller input options (CONTROLLER-INPUT-PLAN Wave A):
- Input.PadLookSensX/Y: per-axis right-stick look sensitivity (100 = native)
- Input.PadSouthpaw: swap fire<->grenade triggers (applied after edge capture,
so edge-detection stays consistent)
- Input.PadDeadzoneL/R: per-stick deadzone; legacy Input.PadDeadzone key kept
registered (no row) + one-time migration in inputInit() seeds any unset L/R
from it (C-init sentinel PAD_DZ_UNSET) before the first configSave
- Input.PadLookSmooth: 0-10 per-pad EMA low-pass on the natural-pitch look
output (default 0 = off), reset on pad hot-unplug
- Input.MouseYScale: pre-existing registered key finally gets an INPUT row
Verbiage baseline (Turok PC options menu, user-provided verbatim): Mouse
horizontal/vertical sensitivity, X axis/Y axis look sensitivity, Look
smoothing, Southpaw, Vibration (was "Rumble strength"), Deadzone
(left/right stick). GE keeps one flat INPUT section (D356), so the two
Invert-look rows retain their (mouse)/(controller) suffixes.
Display baseline (Turok scales): sensitivity + deadzone rows render 0-100
with 50 = default (existing calibratedDefault path); look smoothing renders
0-10 via a new dispMax "N/M" row formatter (alongside the existing N/100 and
N/255 special cases).
Tested on a real pad (Windows build) - all six options behave as intended.
Low-hanging-fruit controller/input options ported from the Turok PC port's
menu, scoped as port-layer-only (zero src/ edits): right-stick look X/Y
sensitivity, southpaw trigger swap, per-stick deadzone (with the legacy
single-deadzone migration), look smoothing, and a mouse-vertical-sensitivity
row. All off-by-default (byte-identical at defaults, golden gate preserved).
Documents what the GE port already has, the per-item code shape + insertion
points in input.c, and the Tier B/C items explicitly out of scope (head bob,
bloom/shadows, death-cam, uncapped-FPS -> UNLOCKED-FPS-PLAN, controller
rebind -> PR #109).
After regenerating the Deck's pcmodels/pccg sidecars from the current
converter (M-197 blob fix + D303) and pushing them, the user re-checked
the front end on the Steam Deck and confirmed the Nintendo/GoldenEye logo
+ copyright/legal page now render correctly. D403 was the stale-sidecar
test artifact all along; no code was ever at fault.
The Deck bundle's stale 0.3.0-era pcmodels/pccg sidecars were regenerated
from the current Python-source converter (prepare-assets.py + d43/d69/d88,
ALL CHECKS PASSED) on the NTSC ROM and pushed to the Deck (sha256-verified),
preserving the user's ge007.eep + ge007.ini. Awaiting the user's by-eye
front-end recheck (logo/copyright page) to close the loop.
- D402: first clean Linux build fix (shim include-cycle + _SHIFTL).
- D403 (new): Deck front-end logo/copyright page broken + graphics not
updated is a test artifact — the bundle was seeded with 0.3.0-era
sidecars that predate the D75/M-197 blob fix and D303; not a code
regression. Disambiguation: regenerate the Deck's data/ sidecars.
- D404 (new): 'Aim style: Centred (PC)' is inert on a gamepad; the row's
doc comment over-promises (applies to pad trigger) but the centred
path is mouse-only (input.c:1783, D337 device-aware gate).
- Deck test confirmations: D175 gains a low-utilisation (~15-30%)
frame-pacing/present data point; D395 pad-binding retest satisfied
(not stuck); D401 rumble + D287 idle/resume-from-sleep confirmed on
the Deck's Steam Controller / SteamOS.
The release/v0.4.0 branch had never been compiled on Linux; two
port-layer include-resolution spots that MinGW tolerated break under
glibc. Shim-only fixes, zero src/ edits, no rule-2:
- port/shim/PR/ucode.h + port/shim/bondconstants.h: break the
bondconstants.h -> ultra64.h -> pc_protos.h -> bondtypes.h ->
bondconstants.h first-include re-entry by deferring pc_protos.h during
bondconstants.h's own ultra64.h pass (_PORT_DEFER_PC_PROTOS).
- port/shim/PR/gbi.h: define _SHIFTL/_SHIFTR when absent, so video.c's
gbi.h macros keep their mbi.h definitions on Linux.
Builds + links clean on Ubuntu 24.04 (glibc 2.39 <= SteamOS 2.41).
Per the user's release decision, keep the toggle (default OFF) but make it
impossible to miss in-game: the row is now labelled 'All unlocked
(EXPERIMENTAL)' and its ON value renders 'ON - UNSAFE' in both the F10
overlay and the front PC Options screen (both read the shared row table).
ON value now bright-highlights like a normal On. Port-only, display-only
(config still stores 0/1); a safe architecture is v0.5.0 work.
Housekeeping: track docs/dev/M7-ALLUNLOCKED-PLAN.md (records the decision);
gitignore the stray repo-root Python/ install; refresh the D387 status.
Port layer only (zero src/ edits), per docs/dev/RUMBLE-PLAN.md:
- port/src/input.c: inputRumbleSupported/inputRumble -> SDL_GameControllerRumble;
detection via SDL_GameControllerHasRumble (SDL >= 2.0.18) else
SDL_JoystickIsHaptic + known-type Windows fallback; zeroed on pad close.
Single global gRumbleScale (dflt 0.5) registered as Input.RumbleScale.
- port/src/libultra.c: the two M-87 blockers resolved - g_contStatus[i].status
now sets CONT_CARD_ON for connected rumble-capable pads (so
joyRumblePakInit arms) and osPfsInit returns PFS_ERR_DEVICE for them (so
osMotorInit is attempted); osMotorInit/Start/Stop drive the haptics API.
- port/src/optionsoverlay.c: one global 'Rumble strength' ROW_SLIDER
(Input.RumbleScale, 0.05 detents, %.2f) in the INPUT section +
kResetDefaults entry. The four per-player Input.PlayerN.RumbleScale keys
were dropped (user decision 2026-09-28); stale keys in an existing ini
log four 'unknown key' LOG_NOTE lines and self-clean on next configSave.
- Real-pad haptics confirmed working by the user (2026-09-28).
Build env: build-pc.sh self-heal guard revision - re-exec now writes a
self-contained .ps1 (msys->native argv conversion mangles $-bearing
-Command strings; file write is conversion-free), builds PATH explicitly,
picks TMP from writable candidates (GetTempPath honours the broken
inherited TMP), self-logs to build-pc/ge007-native-reexec-*.log, and
treats a null LASTEXITCODE as 'did not execute' (PS 5.1 piped native
commands error non-terminating). OPEN: the guard still fails inside
build-pc.sh while the identical standalone .ps1 invocation works - AGENTS.md
documents the workaround. docs: RUMBLE-PLAN.md (new, tracked), findings.md
D401 entry + index rows, QOL-INVENTORY D224 -> DONE (D401),
findings-index.csv regenerated.
Root cause H2 confirmed: the gun-barrel / file-select vertical gradient is
built from 0.75-native-pixel-tall gSPTextureRectangle row strips (title2.c
(<<2)-1 bottom idiom). On the N64 RDP the scan converter floors the U10.2
edges to whole scanlines, so each strip rasterizes to exactly one scanline;
gfx_draw_rectangle passed the fractional edges straight to the GPU's
continuous rasterizer, aliasing the 0.75px pitch into a periodic 'comb'
(fixed ~3-native-row period; window period = 3*scale — confirmed by a
3-scale 640/1024/1280 GE_PCDUMP test).
Fix (port-layer, surgical): after the sub-pixel offset, floor the top edge to
its scanline and the bottom edge to the last covered scanline (the RDP
scanline range [top_scan, bot_scan+1]) before NDC conversion. No-op for
whole-pixel rects (edges already 4k/4k-1), so only sub-pixel strips change
and normal fills are untouched.
Also: remove the temporary GE_D397SH / D397SHM env-gated shader probes
(gfx_opengl.cpp, gfx_pc.cpp) now that the root cause is settled.
Docs: findings.md D397 -> RESOLVED (+3-scale confirmation + fix record),
findings-index.csv regenerated, porting-notes.md section D15 marked resolved
with the landed fix.
Verified by playtest: gun-barrel + file-select gradient render clean, comb
gone.
New env-gated D397SHM probe in gfx_generate_cc dumps which G_CCMUX
inputs a combine feeds as vInput1..N. For the gun-barrow row-strip
shader (id0=0000000010001218): vInput1=PRIMITIVE, vInput2=ENV_ALPHA,
so the front.c LERP renders out = PRIM + (TEXEL-PRIM)*(envA/255).
The PD ground-truth port feeds the identical env_color.a/255 factor for
the same ENV_ALPHA LERP input, so the LERP factor is not a port-side
scaling bug (H1's 1/255-vs-1/32 variant refuted). A uniform factor
cannot produce the *periodic* comb, which points to H2 (row-strip
geometry / 256-tri vertex-ring wrap). Next: Q3 gfx_flush() probe +
Q5 native FBO capture. Findings §D397 + handoff updated.
Per scratch/d397_fs/HANDOFF-D397.md: the stale-RENDERTILE hypothesis is
REFUTED (D161 already routes CI + G_TT_NONE to the I8 import; the GE_TEXI
logs show palfmt=0 on all 299 row strips), so the D397 block added to
import_texture had no effect (12px frame diff) and is removed.
The finding's true state: data path proven clean (byte-exact I8 uploads
vs the ground-truth PGM); MSAA/mips/noise/quant/cache ruled out.
Leading cause H1 = the file-select custom-CC LERP feeding ENV_ALPHA 0x14
as a 20/255 mix factor (black texels render ~0.92x prim = the measured
27/28/17 census); the N64 RDP 5-bit coefficient scaling (gmain.s / PD
CC engine) is the open root-cause question. H2 = possible separate
row-strip-geometry comb. GE_D397SH probe stays in-tree until the root
cause lands. findings table + tail section + CSV corrected.
Root-caused the D397 interlaced/comb symptom on the file-select
background and intro gun-barrel (first display, not round-trip):
titleRenderFolderMenuBackgroundLines loads 299 one-pixel 440-byte I8
rows with gDPLoadTextureBlock(I, 8b) and samples G_TX_RENDERTILE but
never emits gSPSetTile, so the tile's fmt/siz is whatever the previous
screen left (CI/4b spectrum-screen leftover, D116 probe). fast3d ran
the I8 bytes through import_texture_ci4 against a stale/all-zero
16-entry palette -> periodic black rows (GL texture-cache artifact,
not a ground-truth dither — no ROM RLE decode needed).
- gfx_pc.cpp import_texture: route the import through the source's
fmt/siz when a CI/RGBA tile genuinely disagrees with an I8 8b load
(mirrors the D229 case); real CI/RGBA textures untouched.
- gfx_opengl.cpp: GE_D397SH env-gated shader-formula probe for the
file-select gunbarrel combine (label initially written as D398,
renamed to avoid the PC-Options-label finding collision).
- findings: D397 row -> FIX LANDED (by-eye + PCDUMP verification
owed); tail section notes the fix. CSV regenerated.
Cross-ref D182(2) (same symptom family), D159/D183 (disproven leads),
D229 (mirror-case precedent).
Input (input.c/h):
- D380/D386: single effective PC layout; ini values ARE the bindings
(no hidden defaults); versioned migration materialises effective
GEPD defaults once (inputMigrateBinds, bindsVersion).
- D385: keyboard keys and pad buttons 1..5 share the two bind slots;
legacy extra ini tokens are migration-only. D386: SDL button-number
correction in the D385 defaults.
- D383/D384: opt-in inputBindingProbe (restores ini state); the PC
settings editor must not navigate with live game keys.
- D393/D394: Xbox 1.1 Jinx face map — A use, X reload, Y weapon
cycle, B owned-gadget cycle (no N64 equivalent, port-side), L3/R3
crouch, LB aim; retires LB/RB weapon cycling.
- D395: both options UIs refuse pad-initiated keyboard/mouse capture;
pad B cancels a capture started by keyboard/mouse.
Options UI (optionsoverlay.c/h):
- D388: nested Input -> Bindings / Gameplay -> HUD roots in both
F10 and front-options; redundant Low-end toggle retired (FPS cap /
MSAA remain independent, stored values preserved on migration).
- D391: hit bands follow rendered glyph baselines; dead zone +
out-of-card click rejection.
- D396: F10 pad B backs/closes a page (closes at root), A/X
accepts/toggles without shifting sliders; left/right still adjust.
All behind #ifdef PORT; N64 input/menu paths untouched. M7 (D387
AllUnlocked save-safety) intentionally NOT in this wave.
gunDrawSight (gunfire.c, #ifdef PORT) now routes through portCrosshair*
accessors: hide flag, colour presets + custom RGB, 100% default size,
and an opt-in beta crosshair sprite. With all defaults the original
red sprite, 16-unit half-size and N64 combiner are untouched; non-PORT
builds keep the original literals and control flow.
- D373: crosshide/tint plumbing (was rule-2 sign-off PENDING at M2
authoring; the PORT-guarded pattern in gunDrawSight is the sign-off
record).
- D379: only recoloured reticles switch to the texture-alpha silhouette
combine (portCrosshairApplyTintCombine); the original keeps the N64
G_CC_FADEA commands.
- D381: size/style/colour surface + live config apply
(videoRequestLiveConfigForKey) in video.c.
Rule-2 framing: port-layer feature behind the gunDrawSight #ifdef PORT;
zero N64-path change.
chrlvUpdateAimendbackShoulders read the leading anim union of
weapon_firing_animation_table through raw (f32*) slots [12/13/16/17].
On PC the union widens four->eight bytes, so those offsets alias the
wrong fields and the intro soldier hunched with the hand through the
torso (D392, vs the 1964 reference).
The #ifdef PORT arm reads the intended limits/arm fractions by name
(config->max_up/max_down, free_arm_frac_up/down); the non-PORT arm is
untouched. No logic or behavior change beyond the pointer-width
misread; D392 finding + Train aim-angle probe confirm, user visually
verified against 1964.
- D398: replace D348 procedural slider glyph with ROM-native IMG_DOT
texture (texSelect + display_image_at_position, Copy/Erase pattern);
superseded by D400.
- D399: file-select bottom row shifted 15px left as a block (user
sign-off); selectpos.f[0] behind a new #ifdef PORT in front.c.
- D400: icon removed on user sign-off; PC Options is now a text-only
label exactly like Copy/Erase (one textMeasure + one textRender,
game's packed 0xEBD879FF gold). Fixed two measurement bugs:
textMeasure(textheight, textwidth) args were passed swapped (label
35px above the bar, hit band transposed) and the bare label measured
h=0 vs 14 for newline-terminated Copy/Erase (7px low); height now
measured with kLabelNL. LABEL_GAP 20->28, LABEL_X 343->351.
PCDUMP frame 195: tops aligned 279.8 vs 279.3, 31px clearance from
Erase. By-eye check owed.
- docs: findings.md had been truncated to its section table (686
lines), losing all ## Dxx tail sections D376-D400. Recovered:
table D376-D400 rows + body/sections D001-D375 from HEAD;
D376-D387, D396-D399 sections recovered verbatim from pi session
transcripts; D398 section recovered from the 2026-09-28 session;
D388-D395 rebuilt as explicit reconstructed stubs from the
table/CSV one-liners (original detail unrecoverable); D400 section
written fresh. findings-index.csv regenerated and verified
byte-identical to the pre-truncation copy.
Build clean (ntsc-final). Verified: PCDUMP frame 195 (no overlaps,
tops aligned), CSV matches pre-truncation backup.
bondview2.c reads crouchDown = insightaimmode && (buttons & (D_JPAD|D_CBUTTONS))
(5318/5579): crouch is C-down WHILE IN AIM MODE, never the B button. M3's
IA_CROUCH -> GE_CONT_B (and the CrouchMode=1 latch) was inert, so crouch
never fired in the GEPD layout. Hold + latch now emit GE_CONT_D (0x0004).
N64-faithful side effect: S (IA_BACK, also C-down) crouches while aiming.
User re-test: aim (RMB/Left Shift) + Left Ctrl; hold or F10 Toggle.
M2 (the wave's only rule-2 item, sign-off recorded via user direction):
#ifdef PORT hook in gunfire.c gunDrawSight -- Video.CrosshairHide skips
the draw, Video.CrosshairColor tints the RGB (7 named tints, 0 = the
N64 white); non-PORT builds keep the original literals. F10 GRAPHICS
rows + kResetDefaults.
D374 (M3 playtest fix, user decision): GEPD is now the DEFAULT key
layout (Input.Layout 0 = GEPD, 1 = N64-era binds; a mouse FPS layout is
the natural default and the N64 never had a keyboard). The preset is
now the true effective default: only EXPLICIT per-key [Input.Bind]
overrides beat it; persisted values equal to the N64 defaults count as
unset. This fixes the playtest bug where a stale persisted
Fire=Left Ctrl masked the GEPD preset and made Ctrl fire+crouch at the
same time. Boot log reports the applied layout. One-off: pre-D374
Layout=1 (ex-GEPD) inis mean N64 now -- flip to 0 (release note).
Video.LowEndMode (default off) in F10 VIDEO: ON writes FpsCap 30 +
MSAA x1 (restart row); OFF restores the compiled defaults (60/2) rather
than per-user pre-preset values. The key records the last selected
preset; the effect flows through the two real knobs.
Input.Layout (0=FPS default, 1=GEPD) + Input.CrouchMode (hold/toggle) rows
in F10 INPUT (Turok-standard functional section; the planned MODERN bucket
is dropped per D356). The GEPD preset acts as the per-key effective
default under [Input.Bind] overrides; new IA_RELOAD/IA_CROUCH actions emit
GE_CONT_B (unbound by default, N64-original). Toggle crouch latches B with
a menu-boundary latch drop. optionsoverlay rowSetCommit hook re-derives
the binds via inputLayoutApply(). inputRebuildBinds no longer warns on
intentionally-empty defaults. Boot + ini round-trip + F10 page verified;
in-game feel-check owed (D371).
'Game file' (the manual's term) reads, in modern PC usage, as the
program's own files, not the user's save. 'Profile' is the clearest
end-user term for what these files actually are -- a per-player
bundle of progress + per-file settings -- and '(per profile)'
expresses the tag's scope where '(save)' read as a verb.
Strings (both UIs: front options + F10 overlay):
- front top row: 'Save file' / 'File N' -> 'Profile' / 'N'
- section annotation: '(File N)' / '(no file)' -> '(Profile N)' / '(none)'
- per-file row tag: '(game file)' -> '(per profile)'
- F10 unavailable value: 'Select game file' -> 'Select a profile'
Display layer only: identifiers and ini keys (Bond.* rows,
watchSettings*, ini) are untouched. D357 plan section 4 is the
terminology record; new D359 finding + index row; manual
'game file' -> 'profile' sync noted as a TODO (port-owned doc,
not in this repo). Build + headless smoke clean.
Full-string audit of the settings UI (front options + F10 overlay):
- front top-row label 'Save file' -> 'Game file' (the instruction
manual's term)
- per-file row tag '(profile)' -> '(game file)', both UIs
- F10 unavailable value 'Select file' -> 'Select game file'
- kept: 'File N' values, the '(File N)' section annotation and the
'(no file)' unavailable state (short forms; the annotation's host
word is the section title, which says the file number)
- 'Bond file' now appears only in identifiers/config keys
(Bond.* rows, watchSettings* names, ini keys), never user-visible
Docs: D357 plan section 4 is now the terminology record (final string
table); D358 finding notes the (profile) -> (game file) supersession.
Build + headless smoke clean.
- The front options screen numbers items 0..s_rowN (0 = the top
save-file row, k = content row k-1), but the level-1 draw loop painted
content row k at rowY(k) -- the save row's own line -- and mapped
s_hl (item-numbered) straight into s_rowIdx (content-numbered): the
'Save file' row overlapped the first content row, highlights/adjusts
hit the wrong row, the last content row was dead, and s_rowIdx[s_hl]
could read one past the built rows. Content rows now draw at rowY(k+1)
and all s_hl -> s_rowIdx uses take s_hl - 1 (A-press, L/R repeat,
reset-maintain); hit-test/cursor were already item-numbered and are
untouched.
- Per-file row tag '(save)' -> '(profile)' (maintainer request), both
UIs (front options + F10 overlay) plus the stale comments.
- docs/dev/D357-SETTINGS-VALUES-PLAN.md (PROPOSAL): row-by-row audit of
current defaults/ranges/steps/displays vs the PD port (local checkout)
and the Turok PC list (maintainer notes); proposals: mouse sensitivity
as a x multiplier, deadzone as a whole-% key (D333 legacy-alias
pattern), texture filter '3-Point' -> Bilinear, audio step 128 -> 328
(whole-%), MSAA +16x, FPS-cap preset grid, FOV degrees; kResetDefaults
lockstep gate + GE_WSPROBE_RESET re-verification.
Build clean (248 targets), headless menu boot smoke clean; visual
check of the front options screen + F10 owed to the maintainer.
- 5 sections (INPUT/GAMEPLAY/GRAPHICS/AUDIO/VIDEO, all below MAX_PROWS):
DISPLAY+GRAPHICS merged into GRAPHICS; AUDIO carries the watch music/FX
sliders out of the Bond-file page; VIDEO keeps the pure video rows;
__HdrMouse/__HdrPad merged into INPUT; __HdrBond/__BondFile retired for
the front-only top save-file row (frontoptions.c)
- save-file scoping: per-file sections (GAMEPLAY, AUDIO) annotated
'(File N)'/'(no file)' via the new Row.saveScoped flag +
watchSettingsActiveFolder(); per-file rows in mixed sections carry a dim
'(save)' tag; the D353 optionsRowIsBondChooser special-case is
generalized (both UIs)
- N64-watch rows Bond.Look / Bond.AimControl commented out of the menu
surface (D181/D216 style: keys + watch paths stay live)
- per-section 'Reset to defaults' replaces the Turok rows (plan Gate E):
edge-triggered two-step arm->confirm within 3 s, one commit, disarm on
timeout / navigate-away / close (optionsResetMaintain/Clear);
rowResetSection walks the section's declared range: watch rows ->
BLANKSAVEDATA through the D352 commit path (front direct, stage queue),
ini rows -> kResetDefaults[] (the port's C initializers); __Resolution
is a documented exclusion (action-backed)
- GE_WSPROBE_RESET probe: front tick-120 prepare/dispatch/verify, stage
tick-120 dispatch / tick-121 verify; a direct -level_XX boot starts its
timer from phase 0 (front prepare is front-only)
- docs: findings.md D356 + index + CSV, WATCH-SETTINGS-PLAN status,
OPTIONS-MENU-PLAN pointer, plan doc status -> LANDED
Verified headless: GE_WSPROBE_RESET front (all 5 sections failures=0,
__Resolution untouched, file-2 scope isolation OK) and -level_33 (6
commands queued, all sections verified post-drain, saved music/sfx = 0xFF);
GE_WSPROBE / GE_WSPROBE_FRONT regressions clean; 248/248 build + link.
Add a game-thread bridge and field-scoped per-file persistence for PC UI edits, with an explicit file chooser; keep watch-only edits session-only. Fix D351 row initializer offsets for units and auto-hide. Document verification and review follow-ups.
No settings icon exists in the game art (mainfolderimages holds only
copy/del/select-arrow/X/check/dot), so a miniature slider control is drawn
instead: three menu-box tracks with offset knobs in the 14px gap after Erase
(10px wide, 2px margins), INK_DIM tracks, knobs matching the label ink
(white / gold while hot). Tracks the dynamic label x (JP-safe), text position
unchanged; hot region extended to include the glyph. PCDUMP pixel-verified.
D-pad left/right and stick-X now adjust the selected row's value (A/X/B/Y
kept); 18/4-frame hold-to-repeat added to value adjust and up/down nav,
matching the options screen's D345(e)/(f) cadence. Port-only; user
controller test verified.
D345 (a-g): re-assert window on menu change; cursorToItem skips snap while
the 1:1 pointer is live; W/S emit stick in menus under NaturalPitch;
F10-style discrete row stepping + arrow left/right value adjust replace raw
drift (first pass shipped broken, fixed); screen-placed cursor parks in the
left gutter off the row text.
D346 (+b): labels/values aligned to Nightdive/Turok + PD-port conventions
(title-case On/Off, Frame rate cap, Anti-aliasing/None, Anisotropic
filtering, units in values not labels, deadzone as %, self-explanatory
auto-FOV toggles); VIEW section merged into VIDEO (MAX_PROWS 12->15,
ROW_DY 20->18). Display-only: config keys and stored values untouched.
Port-only; no src/game edits. User playtest verified.
Rule-2 layout edit (user sign-off 2026-09-27): Copy 247/225 -> 222/200,
Erase 357/335 -> 304/282 under #ifdef PORT; their click boxes follow.
"PC Options" is drawn by the port hook right after Erase's measured text
end (min x 352), so wider localised labels push it right. The top-right
label is gone.
Replaces the M2 panel. Built like the Cheat Options screen: open dossier
(tabs + blank CLASSIFIED page), black Zurich ink via frontPrintText, GE's
highlight box, PREVIOUS tab as back, the game's crosshair. Page 0 is a
numbered section list; each section lists the F10 overlay's rows with a
value column (ON in the cheat red) and slider bars.
Game code (Rule 2, user sign-off 2026-09-27): MENU_PC_OPTIONS enum value
and five one-line dispatch cases, all #ifdef PORT. The file-select label
now enters the screen via frontChangeMenu. The M2 button hand-off in
input.c is gone; F10/Select don't open the overlay on this screen.
The file-select Options label opens a panel drawn in the same hook:
sections as tabs, rows from the F10 overlay's table via an exported row
API, the game's crosshair as pointer (input.c passes the stick, hands the
buttons to the panel). Quit to desktop now goes through videoRequestQuit.
Also repairs two escaped-newline typos in the D343 docs.
Quit (quit event, window close, Alt+F4, from either SDL pump) used to
exit(0) immediately while the render thread could be mid-frame in the
driver: the likely trigger of the host's 0x119 bugchecks. Now quits are
requested; the render thread glFinish()es, unbinds the context and parks
at a frame boundary, and the host exits after (max 2 s). GE_QUITFRAME=<n>
gives the harness a clean self-quit; golden_gate.sh uses it.
One Rule-2 #ifdef PORT draw hook in constructor_menu05_fileselect (user
sign-off 2026-09-27). The label's hit test and open request live in
optionsoverlay.c; the open is applied on the input thread. While the
overlay is open on file select, g_MenuTimer is held at 0 so the screen no
longer drops to the legal screen after 30 s.
The crop tested loaded_texture.line_size_bytes, which is the whole
LoadBlock (1400 B), so the water still uploaded 32x43 while UVs were
normalised to 32 rows; the mip rows showed as coloured dashes. Test the
tile line size instead. User-verified on Frigate; golden 3/3.
The PC wallet model (0x1664C B, 0x17000 reservation) loads at the start of
ptr_logo_and_walletbond_DL, but the file-select scratch DL and the
briefing text still used the N64 offset +0xA000 -- inside the model.
After main menu -> back the model is not reloaded, so the overwritten
folder bodies/photos disappeared. Both now use +0x17000 (same 0x6e000
budget). User-verified; golden gate 3/3. D341 also user-verified.
Two fast3d defects. (1) The 1-cycle blood texture rectangle sampled a
stale tile-1 CI8 declaration: the title leaves G_TL_LOD set and the D236
detail-base rule redirected texunit 0 to tile 1. LOD tile selection is
2-cycle-only on the RDP, so texrects outside 2-cycle now sample their own
tile. (2) The drip image is regenerated in place in the dyn pool (reached
via the KSEG0 mirror); the address-only cache key froze it on its first
image. Dyn-pool textures now key on a content hash. Headless-verified,
golden 3/3. A/B: GE_DYNTEXHASH_OFF.
The IsWater 32x32 CI8 image (drawn through an RGBA16 tile, D229) was
imported with its mip chain as 32x43 and normalised as 16 wide, so the
pattern ran at 2x and wrapped every 43 rows instead of the N64's 32:
fold steps made it jump. Import the base level only and use CI8
geometry in the tri path. Sky/water fans now go through a port-only
G_FLOATVTX_EXT (float clip positions + S/T) instead of s16 Vtx, removing
the tc shift and wScale quantisation jitter. User-verified vs 1964;
golden 3/3; Win + Linux build. A/B: GE_D245_OLDVTX / GE_D245_OLDTEX.
The op-22 DisplayListPrimaryRecord count is the number of star-flash
arms; dorottex (and PD's modelRenderNodeStarGunfire) read count*4
vertices. d43_emit/d43_convert sized the array as count vertices, so
3/4 of every muzzle star came from neighbouring sidecar bytes and drew
as long spikes (M16 "shooting up"). Size it 16*4*count. Emitter
round-trip checks all pass; M16 renders a clean star headless.
Also TEMP GE_STARTWEAPON (bondview_r.c) for weapon repros. Re-run
tools_pc/d43_emit.py to regenerate data/pcmodels-*.
CCTVRecord's own s32 `pad` (the look-at pad) shadows the inherited
ObjectRecord `pad` (mount pad) under IDO. The PC struct renamed it
`lookpad` to satisfy GCC, which silently re-pointed setupCctv's reads at
the mount pad, so every camera's look vector was its lens offset and the
lens faced the wall. Read `lookpad` on PC (#ifdef PORT, A1 class);
matches PD's setupCreateCctv (lookatpadnum). User-verified vs 1964,
golden gate 3/3. Porting-notes gains the duplicate-member rename trap.
TEMP GE_D207S (gunfire.c, #ifdef PORT, read-only) logs every player
shot-sound decision (H1/H2/SKIP). Four user captures incl. a 7-weapon
stress test at 60 fps: 0 SKIP, 0 voice drops, audio production at rate.
The one captured drop-out ran under GE_AUDIOTRACE, whose audio-path file
I/O is a likely cause. Still open as intermittent.
optionsoverlay.c called atoi and lround through implicit int declarations
(GE's stub stdlib.h/math.h shadow the CRT, D324 class); atoi joins the
other K&R decls in port/shim/stdlib.h, lround gets a local prototype.
Windows + Linux builds clean of both warnings.
GE_INPUTSCRIPT gains ZHOLD/ZREL (sustained fire) for automatic-weapon
audio repros. D207/D241: headless burst-fire at 60 vs 30 fps with the
alarm forced logged zero voice drops at both caps; owed a real-play
capture. Golden gate 3/3.
D291 (user-captured) is D306: Dam intro camera 5, truck over the walkway
camera; reproduces at FovScale 100. All PC-specific leads refuted with
evidence: FOV, depth precision/DrawDistance, depth state (every truck
triangle is opaque z-tested), near-plane handling, wheel transforms. The
image is a depth-correct view of the authored model from under the truck.
GE_ZF's earlier "real fighting" was a +-255 z quantisation artefact.
TEMP tools: GE_INTROCAM=<n> pins the level intro's first camera
(bondview_r.c, #ifdef PORT, PRNG draw still consumed); GE_D306C depth-state
census in gfx_pc.cpp. Golden gate 3/3.
gimgSyncCompiledGlobalDLs (D68 shim) only copied texLoad's resolved
texture pointers into the compiled globalDL shadows while a slot still
held the 0xABCD IMAGESEG marker, i.e. only for the first stage (title).
Every later stage kept pointers into the title's freed texture pool, so
explosion smoke/fire/debris rendered as rainbow garbage once the stage
overwrote that memory (Facility: past the lockers). Learn the slots once,
re-sync them every stage. User-verified; golden gate 3/3.
Also: TEMP GE_D252POOL texpool/sync probe; GE_D157 F12 burst and caps
(uncapped colour match made live play 4-6 fps). D314 F10 flicker
user-confirmed fixed. Findings M-201, porting-notes C2.
- D252 (M-200): the 'rainbow confetti' is the explosion debris
(explosionInitFlyingParticles). Statically clean for colour; 4 headless
repros including the user's grenade-launcher trigger are all clean. Still OPEN.
- d320repro (TEMP): new explode= and cheat= actions for headless explosion
captures; documented in GE-ENV-PROBES.
- D314: optionsOverlayScroll (host thread) rebuilt s_visIdx/s_visN and wrote
s_sel while the scheduler thread draws the overlay. Wheel notches are now
queued atomically and applied in HandleInput (D287 pattern). Emit and
HandleInput share the scheduler thread. Headless file/mission select is
pixel-stable; likely fixed together with D287, needs a user check.
- D291: truck AI mapped (ai_9 / 0x040a, path 7); headless walk did not reach it.
input.c called SDL_SetRelativeMouseMode / SDL_ShowCursor from the scheduler
thread (every controller poll) and from the host thread's focus/click
handling at the same time. SDL's mouse API is not thread-safe; on Windows
relative mode starts/stops the raw-input thread, and clicking back into the
window after being unfocused is exactly when both threads flip it - the D287
freeze trigger. Callers now record the wanted state; inputApplyMouseRequests()
applies it on the host thread from videoPumpEvents(). Queue-deadlock leads
ruled out statically. Win+Linux build clean, golden 3/3; freeze re-test owed.
- front.c: constructor_menu07_missionsel / constructor_menu0D_missioncomplete
fall off the end while the caller uses the Gfx* result; return DL under
AVOID_UB (porting-notes D6). Old/new objects disassemble identically at -O2,
so no behaviour change on this compiler; clang/arm64 no longer rely on luck.
- libultra/audio/event.c: include <stdlib.h> under PORT so getenv() is not an
implicit int-returning declaration (D324 class).
- dram.c: DRAM_SIZE >> 32 on a 32-bit unsigned long (Win64) was UB.
Golden gate 3/3.
Four simultaneous drain monitors on the same USB interface: WASAPI (512 and
2048 samples) fell to 81-88% drain for ~30-50 s every ~317 s and lost 1.4% of
playback, while DirectSound stayed at exact real time. Each WASAPI shortfall
overflowed the audio queue and dropped blocks (the periodic garbled bursts).
audioInit() now hints directsound,wasapi; SDL_AUDIODRIVER still overrides.
Golden gate 3/3; 60 s GE_D204 run clean after load.
A no-game SDL drain monitor (new tools_pc/sdl_drain_monitor.c, opens SDL
exactly like port/src/audio.c) shows the maintainer's Focusrite USB interface
draining at 88-95% for ~15 s every ~312 s, matching the in-game overflow
bursts' period. Level loads, combat, the SDL pull pattern and a
pacemaker-vs-vsync beat were ruled out. Remaining D322 items: the 8-voice SFX
cap (D207/D241) and #87's progressive decay. porting-notes: measure the host
device before chasing periodic audio glitches.
D311 FIXED (root cause D336; 0 anomaly lines incl. Caverns combat), D288
CLOSED, D294 and D330 user-confirmed, D235 still unreproduced. D322: pools
healthy (leak hypothesis refuted); SFX soft-cap saturation in firefights
(D207/D241) plus a new periodic SDL output-queue overflow every ~314 s that
starts at the title screen, so it should reproduce headless.
Pins data/ge007.ini to a defaults-only 640x480 window (restored on any exit),
captures Bunker 1 at 640-1120:240 and runs framediff.py against the golden
baseline. --level/--dump give a capture-only smoke run (exit 0 = frames
captured and no crash). Full framediff output goes to scratch/.
New findings not covered by TCRF's articles (full detail in local
backlog/cut-content/RESEARCH.md):
- The shipped game's M16 pickup text still reads 'an AR33 assault rifle'
(LpropobjE PROPOBJ_STR_2A, propobj.c:10471); unused 'US AR33' strings and
PROP_CHRM16 comments survive too.
- Klobb rename scar verified: GUN_STR_6B is the only out-of-alphabetical-order
weapon name in LgunE.c, exactly as TCRF's prerelease page describes.
- cartrifle + wristdart: full pickup/prop resources, no ITEM_ enum, no weapon
stats — cut weapons missing from TCRF's unused-items list (wrist dart = the
AMMO_DARTS hardware; decomp comments it '(BETA)').
- All four Bonds mapped: CUFF_/BOND_ Connery/Dalton/Moore enums + the
actor-switch code in bondview2.c with every branch flattened to Brosnan;
no former-actor models exist in the ROM (80 C*Z chr models enumerated).
- Cuba (LlenE.c) is the only orphan level with recoverable text: cut Natalya
dialogue, title card, alternate credits cast.
- Orphan-level geometry confirmed present in ROM (obseg.h segment decls);
what's missing is mission setup, not geometry.
- Music enum vs Notes-page GS index aligned slot-for-slot through 0x2C with
four local mismatches past it (49/52/54/59) pending in-game audition.
Ammo (per-hand corner), bottom messages/title card (bottom-left) and top
dialogue (text origin) scale with crisp glyphs; dialogue and bottom
messages are width-capped because their line breaks are authored. 100%
emits nothing (golden 3/3). Captures verified at 150/200%; Win + Linux.
Env-gated per-frame timing: render phases, tris/batches, game-thread busy,
GL_TIME_ELAPSED GPU timer. Measured ~2.1 ms/frame total CPU on the heaviest
levels here; the GPU timer is only meaningful when GPU-bound. Target-hardware
numbers needed before a low-end preset. Golden 3/3; Win + Linux builds.
Scales the mouse accumulator's screen mapping and edge-scroll threshold; aim
entry adopts through the game's screen mapping so it never jumps. Measured:
PC 98%/72%, N64 65%/49%. Golden 3/3; Win + Linux builds.