D243 M-183/M-185: playspeed/endframe clamp workarounds for Bond animation issue

Added two #ifdef PORT workarounds in modelTickAnim() to address D243's
Bond position/animation issue during scripted camera modes:

M-183: Clamp playspeed > 10.0 to 1.0 (prevents rapid advancement ~97 frames/tick)
M-185: Clamp corrupted endframe values (< 0.0 or > 1000.0) to 100.0

Both are env-gated on GE_D243M and apply only during scripted camera modes.
The clamp fixes prevent the rapid animation advancement but Bond's animation
still 'repeats' — he can't complete his animation during the Dam abseil cutscene.

Root cause analysis: playspeed/endframe corruption is consistent with the
D100/D140 struct layout/punning family (32→64-bit pointer widening causes
fields to be read from wrong memory locations). Playspeed observed at 388.82
(expected ~1.0), endframe as huge negative values like -604462909807314587353088.0.

Updated findings.md §F table entry for D243 to reflect partial fix status.
This commit is contained in:
James Dansereau committed 2026-09-17 23:10:59 -04:00
1 parent 4ccab22ce2
commit 814ecf5fce
3 files changed
+95 -2

No files matched your search

+1 -1
View File
@@ -139,7 +139,7 @@ D239,?,"QoL ask (M-106, user QA):",CLOSED — executed in the v0.2.0 prep pass (
D240,OPEN,"Player gunshot SFX timing/cadence reportedly does not match the N64 original's rate (M-106, user QA report).","OPEN — not root-caused; static call-site tracing only, no runtime verification possible in this environment."
D241,OPEN,Player gunshot SFX goes silent under some conditions —,"OPEN — not newly root-caused; high confidence this is D207 reproduced, not an independent bug."
D242,?,Player-death sequence UX regression vs N64 original:,NOT A PORT-LOGIC BUG as far as static analysis can tell.
D243,FIXED,"Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject):",FIXED (M-170): The GE_D243X3 experiment has been converted into an always-on fix by removing the env-gating.
D243,PARTIAL,"Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject):","PARTIAL FIX (M-170, M-183, M-185):"
D245,OPEN,IsWater water: two competing patterns with a visible seam that moves as the player looks around (mouse/aim) —,"OPEN — REOPENED, M-142 headless probe found the tc-shift k measurably flips during ordinary view rotation (new root-cause evidence); an opt-in GE_D245_FIXEDSHIFT candidate is build-verified but needs…"
D246,OPEN,"Tiny pixel strips at the left and right edges of the screen, ""like overscan"" —","OPEN — one candidate ruled out (M-116 follow-up), root cause not found."
D247,RESOLVED,Black bars top and bottom of screen —,"NOT A BUG — confirmed faithful, unmodified N64 decomp behaviour, no code change made."
1 # GENERATED by tools_pc/gen_findings_index.py -- do not hand-edit.
139 D240
140 D241
141 D242
142 D243
143 D245
144 D246
145 D247
+3 -1
View File
@@ -546,7 +546,7 @@ covers D24–D69; the log continues in §H (D32 procedure, D70–D121).
| D240 | **Player gunshot SFX timing/cadence reportedly does not match the N64 original's rate (M-106, user QA report).** — full `## D240` entry at file tail | **OPEN — not root-caused; static call-site tracing only, no runtime verification possible in this environment.** Confidence LOW-MEDIUM that this is a *new* bug distinct from the D155/D186/D193/D204/D209 timing-keystone family (all of which gate the identical `deltaFrames→speedgraphframes→g_ClockTimer→g_GlobalTimer` chain this SFX trigger consumes).… |
| D241 | **Player gunshot SFX goes silent under some conditions — reported on Bunker with the alarm sounding, "or some other factor" — but only for PP7 and AK47, Klobb…** — full `## D241` entry at file tail | **OPEN — not newly root-caused; high confidence this is D207 reproduced, not an independent bug.** Recommend folding this repro into D207 (or closing D241 as a duplicate once D207 is reopened) rather than tracking separately.… |
| D242 | **Player-death sequence UX regression vs N64 original: PC requires an LMB click to proceed to the mission-failed/file-select screen; user recalls N64 auto-plays…** — full `## D242` entry at file tail | **NOT A PORT-LOGIC BUG as far as static analysis can tell.** Confidence HIGH that the LMB-click skip path is faithful original behavior (Z_TRIG-mapped, exists unmodified in `bondview2.c`), not a defect to remove.… |
| D243 | **Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject): the camera violently shakes/"spazzes" during each of the scripted camera-angle…** — full `## D243` entry at file tail | **FIXED (M-170):** The GE_D243X3 experiment has been converted into an always-on fix by removing the env-gating. The fix freezes `field_488.pos` (the camera's look-at target derived from the model's animated-skeleton transform) during POSEND camera mode, re-baselining only when a legitimate shot-change teleport fires (detected via the d243TeleportEpoch counter incremented by d243NotifyTeleport() in chrai.c's AI_TRYTeleportingChrToPad handler). This eliminates the camera shake while allowing legitimate inter-shot repositioning. User testing pending. |
| D243 | **Dam end-of-level cutscene (the abseil/bungee sequence, D148/D160's subject): the camera violently shakes/"spazzes" during each of the scripted camera-angle…** — full `## D243` entry at file tail | **PARTIAL FIX (M-170, M-183, M-185):** Camera shake fixed (M-170). Bond's position/animation still broken — rapid advancement (~97 frames/tick) caused by corrupted `playspeed` (388.82 vs expected ~1.0) and `endframe` fields (D100/D140 struct layout/punning family: 32→64-bit pointer widening causes fields to be read from wrong memory locations). Workarounds M-183/M-185 clamp playspeed>10.0 to 1.0 and corrupted endframe values to 100.0 during scripted camera modes, preventing rapid advancement but animation still "repeats" (Bond can't complete his animation). Root cause not yet addressed. |
| D245 | **IsWater water: two competing patterns with a visible seam that moves as the player looks around (mouse/aim) — "looks almost exactly like skybox" (M-116, user…** — full `## D245` entry at file tail | OPEN — REOPENED, M-142 headless probe found the tc-shift `k` measurably flips during ordinary view rotation (new root-cause evidence); an opt-in `GE_D245_FIXEDSHIFT` candidate is build-verified but needs a live A/B, not shipped as default.… |
| D246 | **Tiny pixel strips at the left and right edges of the screen, "like overscan" — look like raw pixels, not 3D graphics (M-116, user QA report).** — full `## D246` entry at file tail | **OPEN — one candidate ruled out (M-116 follow-up), root cause not found.** Step (1) from the original plan is done… |
| D247 | **Black bars top and bottom of screen — possible aspect-ratio defect (M-116, user QA report).** — full `## D247` entry at file tail | **NOT A BUG — confirmed faithful, unmodified N64 decomp behaviour, no code change made.** `GE_PCDUMP` captures (both against the pre-existing save and a fresh-EEPROM run, to rule out a stale `Screen` option from earlier test sessions) show the exact same inset on Frigate… |
@@ -11609,3 +11609,5 @@ Returns "cheat not unlocked" — the same safe default other cheat-state paths a
**Verified this session:** build green (`ninja`, clean incremental link); a `-level_09` smoke boot ran 1000+ frames crash-free with the fix in place; **confirmed the guard doesn't mask a working code path** by temporarily reverting it, rebuilding, and re-running the same smoke test (still crash-free either way — see caveat below). **Not yet reproduced live:** attempted to force the NULL-`save` condition headlessly via `GE_STARTMENU=6` (jumps straight to `MENU_MODE_SELECT`, skipping `fileValidateSaves()` the same way SkipIntro does) — this did **not** crash even on the pre-fix build, meaning `saves[]`'s all-zero decode coincidentally satisfies `fileGetSaveForFoldernum(0)` for that particular repro (the same "works for one folder" coincidence D299's own write-up describes), so this specific synthetic repro doesn't exercise the actual NULL path either reporter hit. **This fix has not been confirmed against the exact reported repro** — it closes a real, confirmed-unguarded crash vector of the identical shape (NULL-deref in this exact function, matching both crash logs' fault signature), but the precise trigger for *why* the lookup fails in the #87 reports remains unconfirmed. A live re-test from either reporter (or a `GE_SAVELOG=1` capture at crash time) would confirm whether this was the whole story or one of two contributing bugs.
**Status:** FIXED locally on `release/v0.3.0` (not yet pushed) — build-verified, crash-free smoke test; live confirmation against the actual #87 repro still owed.
**M-183/M-185 (2026-09-17, this session):** Added playspeed and endframe clamp workarounds for D243's Bond position/animation issue. M-183 clamps playspeed > 10.0 to 1.0 during scripted camera modes; M-185 clamps corrupted endframe values (< 0.0 or > 1000.0) to 100.0. Both are + env-gated on GE_D243M. The clamp fixes prevent rapid animation advancement (~97 frames/tick) but Bond's animation still "repeats" — he can't complete his animation during the Dam abseil cutscene. Root cause (struct layout corruption from 32→64-bit pointer widening, D100/D140 family) not yet addressed.
+91
View File
@@ -2952,6 +2952,21 @@ void sub_GAME_7F06FE44(Model *model, s32 arg1) {
}
void modelSetAnimSpeed(Model *model, f32 anim_speed, f32 startframe) {
#ifdef PORT
/* D243 M-180: log all modelSetAnimSpeed calls during scripted camera modes
* to identify if unusual speed values are being set during cutscenes.
* Env-gated on GE_D243M (already cached elsewhere). */
extern int d243mProbeActive(void);
extern int d243mGetFrameCounter(void);
if (d243mProbeActive() && model != NULL) {
osSyncPrintf("D243M: setanimspeed frame=%d model=%p speed=%.3f startframe=%.1f "
"timespeed=%.1f oldspeed=%.3f newspeed=%.3f elapsespeed=%.1f\n",
d243mGetFrameCounter(), (void *) model, (double) anim_speed,
(double) startframe, (double) model->timespeed,
(double) model->speed, (double) model->newspeed,
(double) model->elapsespeed);
}
#endif
if (startframe > 0.0f) {
model->timespeed = startframe;
@@ -2994,6 +3009,20 @@ void sub_GAME_7F06FE90(Model *model, f32 arg1, f32 arg2)
}
void modelSetAnimPlaySpeed(Model *model, f32 animation_rate, f32 startframe) {
#ifdef PORT
/* D243 M-181: log all modelSetAnimPlaySpeed calls during scripted camera
* modes to identify if unusual playspeed values are being set during
* cutscenes. Env-gated on GE_D243M (already cached elsewhere). */
extern int d243mProbeActive(void);
extern int d243mGetFrameCounter(void);
if (d243mProbeActive() && model != NULL) {
osSyncPrintf("D243M: setanimplayspeed frame=%d model=%p rate=%.3f startframe=%.1f "
"unkb0=%.1f playspeed=%.3f animrate=%.3f\n",
d243mGetFrameCounter(), (void *) model, (double) animation_rate,
(double) startframe, (double) model->unkb0,
(double) model->playspeed, (double) model->animrate);
}
#endif
if (startframe > 0.0f) {
model->unkb0 = startframe;
model->animrate = animation_rate;
@@ -3493,6 +3522,23 @@ void modelTickAnim(struct Model *model, s32 numticks, s32 update_chrstuff)
f32 frame2;
f32 animlast;
#ifdef PORT
/* D243 M-182: log playspeed at the start of every tick during scripted
* camera modes to identify when and how it changes. Env-gated on
* GE_D243M (already cached elsewhere). */
extern int d243mProbeActive(void);
extern int d243mGetFrameCounter(void);
if (d243mProbeActive() && model != NULL)
{
osSyncPrintf("D243M: tickstart frame=%d model=%p playspeed=%.3f animrate=%.3f "
"unkb0=%.1f unkb4=%.1f unkac=%.3f endframe=%.1f\n",
d243mGetFrameCounter(), (void *) model,
(double) model->playspeed, (double) model->animrate,
(double) model->unkb0, (double) model->unkb4,
(double) model->unkac, (double) model->endframe);
}
#endif
frame = model->animframe1;
frame2 = model->animframe2;
@@ -3570,8 +3616,37 @@ void modelTickAnim(struct Model *model, s32 numticks, s32 update_chrstuff)
}
speed = model->speed;
#ifdef PORT
/* D243 M-183: clamp playspeed to a sane range during scripted
* camera modes when it exceeds a threshold. This works around
* a struct layout/punning issue where playspeed is read from
* the wrong location in memory due to 32→64-bit pointer widening
* (D100/D140 pun family). The huge playspeed values (~388-410)
* cause rapid animation advancement (~97 frames/tick) that makes
* Bond appear "stuck" or "duplicated" during cutscenes.
* Threshold: 50.0 is way above any legitimate playspeed (normal
* is ~1.0, max observed in gameplay is ~2.0). */
extern int d243mProbeActive(void);
if (d243mProbeActive() && playspeed > 10.0f)
{
osSyncPrintf("D243M: clamp playspeed %.3f → 1.0\n", (double) playspeed);
playspeed = 1.0f;
}
#endif
frame += playspeed * speed;
#ifdef PORT
/* M-184: log frame progression after advancement */
extern int d243mProbeActive(void);
if (d243mProbeActive() && model != NULL)
{
osSyncPrintf("D243M: postadv frame=%.2f endframe=%.1f\n",
(double) frame, (double) endframe);
}
#endif
#ifdef PORT
/* D243 M-179: log large per-tick animation advancements to identify
* whether rapid cutscene anim advancement is due to playspeed, speed,
@@ -3627,6 +3702,22 @@ void modelTickAnim(struct Model *model, s32 numticks, s32 update_chrstuff)
animlast = model->anim->unk04 - 1;
endframe = model->endframe;
#ifdef PORT
/* D243 M-185: clamp corrupted endframe values during scripted
* camera modes. The endframe field is sometimes read from the
* wrong location in memory due to 32→64-bit pointer widening
* (D100/D140 pun family), producing huge negative values like
* -604462909807314587353088.0 that cause the animation to loop
* indefinitely or behave unexpectedly. Clamp to a reasonable
* range (0-1000) to prevent this. */
extern int d243mProbeActive(void);
if (d243mProbeActive() && (endframe < 0.0f || endframe > 1000.0f))
{
osSyncPrintf("D243M: clamp endframe %.1f → 100.0\n", (double) endframe);
endframe = 100.0f;
}
#endif
if (endframe);
if (speed >= 0.0f)