forked from Ports/goldeneye-pc-port
After D155 the user re-ran the playthrough and it hung again at the end of Facility -- same stack but frames=12962 frozen CONSTANT for minutes = a true infinite loop, not D155's slow spiral. The `while (1)` at model.c:3131 in modelSetAnimFrame2WithChrStuff steps root-motion accumulation one anim frame at a time from framea to frameb. frameb comes from modelTickAnim's `frame += playspeed * speed` per tick. A cutscene anim transition with a near-zero blend/timespeed/unkb0 denominator (model.c:3436/:3477, guarded >0 but not against tiny values) makes model->speed/playspeed huge or NaN -> frameb huge/NaN -> floorFloatToInt garbage -> endframe-curframe ~= 2^31 -> effectively infinite. #ifdef PORT guards (N64 unchanged): - modelSetAnimFrame2WithChrStuff: if frameb non-finite or |frameb| >= 1e6, snap to framea (loop does ~0 iterations; modelConstrainOrWrapAnimFrame at :3211 still clamps model->frameb to a real frame). - modelTickAnim: after the tick loop, if frame/frame2 went non-finite, fall back to the pre-loop model->animframe1/animframe2 so the NaN can't poison model state every frame. `!(x > -1e6f && x < 1e6f)` catches NaN too. Anim frames are 0..few-hundred so 1e6 is a safe ceiling. Likely deeper cause (needs the repro): these speed/blend fields are Model members -- a D100/D140-style struct-pun misalignment would feed garbage exactly when a cutscene sets an unusual transition. Dump model->speed/playspeed/timespeed/unkb0 in modelTickAnim during the cutscene if it still hangs. Verified: build green; -level_09 frame 200 unregressed (guards never fire in normal play). PORT-LEARNINGS §E.