mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-10-01 14:58:14 +00:00
Savestate: Save state that was missing from several modules
sceMpeg's AVC resource flag, whether the VSH is running, sceReg's handle counter, sceNet's pending apctl events and product code block, and the save dialog's copy of the original request (without which the first Update after a load reloaded the request and lost its results). Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
1 parent
52299a5e17
commit
8663ce2a33
5 files changed
+79
-13
No files matched your search
@@ -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() {
|
||||
|
||||
@@ -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
|
||||
|
||||
+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();
|
||||
}
|
||||
|
||||
+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)
|
||||
|
||||
Reference in new issue
Block a user