mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Merge pull request #22370 from hrydgard/more-savestate-fixes
Claude code review: More savestate fixes
This commit is contained in:
27 files changed
+228
-26
No files matched your search
+6
-1
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+8
-2
@@ -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.
|
||||
|
||||
+5
-1
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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!
|
||||
}
|
||||
|
||||
@@ -471,6 +471,9 @@ struct SceKernelSMOption {
|
||||
static int actionAfterModule;
|
||||
|
||||
static std::set<SceUID> 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<PSPModule>(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
|
||||
|
||||
+6
-1
@@ -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);
|
||||
|
||||
+18
-1
@@ -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() {
|
||||
|
||||
+14
-3
@@ -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<std::recursive_mutex> 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<std::recursive_mutex> 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();
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
+15
-2
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
+12
-4
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user