From 54e4be47aaec8cf747a245e361359c3fae0804b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 29 Sep 2026 16:42:38 -0600 Subject: [PATCH] CoreTiming: Grow the event table for events a savestate lacks Taking the highest unused slot instead could steal one that a state event restores later, as VBlankWake did to MicBlockingResume, which then had nowhere to go. Also name the event in the assert. AGENTS.md: When a savestate fails to load, suspect the branch first. Co-Authored-By: Claude Opus 5.5 (1M context) --- AGENTS.md | 4 ++++ Core/CoreTiming.cpp | 13 ++++--------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1f48c19da8..f16b2f0d2a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -108,6 +108,10 @@ $msbuild = "$installPath\MSBuild\Current\Bin\MSBuild.exe" - **A stale binary lies consistently.** After a `git stash` cycle that touched a header, do a `/t:Rebuild`; when bisecting a behavioural change, confirm the binary actually changed before you believe the result. +- **If a savestate fails to load, first suspect the branch, not the loader.** Either your branch is + behind the build that made the state (rebase it on `origin/master` and rebuild), or the state was + made on a branch that hasn't been merged yet. Only once you've ruled out both is it a savestate + compatibility bug. UWP, the legacy Android NDK build and the libretro core have their own build systems. diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 4bbcdde74d..ca20954121 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -193,16 +193,11 @@ void RestoreRegisterEvent(int &event_type, const char *name, TimedCallback callb if (event_type == -1) event_type = nextEventTypeRestoreId++; if (event_type >= (int)event_types.size()) { - // Give it any unused event id starting from the end. - // Older save states with messed up ids have gaps near the end. - for (int i = (int)event_types.size() - 1; i >= 0; --i) { - if (usedEventTypes.count(i) == 0) { - event_type = i; - break; - } - } + // An event the state doesn't have. Grow the table: an unused slot below may still belong to + // a state event whose module restores it later. + event_types.resize(event_type + 1, EventType{ AntiCrashCallback, "INVALID EVENT" }); } - _assert_msg_(event_type >= 0 && event_type < (int)event_types.size(), "Invalid event type %d", event_type); + _assert_msg_(event_type >= 0 && event_type < (int)event_types.size(), "Invalid event type %d (%s, of %d)", event_type, name, (int)event_types.size()); event_types[event_type] = EventType{ callback, name }; usedEventTypes.insert(event_type); restoredEventTypes.insert(event_type);