Census-driven: rebuilt the compiled set with -Wint-to-pointer-cast
-Wpointer-to-int-cast -Wint-conversion, giving 276 actionable sites, triaged
in three disjoint file groups. Group A (206 warnings) and most of C were
already-correct round trips; these are the real bugs.
- D301: chrCreateBloodStain() crashed on guard gunfire. Two defects —
portN64ToHost(0) returned base+0 instead of NULL (defeating the decomp's
`if (p != NULL)` failure checks), and Vertex.CollisionRelatedNode (a u32 vma
kept 32-bit by PROMOTE32) was read with a bare cast.
- D302: six more truncations. Four are the "twin function" pattern — one of a
near-duplicate solo/MP pair converted, the other missed. Includes bg.c:5213,
where a truncated loop bound meant bgRoomCalcBB() never ran its body on
arm64, leaving EVERY room in every level with an inverted bounding box.
Silent, not a crash. Possible relevance to open D249.
- D303: texReadUncompressed() aligned its destination through u32. Latent —
a probe over a full level boot showed the path is unreachable on PC
(textures come from the pre-converted sidecars).
- D306: MSAA=8 produced a completely black screen. GL_MAX_SAMPLES is 4, and
nothing clamped the request, so the renderbuffer was never allocated.
video.c's own comment claimed MSAA "snaps down to the highest supported
level"; it did not. Also fixed two index-vs-GL-name bugs in the same file.
- D308: the watch's Control page crashed. options.c cast a stack-local table
pointer to (s32) before passing it as WatchContButtonPositions*, and
watchRenderControllerOpaque forwarded the truncated value. Verified A/B
against the user's exact repro — fault address byte-identical.
- D305: PORT_ADDR_STRICT, a debug gate (off by default, compiled out) that
validates every u32->host conversion against the regions the port maps and
reports offenders with a backtrace. Zero reports across 6 levels, 5 of them
under full-control input.
Also records D307 (reticule jitter in aim mode) in the finding log as OPEN,
with the cause identified from live captures and no code change — an attempted
fix was reverted for not working.
Verified: clean build, 21/21 sweep, plus a targeted repro per fix.
- tools_pc/bundle-mac.sh: a signed, double-clickable GoldenEye.app. Homebrew's
libstdc++/libgcc_s and SDL2 are not system libraries, so they are bundled
and rewritten to @executable_path/@loader_path. Note sdl2-compat (Homebrew's
`sdl2`) finds its SDL3 backend through a RELATIVE rpath baked into the dylib,
so SDL3 ships too and that rpath is repointed — copy it without that and
sdl2-compat raises a modal error dialog from its initialiser and the app
hangs before main(). prepare-assets/ goes in Contents/Resources/: codesign
treats a directory under Contents/MacOS/ as nested code and fails the bundle.
- tools_pc/level_sweep_mac.sh: per-level crash-log sweep, symbolicated with
atos, with a rendered-pixel check so "runs but renders nothing" is caught.
- D299: sndPlaySfx() rejected EVERY sound on arm64 — all in-level SFX were
silent, while music played (separate player). Its guard predated the shifted
window: `sp >= 0x400000000ULL` (16 GiB) is correct on x86_64 (DRAM at
0x70000000) and rejects everything on arm64 (DRAM at ~0x1000_707b_5740).
Fixed with a portAddrIsInWindow() helper rather than absolute bounds.
- D300: cleanupObjects() did `u32 *obj = (u32)g_CurrentSetup.propDefs`,
truncating a host pointer to 32 bits. Crashed at stage unload — only
reachable once a level actually ends.
Verified: 21/21 on both the no-input and full-control sweeps; SFX voices
allocate on every level.
Adds native arm64 macOS support on top of #88's Intel macOS layer.
The one structural change arm64 needs is an address model. The port realises
the N64's 32-bit address space *as host addresses* — DRAM at 0x70000000, cart
at 0x10000000, plus ~1700 linker-absolute symbols — which is only sound while
host addresses fit in 32 bits. Native arm64 cannot map below 4 GiB
(__PAGEZERO; a shrunken pagezero is SIGKILLed at exec; PIE is mandatory;
MAP_32BIT fails). So the whole space is relocated to a fixed host base,
PORT_ADDR_BASE (16 TiB), with portN64ToHost / portHostToN64 / PORT_N64PTR as
the conversion chokepoints. PORT_ADDR_BASE is 0 on every other platform, so
all of this is an identity there.
That relocation then surfaces a class of latent 64-bit bugs that are invisible
at base 0 — any place the decomp round-trips a pointer through a 32-bit
integer. The src/ changes here are that class: pointer-width/layout only, no
behaviour change, N64 branch textually unchanged, each documented in
docs/dev/findings.md (D298/M2).
Also: Mach-O absolute-symbol generation at the same base, #pragma weak ->
symbol equates, __x86_64__ -> PLATFORM_64BIT, Darwin crash handler/ucontext,
shm_open DRAM, the AppKit main-thread guard, and the sidecar generation that
was the real boot blocker.
Verified: 21/21 solo levels boot, render and run crash-free.
Opt-in Input.PdMouseAim (F10 "PD mouse aim"): in aim mode the port only
accumulates the mouse position and the game's own crosshair integrator is
driven by it with PD's mouse damp (0.01), instead of GEPD's per-poll
overwrite fighting the weapon's ~0.8 per-tick damp (write*damp^k jitter).
Hook: #ifdef PORT block in sub_GAME_7F067FBC (aim-mode branch only),
identity when off; Rule-2 sign-off given by the maintainer.
Integrated locally from PR #96 with review fixes: finding renumbered
D337 -> D332; GE_PDMOUSEAIM lookup cached (GE_ENVSTR; it ran every poll
and aim tick); "live env" comments corrected. Windows build clean; bunker1
golden 3/3. Feel A/B owed.