diff --git a/Core/CoreTiming.cpp b/Core/CoreTiming.cpp index 5708f075f0..df8ab549d7 100644 --- a/Core/CoreTiming.cpp +++ b/Core/CoreTiming.cpp @@ -551,7 +551,8 @@ void DoState(PointerWrap &p) { event_types[i].callback = AntiCrashCallback; event_types[i].name = "INVALID EVENT"; } - nextEventTypeRestoreId = n - 1; + // The state's own events are 0..n-1, so one it doesn't have gets the first id after those. + nextEventTypeRestoreId = n; usedEventTypes.clear(); } // Needed in every pass, or each restore would look like a duplicate and get a new id. @@ -578,6 +579,10 @@ void DoState(PointerWrap &p) { lastGlobalTimeTicks = 0; lastGlobalTimeUs = 0; } + if (p.mode == PointerWrap::MODE_READ) { + // A debugger's run-until deadline is in emulated us, and the ticks it maps to just changed. + RecomputeBreakDeadline(); + } __AudioCPUMHzChange(); } diff --git a/Core/Dialog/PSPSaveDialog.cpp b/Core/Dialog/PSPSaveDialog.cpp index 08a9b88078..d3bde4a164 100755 --- a/Core/Dialog/PSPSaveDialog.cpp +++ b/Core/Dialog/PSPSaveDialog.cpp @@ -1373,8 +1373,9 @@ void PSPSaveDialog::DoState(PointerWrap &p) { // value is never SAVEIO_PENDING. Without this, loading a state taken // while a savedata operation was in flight would restart the operation // instead of resuming from its recorded status. Version 4 keeps the - // results of a finished operation that haven't been taken yet. - auto s = p.Section("PSPSaveDialog", 1, 5); + // results of a finished operation that haven't been taken yet. Version 6 keeps originalRequest, + // or the first Update after a load would reload the request and lose results kept in request. + auto s = p.Section("PSPSaveDialog", 1, 6); if (!s) { return; } @@ -1407,6 +1408,18 @@ void PSPSaveDialog::DoState(PointerWrap &p) { if (s == 4) { DoStateOldPendingWrites(p); } + if (s >= 6) { + Do(p, originalRequest); + } else if (p.mode == p.MODE_READ) { + // Best guess: what the game's request holds now, the way Update reads it. + memset(&originalRequest, 0, sizeof(originalRequest)); + if (Memory::IsValidRange(requestAddr, 4)) { + const u32 size = std::min((u32)sizeof(originalRequest), Memory::ReadUnchecked_U32(requestAddr)); + if (Memory::IsValidRange(requestAddr, size)) { + Memory::Memcpy(&originalRequest, requestAddr, size); + } + } + } } pspUtilityDialogCommon *PSPSaveDialog::GetCommonParam() { diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index 411f1161a8..9435a0e970 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -1093,7 +1093,8 @@ void DirectoryFileSystem::DoState(PointerWrap &p) { Do(p, entry.guestFilename); Do(p, entry.access); u32 err; - bool opened = entry.hFile.Open(basePath, entry.guestFilename, entry.access, err); + // The original open created the file, so an exclusive create would fail now. + bool opened = entry.hFile.Open(basePath, entry.guestFilename, (FileAccess)(entry.access & ~FILEACCESS_EXCL), err); bool brokenFile = !opened; if (!opened) { ERROR_LOG(Log::FileSystem, "Failed to reopen file while loading state: %s", entry.guestFilename.c_str()); diff --git a/Core/FileSystems/ISOFileSystem.cpp b/Core/FileSystems/ISOFileSystem.cpp index b0ccb9ec22..aae8acc549 100644 --- a/Core/FileSystems/ISOFileSystem.cpp +++ b/Core/FileSystems/ISOFileSystem.cpp @@ -857,6 +857,12 @@ void ISOFileSystem::DoState(PointerWrap &p) { std::string path; Do(p, path); of.file = GetFromPath(path); + if (!of.file) { + // Loaded against a different image. Like a file that's gone from a directory, + // drop the handle rather than keep one that points at nothing. + ERROR_LOG(Log::FileSystem, "Failed to reopen file while loading state: %s", path.c_str()); + continue; + } } else { of.file = NULL; } diff --git a/Core/HLE/HLE.cpp b/Core/HLE/HLE.cpp index e66e489806..faa6054a04 100644 --- a/Core/HLE/HLE.cpp +++ b/Core/HLE/HLE.cpp @@ -252,8 +252,8 @@ static bool g_disableHLELatched; static DisableHLEFlags g_unavailableDisableFlags = (DisableHLEFlags)0; // Process compat flags. -static DisableHLEFlags ComputeDisableHLEFlags() { - DisableHLEFlags flags = (DisableHLEFlags)g_Config.iDisableHLE | AlwaysDisableHLEFlags(); +static DisableHLEFlags ComputeDisableHLEFlags(DisableHLEFlags alwaysDisabled = AlwaysDisableHLEFlags()) { + DisableHLEFlags flags = (DisableHLEFlags)g_Config.iDisableHLE | alwaysDisabled; if (PSP_CoreParameter().compat.flags().DisableHLESceFont) { flags |= DisableHLEFlags::sceFont; } @@ -472,6 +472,12 @@ void HLEDoState(PointerWrap &p) { g_effectiveDisableHLE = (DisableHLEFlags)disableHLE; g_disableHLELatched = true; } + } else if (p.mode == p.MODE_READ) { + // Older states didn't save the flags. They were all made before any module graduated past + // these, so resolving their imports against today's defaults would leave the ones since + // (sceMpeg, sceFont, the leaf libraries...) as unresolved stubs. + g_effectiveDisableHLE = ComputeDisableHLEFlags(DisableHLEFlags::scePsmf | DisableHLEFlags::scePsmfPlayer | DisableHLEFlags::sceCcc); + g_disableHLELatched = true; } // Can't be inside a syscall when saving state, reset this so errors aren't misleading. diff --git a/Core/HLE/sceAac.cpp b/Core/HLE/sceAac.cpp index 73567d0853..f70b29d969 100644 --- a/Core/HLE/sceAac.cpp +++ b/Core/HLE/sceAac.cpp @@ -43,8 +43,12 @@ void __AACShutdown() { void __AACDoState(PointerWrap &p) { auto s = p.Section("sceAAC", 0, 1); - if (!s) + if (!s) { + if (p.mode == PointerWrap::MODE_READ) { + __AACShutdown(); + } return; + } Do(p, g_aacMap); } diff --git a/Core/HLE/sceAtrac.cpp b/Core/HLE/sceAtrac.cpp index 096cd7cc93..58ebe7c143 100644 --- a/Core/HLE/sceAtrac.cpp +++ b/Core/HLE/sceAtrac.cpp @@ -255,6 +255,10 @@ void __AtracDoState(PointerWrap &p) { u32 count = (u32)g_pendingOutput.size(); Do(p, count); if (p.mode == PointerWrap::MODE_READ) { + if (!p.CheckRead((size_t)count * sizeof(u32) * 2)) { + g_pendingOutput.clear(); + return; + } g_pendingOutput.resize(count); } for (AtracPendingOutput &pending : g_pendingOutput) { diff --git a/Core/HLE/sceAudiocodec.cpp b/Core/HLE/sceAudiocodec.cpp index d738cd36bf..93de522240 100644 --- a/Core/HLE/sceAudiocodec.cpp +++ b/Core/HLE/sceAudiocodec.cpp @@ -658,9 +658,15 @@ void Register_sceAudiocodec() { void __sceAudiocodecDoState(PointerWrap &p){ auto s = p.Section("AudioList", 0, 2); if (!s) { - oldStateLoaded = true; + if (p.mode == PointerWrap::MODE_READ) { + clearDecoders(); + oldStateLoaded = true; + } return; } + if (p.mode == PointerWrap::MODE_READ) { + oldStateLoaded = false; + } int count = (int)g_audioDecoderContexts.size(); Do(p, count); diff --git a/Core/HLE/sceDisplay.cpp b/Core/HLE/sceDisplay.cpp index 6b5da48bd4..4f81dd093d 100644 --- a/Core/HLE/sceDisplay.cpp +++ b/Core/HLE/sceDisplay.cpp @@ -299,6 +299,10 @@ void __DisplayDoState(PointerWrap &p) { Do(p, lastFlipCycles); Do(p, nextFlipCycles); } + if (p.mode == p.MODE_READ) { + // Not saved. Start counting again rather than carry over the session before the load. + lastFlipsTooFrequent = 0; + } gpu->DoState(p); diff --git a/Core/HLE/sceFont.cpp b/Core/HLE/sceFont.cpp index 32b72b1756..0f3b0354e3 100644 --- a/Core/HLE/sceFont.cpp +++ b/Core/HLE/sceFont.cpp @@ -1107,6 +1107,9 @@ void __FontDoState(PointerWrap &p) { Do(p, actionPostAllocCallback); __KernelRestoreActionType(actionPostAllocCallback, PostAllocCallback::Create); + if (s >= 2) { + useAllocCallbacks = true; + } Do(p, actionPostOpenCallback); __KernelRestoreActionType(actionPostOpenCallback, PostOpenCallback::Create); if (s >= 2) { diff --git a/Core/HLE/sceImpose.cpp b/Core/HLE/sceImpose.cpp index c8b506aea1..0a62a25523 100644 --- a/Core/HLE/sceImpose.cpp +++ b/Core/HLE/sceImpose.cpp @@ -70,6 +70,9 @@ void __ImposeDoState(PointerWrap &p) { if (s >= 2) { Do(p, imposeChanges); Do(p, imposeAvls); + } else if (p.mode == p.MODE_READ) { + imposeChanges = 0; + imposeAvls = 0; } } diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 2bbc70d3fd..f9058bfcae 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -726,6 +726,9 @@ void __IoDoState(PointerWrap &p) { if (s >= 3) { Do(p, lastMemStickState); Do(p, lastMemStickFatState); + } else if (p.mode == p.MODE_READ) { + lastMemStickState = MemoryStick_State(); + lastMemStickFatState = MemoryStick_FatState(); } for (int i = 0; i < PSP_COUNT_FDS; ++i) { diff --git a/Core/HLE/sceKernel.cpp b/Core/HLE/sceKernel.cpp index 0667a9d9e3..c9b80bee98 100644 --- a/Core/HLE/sceKernel.cpp +++ b/Core/HLE/sceKernel.cpp @@ -170,6 +170,7 @@ void __KernelInit() __HttpInit(); __NpInit(); __RegInit(); + __NpDrmInit(); SaveState::Init(); // Must be after IO, as it may create a directory Reporting::Init(); @@ -314,6 +315,10 @@ void __KernelDoState(PointerWrap &p) __UsbGpsDoState(p); __UsbMicDoState(p); __RegDoState(p); + // These two were written long ago but never called, so older states lack them. + __DmacDoState(p); + __UsbCamDoState(p); + __NpDrmDoState(p); // IMPORTANT! Add new sections last! } diff --git a/Core/HLE/sceKernelModule.cpp b/Core/HLE/sceKernelModule.cpp index 15eeec6a21..bb525627ae 100644 --- a/Core/HLE/sceKernelModule.cpp +++ b/Core/HLE/sceKernelModule.cpp @@ -471,6 +471,9 @@ struct SceKernelSMOption { static int actionAfterModule; static std::set loadedModules; +// Set once we've seen a PSP_MODULE_VSH_MODE module load (i.e. we're booting the VSH rather +// than a game), and reset on the next __KernelLoadExec. See ShouldHLEModuleForLoad below. +static bool g_runningVSH = false; // STATE END ////////////////////////////////////////////////////////////////////////// @@ -479,7 +482,7 @@ static void __KernelModuleInit() { } void __KernelModuleDoState(PointerWrap &p) { - auto s = p.Section("sceKernelModule", 1, 2); + auto s = p.Section("sceKernelModule", 1, 3); if (!s) return; @@ -491,6 +494,19 @@ void __KernelModuleDoState(PointerWrap &p) { if (s >= 2) { Do(p, loadedModules); } + if (s >= 3) { + Do(p, g_runningVSH); + } else if (p.mode == p.MODE_READ) { + // Derive it the way the loader sets it. + g_runningVSH = false; + for (SceUID moduleId : loadedModules) { + u32 error; + PSPModule *module = kernelObjects.Get(moduleId, error); + if (module && ((module->nm.attribute & PSP_MODULE_VSH_MODE) != 0 || equals(module->nm.name, "vsh_module"))) { + g_runningVSH = true; + } + } + } if (p.mode == p.MODE_READ) { u32 error; @@ -1120,10 +1136,6 @@ enum : u32 { ELF_MAGIC = 0x464c457f, }; -// Set once we've seen a PSP_MODULE_VSH_MODE module load (i.e. we're booting the VSH rather -// than a game), and reset on the next __KernelLoadExec. See ShouldHLEModuleForLoad below. -static bool g_runningVSH = false; - // A few flash0 modules (VSH's own bridge/UI/utility libraries) should only ever be genuinely // loaded - rather than faked via any HLE implementation we may have for them - once we know // we're actually running the VSH. A regular game never legitimately loads these, so this only diff --git a/Core/HLE/sceMp3.cpp b/Core/HLE/sceMp3.cpp index 9e05bdcede..225b3937d8 100644 --- a/Core/HLE/sceMp3.cpp +++ b/Core/HLE/sceMp3.cpp @@ -158,8 +158,13 @@ void __Mp3Shutdown() { void __Mp3DoState(PointerWrap &p) { auto s = p.Section("sceMp3", 0, 3); - if (!s) + if (!s) { + if (p.mode == PointerWrap::MODE_READ) { + __Mp3Shutdown(); + resourceInited = false; + } return; + } if (s >= 2) { Do(p, g_mp3Map); diff --git a/Core/HLE/sceMpeg.cpp b/Core/HLE/sceMpeg.cpp index 87f7e660d2..c2f0f6371a 100644 --- a/Core/HLE/sceMpeg.cpp +++ b/Core/HLE/sceMpeg.cpp @@ -364,6 +364,10 @@ void __MpegInit() { isMpegInit = false; mpegLibVersion = 0x010A; streamIdGen = 1; + useRingbufferPutCallbackMulti = true; + sceMpegAvcResourceAddr = 0; + sceMpegAvcResourceDataAddr = 0; + sceMpegAvcResourceFlags = 0; actionPostPut = __KernelRegisterActionType(PostPutAction::Create); #ifdef USE_FFMPEG @@ -377,7 +381,7 @@ void __MpegInit() { } void __MpegDoState(PointerWrap &p) { - auto s = p.Section("sceMpeg", 1, 4); + auto s = p.Section("sceMpeg", 1, 5); if (!s) return; @@ -394,6 +398,7 @@ void __MpegDoState(PointerWrap &p) { useRingbufferPutCallbackMulti = false; ringbufferPutPacketsAdded = 0; } else { + useRingbufferPutCallbackMulti = true; Do(p, ringbufferPutPacketsAdded); } if (s < 4) { @@ -411,6 +416,18 @@ void __MpegDoState(PointerWrap &p) { __KernelRestoreActionType(actionPostPut, PostPutAction::Create); Do(p, g_mpegCtxs); + + if (s >= 5) { + Do(p, sceMpegAvcResourceFlags); + } else { + sceMpegAvcResourceFlags = 0; + } + if (p.mode == p.MODE_READ) { + // Constant for now, see sceMpegAvcResourceInit. + const bool inited = (sceMpegAvcResourceFlags & MPEG_AVC_RESOURCE_FLAG) != 0; + sceMpegAvcResourceAddr = inited ? 0x10000000 : 0; + sceMpegAvcResourceDataAddr = inited ? sceMpegAvcResourceAddr + 8 : 0; + } } void __MpegShutdown() { diff --git a/Core/HLE/sceNet.cpp b/Core/HLE/sceNet.cpp index 589a6bc1a3..2e6a22fee1 100644 --- a/Core/HLE/sceNet.cpp +++ b/Core/HLE/sceNet.cpp @@ -29,6 +29,7 @@ #include "Common/System/OSD.h" #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" +#include "Common/Serialize/SerializeDeque.h" #include "Common/Serialize/SerializeMap.h" #include "Common/Data/Format/JSONReader.h" #include "Common/System/System.h" @@ -707,7 +708,7 @@ void netValidateLoopMemory() { // This feels like a dubious proposition, mostly... void __NetDoState(PointerWrap &p) { - auto s = p.Section("sceNet", 1, 6); + auto s = p.Section("sceNet", 1, 7); if (!s) return; @@ -763,6 +764,18 @@ void __NetDoState(PointerWrap &p) { netApctlInfoId = 0; NetApctl_InitDefaultInfo(); } + if (s >= 7) { + // The state only moves on when an event is processed, and each queues the next, so a + // connect in progress would never finish without them. + std::lock_guard apctlGuard(apctlEvtMtx); + Do(p, apctlEvents); + // Allocated from user memory, which the load just replaced. + Do(p, apctlProdCodeAddr); + } else if (p.mode == p.MODE_READ) { + std::lock_guard apctlGuard(apctlEvtMtx); + apctlEvents.clear(); + apctlProdCodeAddr = 0; + } if (p.mode == p.MODE_READ) { // Let's not change "Inited" value when Loading SaveState in the middle of multiplayer to prevent memory & port leaks @@ -770,8 +783,6 @@ void __NetDoState(PointerWrap &p) { netInetInited = cur_netInetInited; g_netInited = cur_netInited; - // Discard leftover events - apctlEvents.clear(); // Discard created resolvers for now (since i'm not sure whether the information in the struct is sufficient or not, and we don't support multi-threading yet anyway) __NetResolverShutdown(); } diff --git a/Core/HLE/scePspNpDrm_user.cpp b/Core/HLE/scePspNpDrm_user.cpp index 6ef44d4ebf..711db775f0 100644 --- a/Core/HLE/scePspNpDrm_user.cpp +++ b/Core/HLE/scePspNpDrm_user.cpp @@ -1,6 +1,8 @@ #include "ext/libkirk/AES.h" #include "ext/libkirk/amctrl.h" +#include "Common/Serialize/Serializer.h" +#include "Common/Serialize/SerializeFuncs.h" #include "Core/HLE/scePspNpDrm_user.h" #include "Core/MemMapHelpers.h" #include "Core/HLE/HLE.h" @@ -16,6 +18,25 @@ static const int PSP_NPDRM_LICENSEE_KEY_LENGTH = 0x10; static u8 licenseeKey[PSP_NPDRM_LICENSEE_KEY_LENGTH]; static bool isLicenseeKeySet = false; +void __NpDrmInit() { + memset(licenseeKey, 0, sizeof(licenseeKey)); + isLicenseeKeySet = false; +} + +void __NpDrmDoState(PointerWrap &p) { + auto s = p.Section("sceNpDrm", 0, 1); + if (!s) { + // Older states didn't keep the key. The game set it once at startup, so EDATA it opens after + // the load can't be decrypted until it runs again. + if (p.mode == PointerWrap::MODE_READ) { + __NpDrmInit(); + } + return; + } + Do(p, isLicenseeKeySet); + DoArray(p, licenseeKey, PSP_NPDRM_LICENSEE_KEY_LENGTH); +} + // Check if the file is an encrypted EDAT file by reading the magic number static bool isEncrypted(u32 edataFd) { // Check for "\0PSPEDAT" magic number diff --git a/Core/HLE/scePspNpDrm_user.h b/Core/HLE/scePspNpDrm_user.h index 35af832eee..40c3645268 100644 --- a/Core/HLE/scePspNpDrm_user.h +++ b/Core/HLE/scePspNpDrm_user.h @@ -5,6 +5,8 @@ class PointerWrap; void Register_sceNpDrm(); +void __NpDrmInit(); +void __NpDrmDoState(PointerWrap &p); // A module wrapped in an NPDRM "\0PSPEDAT" container has its PRX encrypted against a key built // from that container's header and, usually, the licensee key the game handed over through diff --git a/Core/HLE/sceReg.cpp b/Core/HLE/sceReg.cpp index c43fc4321b..54b0357fa1 100644 --- a/Core/HLE/sceReg.cpp +++ b/Core/HLE/sceReg.cpp @@ -1010,9 +1010,13 @@ static const KeyValue *LookupCategory(std::string_view path, int *count) { } void __RegDoState(PointerWrap &p) { - auto s = p.Section("sceReg", 0, 2); - if (!s) + auto s = p.Section("sceReg", 0, 3); + if (!s) { + if (p.mode == PointerWrap::MODE_READ) { + __RegInit(); + } return; + } Do(p, g_openRegistryMode); Do(p, g_openCategories); if (s >= 2) { @@ -1021,6 +1025,15 @@ void __RegDoState(PointerWrap &p) { // Old states didn't track this. Anything with a category open had the registry open too. g_openRegistryCount = g_openCategories.empty() ? 0 : 1; } + if (s >= 3) { + Do(p, g_handleGen); + } else if (p.mode == PointerWrap::MODE_READ) { + // Don't hand out a handle that's still open in the state. + g_handleGen = 1337; + if (!g_openCategories.empty()) { + g_handleGen = std::max(g_handleGen, g_openCategories.rbegin()->first + 1); + } + } } // Registry level (it seems only /system can exist, so kinda pointless) diff --git a/Core/HLE/sceUmd.cpp b/Core/HLE/sceUmd.cpp index 439de53786..40b67d3852 100644 --- a/Core/HLE/sceUmd.cpp +++ b/Core/HLE/sceUmd.cpp @@ -109,6 +109,8 @@ void __UmdDoState(PointerWrap &p) if (g_UMDReplacePermit && p.mode == p.MODE_READ) { System_Notify(SystemNotification::UI); } + } else if (p.mode == p.MODE_READ) { + g_UMDReplacePermit = false; } if (s > 2) { Do(p, umdInsertChangeEvent); diff --git a/Core/HLE/sceUsbCam.cpp b/Core/HLE/sceUsbCam.cpp index 1a6a6cec14..608a0edf60 100644 --- a/Core/HLE/sceUsbCam.cpp +++ b/Core/HLE/sceUsbCam.cpp @@ -58,12 +58,20 @@ void __UsbCamInit() { } void __UsbCamDoState(PointerWrap &p) { + const bool wasCapturing = config->mode == Camera::Mode::Video; auto s = p.Section("sceUsbCam", 0, 1); if (!s) { + if (p.mode == p.MODE_READ) { + // Older states didn't save the camera, so leave it off. + if (wasCapturing) { + Camera::stopCapture(); + } + config->mode = Camera::Mode::Unused; + config->type = Camera::ConfigType::CfNone; + } return; } - const bool wasCapturing = config->mode == Camera::Mode::Video; Do(p, *config); if (p.mode == p.MODE_READ) { if (config->mode == Camera::Mode::Video) { // stillImage? TBD diff --git a/Core/HLE/sceUsbGps.cpp b/Core/HLE/sceUsbGps.cpp index 5291ce8cdd..f97d430e16 100644 --- a/Core/HLE/sceUsbGps.cpp +++ b/Core/HLE/sceUsbGps.cpp @@ -45,11 +45,19 @@ void __UsbGpsInit() { } void __UsbGpsDoState(PointerWrap &p) { - auto s = p.Section("sceUsbGps", 0, 1); - if (!s) - return; - const bool wasOn = gpsStatus == GPS_STATE_ON; + auto s = p.Section("sceUsbGps", 0, 1); + if (!s) { + // Older states didn't save it, so leave it off. + if (p.mode == p.MODE_READ) { + if (wasOn) { + System_GPSCommand("close"); + } + gpsStatus = GPS_STATE_OFF; + } + return; + } + Do(p, gpsStatus); if (p.mode == p.MODE_READ) { if (gpsStatus == GPS_STATE_ON) { diff --git a/Core/HLE/sceUsbMic.cpp b/Core/HLE/sceUsbMic.cpp index da58ec9185..33c838aee9 100644 --- a/Core/HLE/sceUsbMic.cpp +++ b/Core/HLE/sceUsbMic.cpp @@ -126,6 +126,14 @@ void __UsbMicDoState(PointerWrap &p) { eventMicBlockingResume = -1; CoreTiming::RestoreRegisterEvent(eventMicBlockingResume, "MicBlockingResume", &__MicBlockingResume); waitingThreads.clear(); + // Nor was the mic, so leave it off. + if (Microphone::isMicStarted()) { + Microphone::stopMic(); + } + numNeedSamples = 0; + curTargetAddr = 0; + readMicDataLength = 0; + micState = 0; } return; } @@ -146,6 +154,10 @@ void __UsbMicDoState(PointerWrap &p) { if (s > 2) { Do(p, curTargetAddr); Do(p, readMicDataLength); + } else if (p.mode == p.MODE_READ) { + // The host mic thread writes to curTargetAddr, so don't leave the one from before the load. + curTargetAddr = 0; + readMicDataLength = 0; } if (!audioBuf && numNeedSamples > 0) { audioBuf = new QueueBuf(numNeedSamples << 1); diff --git a/Core/HLE/sceVideocodec.cpp b/Core/HLE/sceVideocodec.cpp index 329e01c5bc..a808925f26 100644 --- a/Core/HLE/sceVideocodec.cpp +++ b/Core/HLE/sceVideocodec.cpp @@ -194,6 +194,14 @@ void __VideocodecDoState(PointerWrap &p) { auto s = p.Section("sceVideocodec", 0, 1); if (!s) { + if (p.mode == p.MODE_READ) { + // A state from before this module. Don't keep the contexts and ME memory of the session + // before the load. + ClearContexts(false); + g_meRam.clear(); + g_meRam.shrink_to_fit(); + g_meAlloc.Shutdown(); + } return; } diff --git a/Core/HW/BufferQueue.cpp b/Core/HW/BufferQueue.cpp index 19dc2abd00..c0270ccb1f 100644 --- a/Core/HW/BufferQueue.cpp +++ b/Core/HW/BufferQueue.cpp @@ -22,9 +22,27 @@ void BufferQueue::DoState(PointerWrap &p) { auto s = p.Section("BufferQueue", 0, 2); + const int allocatedSize = bufQueueSize; Do(p, bufQueueSize); Do(p, start); Do(p, end); + if (p.mode == PointerWrap::MODE_READ && bufQueue) { + // Normally the owner allocated the same size, but don't write past the buffer if not. + const int size = bufQueueSize; + if (size <= 0 || start < 0 || end < 0 || start > size || end > size || !p.CheckRead(size)) { + bufQueueSize = allocatedSize; + clear(); + p.SetError(PointerWrap::ERROR_FAILURE); + return; + } + if (size != allocatedSize) { + const int savedStart = start; + const int savedEnd = end; + alloc(size); + start = savedStart; + end = savedEnd; + } + } if (bufQueue) { DoArray(p, bufQueue, bufQueueSize); } diff --git a/Core/MIPS/JitCommon/JitCommon.cpp b/Core/MIPS/JitCommon/JitCommon.cpp index 707fb74984..e9f3811c96 100644 --- a/Core/MIPS/JitCommon/JitCommon.cpp +++ b/Core/MIPS/JitCommon/JitCommon.cpp @@ -70,7 +70,9 @@ namespace MIPSComp { if (!s) return; - bool dummy = false; + // This is startDefaultPrefix. Writing false made a JIT loading the state assume an uneaten + // prefix for the rest of the session. + bool dummy = currentMIPS->HasDefaultPrefix(); Do(p, dummy); if (s >= 2) { dummy = true;