Commit Graph
171 Commits
Author SHA1 Message Date
James Dansereau 1ef57b936d Merge pull request #13 from jkdansereau/docs/building-d88-step
docs(building): add the missing d88_emit.py --regen sidecar step
2026-09-03 10:00:25 -04:00
James Dansereau 7863ec096f port: D186 — refuse a sub-30 Video.FpsCap instead of throttling the sim
sync_framerate_with_timer() runs inline on the scheduler thread
(osSpTaskStartGo -> gfx_run -> swap_buffers_begin), so a low frame cap
blocks VI-retrace delivery and drags every game thread down to the cap
rate rather than just dropping presented frames. There is no N64 analogue
(RSP/RDP/VI are separate silicon; the CPU never waits on them).

Clamp 0 < FpsCap < 30 to 0 (uncapped) with a warning, at both:
  - videoInit, so a bad ge007.ini value (e.g. dinged to 10 via the F10
    options overlay) is normalised and persists sane on next configSave();
  - gfx_sdl_set_target_fps, the fast3d chokepoint, which also covers the
    overlay's live set path.

Caps >= 30 are unchanged. Default (FpsCap = 0) is byte-identical to
golden (-level_09 framediff 3/3). The real fix — moving frame pacing off
the scheduler thread — is documented as owed in findings.md D186.
2026-09-03 00:51:40 -04:00
James Dansereau 547c564c98 docs(building): add the missing d88_emit.py --regen sidecar step
building.md listed only d43 + d69; d88 (per-level Usetup*Z stage setup,
appended to the pccg dir) was omitted, so a source build per these
instructions produced a game that crashed on first level load.
2026-09-03 00:45:33 -04:00
James Dansereau 05b89374e8 Merge branch 'main' into feat/linux-ci-compile 2026-09-03 00:13:40 -04:00
James Dansereau 7c3bb42687 linux-build: -std=gnu extensions + fix remaining snprintf decls
- CMakeLists: CMAKE_C_EXTENSIONS/CXX_EXTENSIONS ON. Strict -std=c11/c++20
  define __STRICT_ANSI__, which disables glibc _DEFAULT_SOURCE and hides
  clock_gettime(CLOCK_REALTIME) etc. that port/src/libultra.c needs.
  -std=gnu11/gnu++20 is GCC's default and what MinGW already used.
- port/src/{pccg,pcmodels,romdata}.c: local 'extern int snprintf(...,
  unsigned long long maxsize, ...)' decls conflict with glibc's size_t one
  (same bug already fixed in pc_protos.h in M-39); spell it size_t.
- libultra.c: drop the _GNU_SOURCE define (pulls cpu_set_t refs into
  <pthread.h> without <sched.h>); the D60 POSIX stack-bounds diagnostic
  now just prints n/a rather than needing pthread_getattr_np.

Windows: full rebuild 240/241, -level_09 crash-free past frame 1740.
2026-09-02 22:12:58 -04:00
James Dansereau b01c7fa89c port(romdata): POSIX fixed-address ROM map (mmap) for the Linux build
The cart-base map at 0x10000000 (so absolute ROM-asset symbols are live host
pointers) was Windows-only; the #else branch was a stub that logged
"not implemented" and fell to the degraded heap-copy path.

- Factor the post-map work (memcpy + pcmodels/pccg sidecar load + D55 RLE
  header fixup) into romdataFinishCartMap(), shared by both paths.
- POSIX #else: anonymous mmap at CART_BASE with MAP_FIXED_NOREPLACE where
  available; on any failure munmap + fall through to the identical heap copy.
- romdataDestroy() munmaps on POSIX (length tracked in mappedLen).

Verified on Windows: builds 242/242, -level_09 boots crash-free (identical
behaviour with/without the refactor). Linux compile + runtime verification
owed — see docs/dev/LINUX-PORT.md. No behaviour change on Windows.
2026-09-02 21:15:01 -04:00
James Dansereau 0e1d430213 docs: drop-in-ROM release plan, mermaid rendering, Qwen dispatch guide
- docs/dev/release-dropin-rom-plan.md: proposal for a 'download zip, drop
  in your ROM, double-click' release — covers the romassets/pcmodels/pccg
  sidecar gap (D179), a native first-run converter, CI and region handling.
- docs/_includes/head-custom.html: render fenced mermaid blocks on the
  docs site (kramdown code fence -> mermaid.run, theme-aware).
- CLAUDE.md: 'Local Qwen dispatch' section — tools, worker list, the
  dispatch rubric, backend reality, and the cmd.exe spot-check quirk.
