Savestate: Don't break event callbacks when a save fails

CoreTiming::DoState replaced every event's callback with the anti-crash
one in every mode, relying on each module's restore to put it back. A save
that failed partway never got to those, and left the running game with
events that break into the debugger. The missing-section fallbacks then
also ran on the save: cheats and the mic re-registered events into the
wrong slots, and achievements reset the runtime.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-09-28 09:34:06 -06:00
1 parent 9140bc9548
commit 2e5edcd298
4 files changed
+27 -15

No files matched your search

+10 -6
View File
@@ -544,13 +544,17 @@ void DoState(PointerWrap &p) {
return;
}
// These (should) be filled in later by the modules.
for (int i = 0; i < current; ++i) {
event_types[i].callback = AntiCrashCallback;
event_types[i].name = "INVALID EVENT";
// These (should) be filled in later by the modules. Only when loading: a save that fails partway
// wouldn't get to all the restores, and would leave the running game with broken events.
if (p.mode == PointerWrap::MODE_READ) {
for (int i = 0; i < current; ++i) {
event_types[i].callback = AntiCrashCallback;
event_types[i].name = "INVALID EVENT";
}
nextEventTypeRestoreId = n - 1;
usedEventTypes.clear();
}
nextEventTypeRestoreId = n - 1;
usedEventTypes.clear();
// Needed in every pass, or each restore would look like a duplicate and get a new id.
restoredEventTypes.clear();
if (s >= 3) {
+7 -4
View File
@@ -270,10 +270,13 @@ void __CheatDoState(PointerWrap &p) {
// CoreTiming::DoState has already replaced the queue with the state's, which has no cheat
// event in it, so without scheduling one here hleCheat never runs again and cheats (plus
// the enable/disable polling) stay dead for the rest of the session.
CheatEvent = -1;
CoreTiming::RestoreRegisterEvent(CheatEvent, "CheatEvent", &hleCheat);
CoreTiming::RemoveEvent(CheatEvent);
CoreTiming::ScheduleEvent(msToCycles(GetRefreshMs()), CheatEvent, 0);
// (Not when a save failed earlier and every section after comes back missing.)
if (p.mode == PointerWrap::MODE_READ) {
CheatEvent = -1;
CoreTiming::RestoreRegisterEvent(CheatEvent, "CheatEvent", &hleCheat);
CoreTiming::RemoveEvent(CheatEvent);
CoreTiming::ScheduleEvent(msToCycles(GetRefreshMs()), CheatEvent, 0);
}
return;
}
+6 -4
View File
@@ -121,10 +121,12 @@ void __UsbMicShutdown() {
void __UsbMicDoState(PointerWrap &p) {
auto s = p.Section("sceUsbMic", 0, 3);
if (!s) {
// Still need to restore the event.
eventMicBlockingResume = -1;
CoreTiming::RestoreRegisterEvent(eventMicBlockingResume, "MicBlockingResume", &__MicBlockingResume);
waitingThreads.clear();
// Still need to restore the event (unless this is a save that failed earlier.)
if (p.mode == p.MODE_READ) {
eventMicBlockingResume = -1;
CoreTiming::RestoreRegisterEvent(eventMicBlockingResume, "MicBlockingResume", &__MicBlockingResume);
waitingThreads.clear();
}
return;
}
bool isMicStartedNow = Microphone::isMicStarted();
+4 -1
View File
@@ -877,7 +877,10 @@ void Idle() {
void DoState(PointerWrap &p) {
auto sw = p.Section("Achievements", 0, 1);
if (!sw) {
// Save state is missing the section.
// Save state is missing the section, or this is a save that failed earlier.
if (p.mode != PointerWrap::MODE_READ) {
return;
}
// Reset the runtime.
if (HasAchievementsOrLeaderboards()) {
auto ac = GetI18NCategory(I18NCat::ACHIEVEMENTS);