The load-time repairs in rcntFreeze/psxRcntFreeze exist only to heal .p2s
files saved by builds that predate the trigger fix (9f6288531d, 2026-08-09).
No new state can carry the scar, so once old states have aged out the loops
can simply be deleted; the sync-time guards stay.
The baseline-ahead guards added in 4e34e65b84 silently skipped the sync.
Post-fix, that condition is unreachable for ungated counters unless the
CPU clock itself moved backwards — which is exactly the signature of the
cross-thread nextEventCycle race that poisoned God of War II savestates
(fixed in the previous commit), and of any future clock-regression bug.
A silent skip would hide the next one; a console warning is what let the
exit-storm repro pinpoint this one.
rcntSyncCounter computed (cpuRegs.cycle - startCycle) / rate into a u32.
With 64-bit cycle counts, a baseline even one cycle AHEAD of now (a
transient state around savestate thaw and vsync-retime seams) underflows
the subtraction, and the truncated quotient becomes change=0xFFFFFFFF:
count += 0xFFFFFFFF and startCycle += 2^32 - rate, zero-extended into the
u64. The counter is then dead until cycle crosses the bogus baseline
(14.6s at EE clock), and the blown-out count drains at one overflow lap
per pass for minutes afterwards - and the scar rides along in every
savestate taken meanwhile. psxRcntSync had the identical pattern, where
one epoch is 116.5s at IOP clock.
This is the God of War II poisoned-savestate bug: the area-title banner
stays stuck and gorgon-eye chest pickups freeze for ~6 minutes after
loading an affected state, on every host that loads it. A poisoned state
carries EXACTLY startCycle = (cycle & ~(rate-1)) + 2^32 on EE timer 0,
byte-for-byte the arithmetic above. A/B from that state: 1200 frames on
the old code still shows the stuck banner; with this change it clears.
Guard the negative case (skip the sync; the counter resumes within one
tick), widen change to u64, and repair poisoned baselines/counts when
thawing a savestate so existing affected saves heal on load.
And associated cleanup.
On most compilers these days, it'll either inline the memset with vector
fills or rep stosq, or outline with a call to memset.
I trust the compiler is probably going to make a better decision here,
than manual SSE intrinsics.
Ends up a couple of percent faster in FMV decoding.