2026-09-02 20:17:10 -04:00
James Dansereau fe9106baef docs: correct download status — no pre-built binaries yet
Nothing runs end to end (Phase 2 WIP), so the 'pre-built Windows bundles
are published under Releases' language in the README and site was
inaccurate. Reword to: no binaries yet, build from source; bundles may
follow once the port is playable. Also rename the local model Qwen 3 ->
Qwen 3.8 in the docs to match reality.
2026-09-02 20:17:10 -04:00
James Dansereau 702dee61fc docs(D176): root-cause D176(a) black sky (RDPHALF no-op); D176(b) M-37 update 2026-09-02 19:07:55 -04:00
James Dansereau 293e7436d3 port: D177 — ladders non-functional (progression blocker), pointer-width ABI
MoveBond gates the entire ladder-collision path on
stanGetLocusCount(&curLocus), which was pinned at 0 on PC. Three compounding
pointer-width bugs in the stan navigation layer (same class as D79/D90),
no game logic touched:

1. stanCheckLinkedSpecialTile receives the caller's
   struct StandTileLocusCallbackRecord typed as `s32 *outFlags` and writes
   the LADDER signal with raw `outFlags[1] = 1`. The record's first member
   is `s32 *rooms` — 8 bytes on PC — so `[1]` is its high half and `count`
   (moved +4 -> +8) is never written. stanGetLocusCount() reads ->count,
   stays 0, ladder path never runs. (FORCECROUCH survived by luck: LE low
   half of rooms is at +0.) Fix: #ifdef PORT cast to the real struct, write
   ->rooms / ->count by name.

2. curLocus was declared `struct move_bond_temp_struct` — an 8-byte
   "placeholder while matching" that equals the N64 record size but is too
   small for the widened PC record, so stan's field writes overflowed the
   local. Fix: #ifdef PORT declare it as the real
   struct StandTileLocusCallbackRecord.

3. stanGetMoveBondCollisionTiles would AV on the first climb:
   `(coord3d *)((s32)coords + off)` truncates the 64-bit &bondCollision
   stack pointer. Fix: PORT_PTRADD (uintptr_t arithmetic) added to stan.c.

All #ifdef PORT; N64 path kept verbatim under #else. Candidate root-cause
from an M-36 subagent (parts 1/3); part 2 found on review.

Verified: build links clean; -level_09 framediff 3/3 (frame_200 run
variance only); -level_36 (Surface, mandatory ladders) boots crash-free;
interactive climb confirmed working in-game by the owner.

findings.md D177 (§F + §H), porting-notes.md §A.
2026-09-02 18:26:38 -04:00
James Dansereau a01e732c55 Merge remote-tracking branch 'origin/main' into qol/native-pc-input-menu 2026-09-02 18:05:22 -04:00
James Dansereau 809e7378e1 port: D176(a) Surface black-sky investigation + GE_D176 probe (M-36, partial)
Not root-caused; narrowed substantially. Headless -level_36 + GE_D176 probe
(bgfog.c fogLoadLevelEnvironment, sky.c skyRender):

- Environment data is CORRECT: fog_tables[] matches Id=36, Sky.Clouds=1,
  warm CloudRGB=240,120,30, SkyImageId=0. Not a serialized-struct
  byte-order bug (fog_tables is a compiled C initializer).
- skyRender runs the cloud path; corner classification is correct
  (switch case 12, "sky is top half").
- But skyRenderTri/skyRenderFull emit nothing - not even the env->RGB
  fill shows. Sky polys degenerate after projection, culled, or the
  skywaterimages[0] bind is failing.

Next (findings.md "D176(a)"): probe sub_GAME_7F097388 projected coords;
verify skywaterimages resolves on PC (image_bank.c GIMG offset 0xFB4);
fast3d sky-tri trace; cross-check Dam/Statue exteriors.

GE_D176=1 probe left in tree (#ifdef PORT, env-gated, inert). Build
240/240 links clean; -level_09 framediff 3/3.
2026-09-02 18:04:36 -04:00
James Dansereau 19715e0997 docs(pages): SEO setup + Cayman design pass
_config.yml:
- canonical url/baseurl for jekyll-seo-tag + jekyll-sitemap
- jekyll-seo-tag + jekyll-sitemap plugins; og:image link-preview card
- keep the raw engineering record (dev/ finding log, per-level status,
  triage notes) out of sitemap.xml; keep the two linked dev/ docs in

_includes/head-custom.html:
- Google/Bing Search Console verification tags
- full design pass over the stock Cayman theme: IBM Plex Sans/Mono,
  restrained amber-on-slate palette, light + dark themes, faded
  attract-mode screenshot header treatment, tightened typographic
  hierarchy, dark code blocks, quieter tables
2026-09-02 17:47:18 -04:00
James Dansereau 4069677b82 docs: M-36 triage — record Family A/B outcomes (B fixed, A disproven)
Claude-Session: https://claude.ai/code/session_01GULf6knbsqfsnW39tMr9WT
2026-09-02 11:01:06 -04:00
James Dansereau 4b71435d73 port: D183 — fast3d source-pitch de-stride + texture diagnostics (M-36 Family A)
Family A of the M-36 triage (Surface "grey static" D176b, file-select
interlacing D182-2). The assigned hypothesis — import_texture_* shear when
line_size_bytes != full_image_line_size_bytes — is DISPROVEN for the Surface
repro:

- Only gfx_dp_load_tile produces a strided load; GE loads every texture via
  gDPLoadBlock (tex.c texWriteLoadToTmem*), which sets line == full. Measured:
  0/64 GE_DTEX + 0/166 GE_TEXDUMP imports strided on -level_36; 0 in the
  front end either.
- Independent offline pitch scan (new GE_TEXRAW dump) shows the importers
  pick exactly the pitch the source is stored at. The Surface cliff texture
  is a 32x32 IA8 whose ROM bytes are genuinely a noise field — decoded
  faithfully, tiled ~1 texel/px. D176(b) is likely a tiling-density / UV /
  shift-LOD issue (RC3/D167 family) or a wrong-texture bind, not a decode
  bug. Full evidence: findings.md D183. D176(b)/D182(2) stay OPEN.
- Re-confirmed D159's texSwapAltRowBytes no-op is correct (re-applying it
  makes the dumps worse).

