Utility: Release volatile memory on a forced shutdown, fix dialog savestates

- The Yugioh savedata workaround force-stopped the other dialog without
  releasing the volatile memory it held, so the savedata helper then waited
  for it forever.
- Screenshot's ShutdownStart and Update succeeded when no screenshot was
  running, putting it back into SHUTDOWN.
- Savestates: Netconf didn't keep its request address, and a DNS json
  download going on was gone after a load, so it could wait for it forever;
  it now fetches the json again (it's cached). NpSignin didn't keep its
  request address either. Both restart their timeouts instead of timing out
  at once. With states from before, they keep the current address as they
  used to.
- Loading a state from before NpSignin, GameSharing or HtmlViewer were saved
  resets them rather than keeping this session's state (including the
  HtmlViewer's memory block), and without Shutdown's side effects, which
  would write to the loaded memory and release its volatile lock.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Henrik RydgårdandClaude Opus 5.5 committed 2026-09-25 12:50:41 -06:00
1 parent 6dffcc91d2
commit 41b5074a98
9 files changed
+68 -6

No files matched your search

+9
View File
@@ -273,6 +273,15 @@ u32 PSPDialog::CalcFadedColor(u32 inColor) const {
return (inColor & 0x00FFFFFF) | (alpha << 24);
}
void PSPDialog::ResetState() {
status = SCE_UTILITY_STATUS_NONE;
pendingStatus = SCE_UTILITY_STATUS_NONE;
pendingStatusTicks = 0;
volatileLocked_ = false;
isFading = false;
fadeValue = 0;
}
void PSPDialog::DoState(PointerWrap &p) {
auto s = p.Section("PSPDialog", 1, 3);
if (!s)
+3
View File
@@ -51,6 +51,9 @@ public:
virtual int Update(int animSpeed) = 0;
virtual int Shutdown(bool force = false);
virtual void DoState(PointerWrap &p);
// For loading a state that doesn't have this dialog: back to NONE, without anything Shutdown does
// on the way (writing results, releasing volatile memory), which would hit the loaded state.
virtual void ResetState();
virtual pspUtilityDialogCommon *GetCommonParam() {
// This is returned properly by the derived classes (or should be...).
return nullptr;
+8
View File
@@ -191,6 +191,14 @@ int PSPHtmlViewerDialog::Shutdown(bool force) {
return 0;
}
void PSPHtmlViewerDialog::ResetState() {
PSPDialog::ResetState();
// The block belonged to this session's memory, not the loaded one.
workMemory_ = 0;
started_ = false;
params_ = 0;
}
void PSPHtmlViewerDialog::DoState(PointerWrap &p) {
PSPDialog::DoState(p);
+1
View File
@@ -35,6 +35,7 @@ public:
int Update(int animSpeed) override;
int Shutdown(bool force = false) override;
void DoState(PointerWrap &p) override;
void ResetState() override;
pspUtilityDialogCommon *GetCommonParam() override;
// Until one has started, all the HtmlViewer calls return WRONG_TYPE.
+17 -3
View File
@@ -344,7 +344,8 @@ int PSPNetconfDialog::Update(int animSpeed) {
EndDraw();
}
if (ReadStatus() == SCE_UTILITY_STATUS_FINISHED || pendingStatus == SCE_UTILITY_STATUS_FINISHED)
const bool finished = ReadStatus() == SCE_UTILITY_STATUS_FINISHED || pendingStatus == SCE_UTILITY_STATUS_FINISHED;
if (finished && Memory::IsValidAddress(requestAddr))
Memory::Memcpy(requestAddr, &request, std::min((u32)request.common.size, (u32)sizeof(request)), "NetConfDialogParam");
return 0;
@@ -365,7 +366,7 @@ int PSPNetconfDialog::Shutdown(bool force) {
void PSPNetconfDialog::DoState(PointerWrap &p) {
PSPDialog::DoState(p);
auto s = p.Section("PSPNetconfigDialog", 0, 2);
auto s = p.Section("PSPNetconfigDialog", 0, 3);
if (!s)
return;
@@ -380,9 +381,22 @@ void PSPNetconfDialog::DoState(PointerWrap &p) {
scanStep = 0;
connResult = -1;
}
if (s >= 3) {
Do(p, requestAddr);
Do(p, showNoWlanNotice_);
} else if (p.mode == p.MODE_READ) {
// requestAddr is kept: most likely the same address in the same game.
showNoWlanNotice_ = !g_Config.bEnableWlan;
}
if (p.mode == p.MODE_READ) {
startTime = 0;
// The connect timeout starts over. The DNS config the json gave isn't in the state, so get
// it again (it's cached).
startTime = (u64)(time_now_d() * 1000000.0);
jsonReady_ = false;
if (ReadStatus() != SCE_UTILITY_STATUS_NONE) {
StartInfraJsonDownload();
}
}
}
+6 -2
View File
@@ -185,16 +185,20 @@ int PSPNpSigninDialog::Shutdown(bool force) {
void PSPNpSigninDialog::DoState(PointerWrap &p) {
PSPDialog::DoState(p);
auto s = p.Section("PSPNpSigninDialog", 1, 1);
auto s = p.Section("PSPNpSigninDialog", 1, 2);
if (!s)
return;
Do(p, request);
Do(p, step);
//Do(p, npSigninResult);
// Older states keep this session's requestAddr: most likely the same address in the same game.
if (s >= 2) {
Do(p, requestAddr);
}
if (p.mode == p.MODE_READ) {
startTime = 0;
startTime = (u64)(time_now_d() * 1000000.0);
}
}
+11
View File
@@ -81,6 +81,9 @@ int PSPScreenshotDialog::Init(u32 paramAddr) {
}
int PSPScreenshotDialog::Update(int animSpeed) {
if (ReadStatus() == SCE_UTILITY_STATUS_NONE) {
return SCE_ERROR_UTILITY_INVALID_STATUS;
}
UpdateCommon();
if (UseAutoStatus()) {
if (ReadStatus() == SCE_UTILITY_STATUS_INITIALIZE) {
@@ -98,6 +101,14 @@ int PSPScreenshotDialog::Update(int animSpeed) {
return 0;
}
int PSPScreenshotDialog::Shutdown(bool force) {
// TODO: Only from FINISHED, like the other dialogs? Update moves on to SHUTDOWN by itself here.
if (!force && ReadStatus() == SCE_UTILITY_STATUS_NONE) {
return SCE_ERROR_UTILITY_INVALID_STATUS;
}
return PSPDialog::Shutdown(force);
}
int PSPScreenshotDialog::ContStart() {
// Based on JPCSP http://code.google.com/p/jpcsp/source/detail?r=3381
if (ReadStatus() != SCE_UTILITY_STATUS_SCREENSHOT_UNKNOWN)
+1
View File
@@ -29,6 +29,7 @@ public:
int Init(u32 paramAddr);
int Update(int animSpeed) override;
int Shutdown(bool force = false) override;
int ContStart();
void DoState(PointerWrap &p) override;
pspUtilityDialogCommon *GetCommonParam() override;
+12 -1
View File
@@ -609,13 +609,21 @@ void __UtilityDoState(PointerWrap &p) {
lastSaveStateVersion = -1;
} else {
lastSaveStateVersion = s.Version();
if (p.mode == PointerWrap::MODE_READ) {
npSigninDialog->ResetState();
}
}
// Dialogs an older state doesn't have mustn't keep this session's state.
if (s >= 8) {
gameSharingDialog->DoState(p);
} else if (p.mode == PointerWrap::MODE_READ) {
gameSharingDialog->ResetState();
}
if (s >= 9) {
htmlViewerDialog->DoState(p);
} else if (p.mode == PointerWrap::MODE_READ) {
htmlViewerDialog->ResetState();
}
if (!hasAccessThread && accessThread) {
@@ -790,6 +798,9 @@ static int UtilityFinishDialog(int type) {
static int sceUtilitySavedataInitStart(u32 paramAddr) {
if (UtilityDialogBusy() && currentDialogType != UtilityDialogType::SAVEDATA && PSP_CoreParameter().compat.flags().YugiohSaveFix) {
WARN_LOG_REPORT(Log::sceUtility, "Yugioh Savedata Correction (state=%d)", lastSaveStateVersion);
PSPDialog *other = CurrentDialog(currentDialogType);
// Its own lock first, so the unlock below only takes one its thread held.
other->FinishVolatile();
if (accessThread) {
accessThread->Terminate();
delete accessThread;
@@ -799,7 +810,7 @@ static int sceUtilitySavedataInitStart(u32 paramAddr) {
// Try to unlock in case other dialog was shutting down.
KernelVolatileMemUnlock(0);
}
CurrentDialog(currentDialogType)->Shutdown(true);
other->Shutdown(true);
}
return hleLogDebug(Log::sceUtility, UtilityInitStart(UtilityDialogType::SAVEDATA, saveDialog, paramAddr));