Shipped anyway (golden-safe, reversible):
- import_texture() compacts strided rows into a contiguous buffer before the
  format dispatch, so every importer sees line == full — the behaviour the
  seven SUPPORT_CHECK(full == line) asserts only assert (and assert compiles
  out under NDEBUG). Provably inert when full == line (all current loads).
  GE_TEXPITCH=0 restores the old flat read.
- GE_DTEX gains line=/full=/STRIDED markers; GE_TEXRAW=1 dumps raw
  importer-input bytes for offline decode-vs-source analysis.

Verified: build 240/240; -level_09 framediff 3/3; GE_STARTMENU=10 briefing
crash-free, renders. Investigation: subagent (worktree).

Claude-Session: https://claude.ai/code/session_01GULf6knbsqfsnW39tMr9WT
2026-09-02 11:00:11 -04:00
James Dansereau cd1ed57449 port: D178 — byte-swap the raw briefing segment (blank objectives / briefing text)
Family B of the M-36 triage. The Ubrief*Z segment (assets/obseg/brief/) is
a raw 48-byte ROM image of struct BriefStruct { u16 brief[4]; struct { u16
textid; u16 enabled_difficulty; } objective[10]; }, loaded by front.c
load_briefing_text_for_stage() via _fileNameLoadToAddr() with no converter
and no BE->LE fixup. On the LE host all 24 u16 read byte-swapped:

- textid 0x2C04 (getStringID(LDAM,4)) -> 0x042C -> langGet() resolves an
  unloaded bank -> NULL -> blank objective/briefing text. This is the whole
  of the D143 "briefing text blank" residual (the bank loaded fine; the ids
  into it were swapped).
- enabled_difficulty 0x0001 -> 0x0100 = 256, so `selected_difficulty >=
  enabled_difficulty` is always false -> difficulty-gated objectives vanish
  (on Agent only the one AGENT-difficulty line survived -> lone "a." bullet).

Fix: romdataFixupBriefing() in port/src/romdata.c, called from a #ifdef PORT
block in load_briefing_text_for_stage() right after the load — same shape as
langFixupLoadedBank(). BE-serialized-struct reconciliation (AGENTS.md #2),
semantics-preserving, N64 build untouched. GE_D178=1 dumps raw + fixed words.

Verified: Dam briefing renders all four objectives at 00 Agent with correct
difficulty gating and "007: James Bond" header; -level_09 framediff 3/3;
GE_STARTMENU=7/=13 crash-free; build links clean 240/240.

Investigation + patch: subagent (worktree). Suspect 2 (Gfx-width temp_s0
arithmetic) was ruled out — benign 512-byte gap, langLoadToBank records the
bank base itself. Follow-ups noted in findings.md: annotate D143 as
superseded; latent 64->32 pointer truncation at front.c:6569 / :2202.

Claude-Session: https://claude.ai/code/session_01GULf6knbsqfsnW39tMr9WT
2026-09-02 10:56:03 -04:00
James Dansereau 9a69d57afd docs: M-36 triage sweep — bug families, correct the D159 mis-attribution
Headless GE_PCDUMP (-level_36, GE_STARTMENU=10) + static read of the
implicated port code. Two headline findings:

- The "comb interlacing" (D176b, D182-2) is NOT the D159 family.
  texSwapAltRowBytes is a full #ifdef PORT no-op (image.c:2199) and
  cannot cause anything. Real cause: gfx_pc.cpp import_texture_* assume
  line_size_bytes == full_image_line_size_bytes and shear non-PoT /
  sub-rect tiles into diagonal noise. New "Family A". Backlog D176/D182
  corrected.

- D178 briefing objectives are BLANK, not wrong-listed: header renders,
  then just "a." with no objective text. That is the D143/D151
  langGet-NULL blank-text family ("Family B"), not a propDef walk desync.

docs/dev/M-36-TRIAGE.md: full family table (A fast3d texture pitch,
B string bank, C D75 models [defer], D blood/particle, E transient
hang [defer], F misc). Recommends Families A and B as the two
high-ROI targets, disjoint enough for two subagents.

Claude-Session: https://claude.ai/code/session_01GULf6knbsqfsnW39tMr9WT
2026-09-02 10:06:56 -04:00
James Dansereau ef10985343 docs: sidecar build step + D176/D182 Surface/file-select bug reports (M-35)
- building.md: document the d43_emit.py / d69_emit.py sidecar generation
  as an explicit required step (4), split Run to step 5. A plain clone
  needs no ROM to build but the sidecars are required to run (D179).

- GRAPHICS-BACKLOG.md: expand D176 (Surface exterior: black sky +
  comb-interlaced tree-line, screenshots) and add D182 (SELECT FILE
  background degrades after navigate-away-and-back: Bond image stops
  drawing + comb interlacing). Both observed this session, not fixed.
  D176(b) and D182(2) interlacing share the D159 texSwapAltRowBytes
  family recurring on texture re-import.

- docs/img/bugs/: d176 our-build vs reference, d182 backout capture.

Claude-Session: https://claude.ai/code/session_01GULf6knbsqfsnW39tMr9WT
2026-09-02 09:54:25 -04:00
James Dansereau 10b278311f docs: M-35 QoL run — review sheet, playtest checklist, options-menu plan
- M-35-QOL-REVIEW.md: per-change file:area breakdown, config keys with
  ranges/defaults, verification evidence.
- M-35-PLAYTEST-CHECKLIST.md: the human handoff — what to do, what
  correct feels like, which key tunes it. Item 0 (default-config
  regression) first.
- OPTIONS-MENU-PLAN.md: WI-3 design — recommends a port-layer fast3d 2D
  overlay over a src/ menu edit; resume checklist.

Claude-Session: https://claude.ai/code/session_01GULf6knbsqfsnW39tMr9WT
2026-09-02 01:30:01 -04:00
James Dansereau 849ee512cd port: Game.ScreenShakeIntensity route-(b) hook in viShake (D181)
First behavioral src/ hook under the AGENTS.md #2 documented-exception
path. src/fr.c viShake() gets one #ifdef PORT line multiplying the
incoming amplitude by portScreenShakeScale (extern f32, registered
0.0-10.0 in port/src/video.c, committed with WI-1). Default 1.0f is an
exact no-op so headless golden dumps are byte-unaffected; 0.0 disables
explosion/effect screen shake. N64 build keeps the original line under
#else. findings D180/D181 + F-index.

Claude-Session: https://claude.ai/code/session_01GULf6knbsqfsnW39tMr9WT
2026-09-02 01:26:41 -04:00
James Dansereau d55d5e065f findings: log D173–D178 from QA playtest reports
New observed-but-not-investigated entries from user QA:
- D173 third-person Bond model spawns too high (level start + Cuba credits)
- D174 no visible blood effect (likely unverified D120 decal fix)
- D175 in-game stutter/brief hang on door open (Runway, Surface)
- D176 skybox broken on Surface
- D177 ladders non-functional — progression blocker, high priority
- D178 pre-mission briefing objectives listed incorrectly

Cross-referenced to the nearest known finding families; next-step
pattern-match notes in GRAPHICS-BACKLOG.md.

Claude-Session: https://claude.ai/code/session_01GULf6knbsqfsnW39tMr9WT
2026-09-02 00:27:21 -04:00
James Dansereau 4e2b721ac8 docs: README hook, discoverability, Pages site, game-behaviour reference
- README: lead with what it is + the AI-agent-collaboration research
  framing + current status in the first paragraph; rename H1 to
  "GoldenEye 007 PC Port"; add a live CI badge.
- New "How this differs from the other GoldenEye PC projects" section:
  this is a source port of the N64 original; the Recomp projects
  binary-translate the Xbox 360 XBLA remaster (different game, no shared
  code).
- CITATION.cff for GitHub's "Cite this repository" widget / Zenodo.
- GitHub Pages site off /docs (_config.yml + index.md landing page).
- agentic-development.md: searchable title + abstract.
- .github/social-preview.{png,html,md}: repo social-preview card styled
  after the GE mission-briefing screen, kept editable (CSS source + a
  regen/how-to doc beside the PNG).
- docs/dev/game-behavior-reference.md: distilled the raw GameFAQs
  walkthrough dump down to the behaviour-defining parts (combat/AI model,
  difficulty scaling, per-level objective sets, timers, weapon data,
  original-game quirks). Credits: Brett "Nemesis" Franklin, marshmallow.

Claude-Session: https://claude.ai/code/session_01GULf6knbsqfsnW39tMr9WT
2026-09-02 00:27:05 -04:00
James Dansereau 4ca11e4386 Prepare repository for public release
- README: attract-mode stills + demo GIF as the hero row, status/warning
  callouts, metrics table, "Contents" index, doc-index table; simplified
  the two-agent workflow diagram (LR -> TD, fewer edges) for GitHub web
- Table-of-contents indexes added to internals.md, porting-notes.md,
  agentic-development.md, AUDIO-PLAN.md
- CI: add Windows/MSYS2 build job, minimal token (contents: read),
  persist-credentials: false, concurrency group; run on main + master.
  validate job regenerates the US ROM-asset symbol file only and configures
  ntsc-final (PAL/JP need a region ROM; no ROM is used in CI)
- .github: SECURITY.md, issue forms + chooser config, PR template,
  labels.yml, dependabot (Actions, monthly)
- Do not track the proprietary SGI IRIX IDO toolchain or SGIImageViewer.exe;
  document in SetupGuide.md / building.md how to obtain IDO for N64 ROM
  builds (the PC port does not need it)
- Stop tracking two committed *.pyc files; drop stale screenshots and a
  redundant .gitignore line; misc doc cross-reference fixes

Claude-Session: https://claude.ai/code/session_01GULf6knbsqfsnW39tMr9WT
2026-09-01 22:42:52 -04:00
James Dansereau e69d7b58a9 docs/img: attract-mode screenshots (bunker1, silo, dam)
Pre-spawn intro-camera views captured with GE_PCDUMP after the D168
flip fix (so they're upright). Not yet wired into the README - awaiting
placement direction (keep archives-1 / bunker1-1 as the top pair).

- attract-bunker1.png: control room + "Satellite Control Bunker" caption
- attract-silo.png: guards + toxic-waste drums, silo hangar behind
- attract-dam.png: guards on patrol, control panel, snowy peaks

Claude-Session: https://claude.ai/code/session_01UpTM1AcBRJuTetoZNcCD5V
2026-09-01 20:22:44 -04:00
James Dansereau 142e9c8f26 D168 verified; goldens regenerated; D75 shrunk; D169 marked fixed
Built the migrated tree (MSYS2 mingw64 + ROM + sidecar regen) and ran
-level_09 with GE_PCDUMP: HUD text and the exit arrow now read
right-side-up. D114/D116 confirmed as capture artifacts.

- tools_pc/golden/*.png regenerated from a fresh correctly-oriented run
  (replaces the stopgap in-place flip); stray v*.png removed
- D75 shrunk: with upright captures the gun-barrel Bond RENDERS fine
  (walk + fire) -- the "absent" reports were the flipped capture. The
  Nintendo logo is genuinely broken (renders as 2 white blobs shifted
  left -- a real logoinst transform bug, not a flip). Cast roll not
  re-captured.
- D169 entry -> FIXED with the build/verify note

Claude-Session: https://claude.ai/code/session_01UpTM1AcBRJuTetoZNcCD5V
2026-09-01 20:22:32 -04:00
James Dansereau fa54f8fb90 docs: add operating manual, fix cross-references after doc restructure
- Commit CLAUDE.md + AGENTS.md so a fresh clone has the operating manual and
  the ABI/no-game-logic rules (trimmed of stale phase status; the personal
  pd_port path removed; points at the split docs).
- Sweep every stale doc reference across the tree after the restructure:
  PCPortResearch.md -> internals.md / dev/findings.md,
  PORT-LEARNINGS.md -> porting-notes.md,
  AGENT-WORKFLOW.md -> dev-process.md,
  docs/{GRAPHICS-BACKLOG,HANDOFF-ARCHIVE,GE-ENV-PROBES,LEVEL-*,TEXTURE-GLITCH-ANALYSIS,AUDIT-M6-*,AUDIO-PLAN}.md -> docs/dev/.
- Drop the superseded docs/PLAN-linear-level-sweep.md.
- docs/HANDOFF.md + docs/BACKLOG.md stay local-only (rolling working files);
  CLAUDE.md now handles their absence in a fresh clone.
2026-09-01 20:01:09 -04:00
James Dansereau 0ac5dfb2fa D169: level-select mouse dead-zone root-cause writeup
Front-end mouse pointer can't reach the outer mission-select grid cells
(only an inner ~3x3). The D165 P-controller in port/src/input.c hard-codes
a 320x240 virtual field; GE's front end runs 440x330, so the pointer
target/estimate saturate at 300/220 before the grid's outer split points
(317 / 235.5). File-select/mode/main-menu unaffected (targets inside the
320x240 sub-box). Fix proposed (derive clamp bounds from
getPlayer_c_screenwidth/height, seed estimator from the real cursor) but
NOT applied - no build in the migrated tree to verify link + regression.

Claude-Session: https://claude.ai/code/session_01UpTM1AcBRJuTetoZNcCD5V
2026-09-01 19:45:17 -04:00
James Dansereau 203549f57e D168: fix vertically-flipped GE_PCDUMP captures; close D114/D116
gfx_opengl_dump_bound_fbo wrote glReadPixels output (GL bottom-left
origin) straight into a top-row-first P6 PPM, so every GE_PCDUMP / F12
capture was upside-down. This is the entire source of the D114/D116
"HUD/text X-mirror" - four sessions chasing a fast3d mirror that never
existed, misreading inverted screenshots of asymmetric content. The
developer confirms the game renders correctly on real hardware.

- writer now emits rows top-to-bottom
- tools_pc/golden/*.png flipped in place to match + golden/README.md
- D114/D116 reclassified as capture-orientation artifacts (not deleted);
  D75 "logo mispositioned" / RC1 "photo 180 deg" flagged for re-judging
- annotations in findings.md, GRAPHICS-BACKLOG.md,
  TEXTURE-GLITCH-ANALYSIS.md, porting-notes.md D2, GE-ENV-PROBES.md

Not runtime-verified (migrated tree has no ROM/toolchain) - needs a
fresh capture to confirm text reads normally.

Claude-Session: https://claude.ai/code/session_01UpTM1AcBRJuTetoZNcCD5V
2026-09-01 19:44:39 -04:00
James Dansereau 81f02c7d3a docs: honesty pass on status + platform claims
- README: "not playable yet" callout under the screenshots (no audio,
  broken front-end models/text, expect crashes past the level intro).
- README/building.md/ci.yml: platform testing is Windows-only. The Linux
  build is provided but has had zero functional testing at runtime; the
  CI job is a CMake-configure smoke test, not a Linux-support claim.

Claude-Session: https://claude.ai/code/session_01UpTM1AcBRJuTetoZNcCD5V
2026-09-01 19:43:40 -04:00
James Dansereau 52539d10de Prepare for public release
- README, LICENSE (MIT for the port layer), NOTICE, CONTRIBUTING.
- Restructure docs: internals.md, porting-notes.md, dev-process.md +
  dev/agentic-development.md (the research write-up), dev/ (raw record).
- Replace decomp status-badge CI with a PC-port validate workflow.
- Remove session/agent scaffolding and stale planning docs from the tree.
- Add .gitattributes (normalize line endings to LF); add screenshots.

The pre-2026-08 history is the GoldenEye 007 decompilation by Larry Ficken
(KholdFuzion) and contributors, preserved unchanged. The PC-port work from
2026-08-17 on is mine.
2026-09-01 19:28:59 -04:00
James Dansereau 294e24cc26 M-32b: HANDOFF reconciliation (strip, D75 Bug 2 probe, RC3 inconclusive) 2026-08-31 15:53:51 -04:00
James Dansereau 652663aac6 D75 Bug 2: runtime probe + triage update
Adds env-gated GE_D75 probe in title.c sub_GAME_7F007F30 (capped 8 lines).
Booted the bare front end through the gun-barrel sequence:

- Bond chr model + gun model entirely absent; both Model instances are
  valid (non-NULL obj, numMatrices 21/1) -> 'model not loaded' ruled out.
- chrModelInstance->render_pos is valid and freshly written each frame
  (== that frame's dynAllocate mtxlist base, clean double-buffer swap,
  o2p returns it unchanged) -> the 'render_pos = stale dynAllocate arena'
  hypothesis (D115 #5) is RULED OUT; do NOT land a persistent render_pos
  buffer.
- Zero fast3d DL warnings during the sequence -> not the D144/D146
  corrupt-sub-DL family.

Failure is downstream in drawjointlist/dotube vtx/node-DL resolution or an
off-screen basemtx. Next step: a drawjointlist-level probe. Full write-up
in PCPortResearch.md 'D75 Bug 2 - RUNTIME PROBE'.

-level_09 nonclear stable (91.66->91.62, D117 pan noise); build green.
2026-08-31 15:53:05 -04:00
James Dansereau ae065c2082 M-31/M-32: reconcile 6-agent burst #2 (D154 vtxoff + sibling, D152+ audio-lock leak, RC3/D167 wrap-period knob, D75/D160 disproofs, docs) 2026-08-31 12:57:50 -04:00
James Dansereau dcf2002d71 docs: reconcile TEXTURE-GLITCH-ANALYSIS status table; add GE-ENV-PROBES.md; GE_D63 strip assessment 2026-08-31 12:50:12 -04:00
James Dansereau 840208b58d D75 triage (M-32): rule out D73 gu-scope gap; split into fast3d-mirror + dynAllocate-arena leads
Static-only pass. (a) D73 scope gap DISPROVEN: src/libultra/gu/* and
matrixmath.c are all endian-clean; the Rareware logo exercises the full
gu pipeline front-end and renders correctly.

D75 now splits into two independent bugs:
- Bug 1 (Nintendo/GE logo transform, wallet/Bond-photo 180deg): the
  parked D114/D116 shared fast3d viewport/MP-matrix mirror. Not game
  code; static tracing banned per PORT-LEARNINGS D2.
- Bug 2 (gun-barrel Bond + cast models entirely absent): category (b),
  independent of any transform. In-level skeletal models render via the
  identical joint path, so the discriminator is the front-end setup:
  render_pos pointed at a transient dynAllocate gfx arena (D115 MED #5)
  is the lead suspect and an in-scope #ifdef PORT fix candidate.

docs only: PCPortResearch.md D75 ADDENDUM + index row; PORT-LEARNINGS
section C (D73/gu scope now settled - stop re-auditing).
2026-08-31 12:49:42 -04:00
James Dansereau 40f177a982 RC3/D167: non-power-of-two texture wrap period (Video.WrapFix, default off)
fast3d never stored the N64 tile mask and wrapped textures purely at the
uploaded image size; the RDP wraps a WRAP render tile at 1<<mask, which GE
sets to ceil(log2(dim)) - so non-power-of-two surfaces (Depot room
textures) repeat at the next power of two on console and one image-width
too soon in the port ('textures repeat oddly').

- gfx_dp_set_tile: store masks/maskt on the tile.
- gfx_sp_tri1 hoisted pre-wrap block (the reworked D74 block, indexed by
  texunit): when WRAP and 1<<mask != tex_width, fold UV at 1<<mask and
  clamp the [dim,1<<mask) overflow band (no real texels) to the edge.
- GE_WRAPFIX env override (wins over the ini) for testing.

Behind the existing Video.WrapFix knob, default OFF -> byte-identical to
golden. Per-level captures (-level_09/-30/-34/-20, off vs on): no crashes,
no structural regression; Depot shows small localized ceiling/wall texel
changes. Kept default off pending a human visual check on Depot.

docs: PCPortResearch.md RC3/D167, PORT-LEARNINGS.md D, TEXTURE-GLITCH-ANALYSIS.md 6d.
2026-08-31 12:49:39 -04:00
James Dansereau dc5739bc93 D167: D152 addendum -- audio osSetIntMask audit + narrow lock-leak fixes
Static audit of every osSetIntMask(OS_IM_NONE) in the compiled audio code
(event.c, csplayer.c, synaddplayer.c, snd.c): all balanced on every exit
path, including alEvtqPostEvent's freeList-empty early return. No unbalanced
same-thread leak exists -- the D152 s_imLock wedge is not a missing restore.

Two real defects fixed (#ifdef PORT, N64 verbatim under #else):

1. snd.c sndSetSfxSlotVolume walked the live ALSoundState list and posted to
   the shared ALEventQueue without holding OS_IM_NONE, unlike its twin
   sndDeactivateAllSfxByFlag. On PC: unguarded walk racing amMain + one lock
   acquire/release per matching sound. During the mission-failed fade-out
   (sndSetScalerApplyVolumeAllSfxSlot -> sndApplyVolumeAllSfxSlot -> here,
   per frame) that is a per-frame lock-acquire storm. Now holds the mask once
   across the walk; sndApplyVolumeAllSfxSlot batches its slot loop under one
   recursive hold.

2. port/src/libultra.c: portThreadWrapper now calls imThreadExitRelease() when
   a host thread returns from its entry -- if it still owns s_imHeld, release
   the orphan (+ LOG_ERROR). Kills the 'transient thread acquired OS_IM_NONE
   and exited' leak (the §F dump's own alternate hypothesis) and the re-wedge
   when a new thread reuses the dead pthread id and imAcquire mis-detects
   recursion. Steal-lock kept as backstop.

Build 240/240 green; -level_09 frame 300 renders 91.7% non-clear crash-free;
-level_20 boots crash-free. Fade-out repro is playtest-gated (not headless-
reproducible) -- user to confirm no black screen + no D152: log lines.
2026-08-31 12:49:06 -04:00
James Dansereau 75641fb0b7 M-30: D154 re-audit — fix vtxoff, port bgTestBulletHitBackground G_SETTILE sibling, add GE_D154 diagnostic
Re-audited the M-28 bgTestRayIntersectionInRoom GBI-parser port (bg.c):

- G_TRI1 (w1>>16/8/0 & 0xff /10) and all 12 G_TRI4 nibble recoveries
  cross-checked twice against the N64 #else path and the raw BE-word bit
  layout -- both CORRECT, no change.
- BUG: vtxoff read gdl->dma.par & 0xf. The PC Gdma_le shim maps .par to
  bits 0-23 of word0 (packed length, 16-aligned) not the N64 byte-1
  params field at bits 16-23, so vtxoff was silently 0 -> wrong
  vtxbase[idx] for any G_VTX batch with v0 != 0. Now
  ((u32)gdl->words.w0 >> 16) & 0xf.
- Sibling ported: bgTestBulletHitBackground tail (post-hit G_SETTILE
  back-scan for tileformat/tilesize) was itself an unported raw
  ((u8*)gdl)[0]/[1] + 64-bit words.w0<<11>>30 parser over the same
  D85-widened 16-byte room DL. #ifdef PORT: opcode from bits 24-31,
  byte1 from bits 16-23, (u32)-forced 32-bit width for the tilesize
  shift. N64 path kept under #else.
- Diagnostic: GE_D154=1 logs the parsed cmd / vtx-index stream (call
  header + per-TRI1/TRI4 idx) for the first 64 invocations so the
  integrator/user can eyeball the parse. Verification is playtest-gated
  (shoot a wall/floor on an idle machine) -- the no-input level sweep
  never enters this function.

Build green (ntsc-final, 242/242). ABI/GBI-parse only, no game-logic
change. docs/PCPortResearch.md D154 + PORT-LEARNINGS §B updated.
2026-08-31 12:48:16 -04:00
James Dansereau 6f45df9dde M-30b docs: 6-agent burst results (D158/D159/D161/D164/D165/D166/D160) merged + verified 2026-08-31 12:25:07 -04:00
James Dansereau deada1bb5f M-30 docs: with-input crash sweep results (21/21 crash-free, D158 holds) 2026-08-31 12:18:33 -04:00
James Dansereau 99c5926924 D165/D166: front-end 1:1 pointer P-controller + speed-proportional hipfire pitch pulses
port/src/input.c only (port-layer, no game-code change):

D165 - the M-30 menu 'pointer mode' fed mouse velocity into the stick, but
front.c frontUpdateControlStickPosition integrates the stick as a velocity
into a screen-pixel cursor -> velocity^2 feel. Replace with a P-controller:
maintain menuEst{H,V} (our estimate of the game cursor, stepped each poll with
front.c's exact recurrence via menuCursorStep), accumulate menuTgt{H,V} from
raw mouse motion, emit stick = clamp(6.0*(target-est), +/-70). Gain 6.0 =>
per-poll error decay 0.45, no overshoot, ~8-poll settle, parks under the
game deadzone with no drift. Input.MenuPointerMode (default 1) keeps the old
velocity mode as a fallback; Input.MenuPointerSpeed scales the mapping;
GE_INPUTLOG prints an est/tgt/eff/stick trace. No mouse motion => emitted
stick unchanged => prior behaviour exactly.

D166 - hipfire mouse pitch was a fixed digital threshold while yaw is analog.
Emit the C-button as a duty-cycled pulse whose frequency scales with mouse-Y
speed (fast = solid hold as before, slow = sparse taps). Input.HipfirePitchSpeed
tunes it. Aim-mode (RMB, already analog) untouched.

Build green; -level_09 (~360 frames) + GE_STARTMENU=6 smoke crash-free.
Feel-check owed (headless can't drive the mouse).
2026-08-31 12:18:15 -04:00
James Dansereau d23802ff9b D161: Depot ceiling blue-speckle — CI tile with G_TT_NONE decoded as palette
The Depot roof (CI8 tile drawn with gsDPSetTextureLUT(G_TT_NONE)) was
decoded against a stale rdp.palette, producing blue/magenta noise that
aliased into radial rays on the receding ceiling plane. A CI-siz tile
with the TLUT disabled must be treated as raw intensity (I). Route
CI4/CI8 -> I4/I8 in import_texture() when palette_fmt == G_TT_NONE.

Adds GE_TEXDUMP (env-gated PPM + param dump) as an inert diagnostic.
-level_09/-30/-34 sweep PASS, unregressed.
2026-08-31 12:16:19 -04:00
James Dansereau 8dfc578f4e D159: no-op texSwapAltRowBytes on PC (fixes combed front-end photos); D164: legal-screen loop bound
D159 (RC1 / D149): texSwapAltRowBytes pre-swaps odd texture rows in u32
groups to cancel the N64 RDP odd-line TMEM address XOR during word block
loads. fast3d emulates no such XOR, so odd rows uploaded scrambled -> the
'interlaced' wallet-Bond photo and other large front-end I4/I8/IA8
textures. #ifdef PORT early-return; N64 body kept verbatim. Wallet photo
now renders as a clean grayscale portrait (still 180deg-rotated - D75
model-transform family, parked). -level_09 frame 200 coverage unregressed
(91.62 vs 91.66 golden = D117 noise), visual frame clean.

D164 (D76): constructor_menu00_legalscreen bounded its text loop on
&legalscreen_MRD assuming linker adjacency to legalpage_text_array;
mingw reorders file-scope globals (MRD lands 0x60 before the array) so
the do/while ran once. #ifdef PORT bounds on the array's own length.
2026-08-31 12:15:32 -04:00
James Dansereau e295ee7ed8 D164 docs: root-cause D76 disclaimer screen (linker-adjacency loop bound in front.c, not an image-table bug) 2026-08-31 12:10:52 -04:00
James Dansereau 93692a1470 D160 docs: Dam level-end cutscene (D148) trace + diagnostic write-up 2026-08-31 12:10:22 -04:00
James Dansereau e86ad05ba8 M-30 docs: SMALL-FIXES status refresh (B3/B4/B5 done, RC4 retracted, D2 caveat) 2026-08-31 11:16:54 -04:00
James Dansereau 1b0b566b90 M-30 docs: flag rebuild+regen requirement before next playtest 2026-08-31 11:15:33 -04:00
James Dansereau ef3de6c102 M-30 docs: session state + next-task summary at top of HANDOFF 2026-08-31 11:12:08 -04:00
James Dansereau 0083cdc10e docs: D157 §F entry + D120 marked FIXED 2026-08-31 11:09:41 -04:00
James Dansereau 6364ddaa4b M-30 docs: RC2 verification notes + framediff phash-noise clarification 2026-08-31 11:08:48 -04:00