Files
ppsspp/Core/Dialog/PSPNpSigninDialog.cpp
T
Henrik RydgårdandClaude Opus 5.5 41b5074a98 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>
2026-09-25 12:50:41 -06:00

209 lines
6.6 KiB
C++

// Copyright (c) 2012- PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
#include <algorithm>
#include "Common/TimeUtil.h"
#include "Common/Data/Text/I18n.h"
#include "Common/Serialize/Serializer.h"
#include "Common/Serialize/SerializeFuncs.h"
#include "Core/MemMapHelpers.h"
#include "Core/Util/PPGeDraw.h"
#include "Core/HLE/sceKernelMemory.h"
#include "Core/HLE/sceCtrl.h"
#include "Core/HLE/sceUtility.h"
#include "Core/HLE/sceNp.h"
#include "Core/HLE/HLEUtil.h"
#include "Core/HLE/HLE.h"
#include "Core/HLE/ErrorCodes.h"
#include "Core/Dialog/PSPNpSigninDialog.h"
#include "Common/Data/Encoding/Utf8.h"
#include "Core/Reporting.h"
// Needs testing.
const static int NP_INIT_DELAY_US = 200000;
const static int NP_SHUTDOWN_DELAY_US = 501000;
const static int NP_RUNNING_DELAY_US = 1000000; // faked delay to simulate signin process to give chance for players to read the text on the dialog
int PSPNpSigninDialog::Init(u32 paramAddr) {
// Already running
if (ReadStatus() != SCE_UTILITY_STATUS_NONE)
return SCE_ERROR_UTILITY_INVALID_STATUS;
const int check = CheckRequest(paramAddr, { 0x40 });
if (check < 0) {
return check;
}
if (!ReadVariableSizedStruct(paramAddr, &request)) {
return SCE_KERNEL_ERROR_BAD_ARGUMENT; // untested, it's misaligned
}
requestAddr = paramAddr;
WARN_LOG_REPORT_ONCE(PSPNpSigninDialogInit, Log::sceNet, "NpSignin Init Params: %08x, %08x, %08x, %08x", request.npSigninStatus, request.unknown1, request.unknown2, request.unknown3);
ChangeStatusInit(NP_INIT_DELAY_US);
// Eat any keys pressed before the dialog inited.
UpdateButtons();
InitCommon();
//npSigninResult = -1;
startTime = (u64)(time_now_d() * 1000000.0);
step = 0;
StartFade(true);
return 0;
}
void PSPNpSigninDialog::DrawBanner() {
PPGeDrawRect(0, 0, 480, 22, CalcFadedColor(0x65636358));
PPGeStyle textStyle = FadedStyle(PPGeAlign::BOX_VCENTER, 0.6f);
textStyle.hasShadow = false;
// TODO: Draw a hexagon icon
PPGeDrawImage(10, 5, 11.0f, 10.0f, 1, 10, 1, 10, 10, 10, FadedImageStyle());
auto di = GetI18NCategory(I18NCat::DIALOG);
PPGeDrawText(di->T("Sign In"), 31, 10, textStyle);
}
void PSPNpSigninDialog::DrawIndicator() {
// TODO: Draw animated circle as processing indicator
PPGeDrawImage(456, 248, 20.0f, 20.0f, 1, 10, 1, 10, 10, 10, FadedImageStyle());
}
void PSPNpSigninDialog::DrawLogo() {
// TODO: Draw OpenDNAS logo
PPGeDrawImage(416, 22, 64.0f, 64.0f, 1, 10, 1, 10, 64, 64, FadedImageStyle());
}
int PSPNpSigninDialog::Update(int animSpeed) {
if (ReadStatus() != SCE_UTILITY_STATUS_RUNNING) {
return SCE_ERROR_UTILITY_INVALID_STATUS;
}
UpdateButtons();
UpdateCommon();
auto err = GetI18NCategory(I18NCat::ERRORS);
u64 now = (u64)(time_now_d() * 1000000.0);
if (request.npSigninStatus == NP_SIGNIN_STATUS_NONE) {
auto di = GetI18NCategory(I18NCat::DIALOG);
UpdateFade(animSpeed);
StartDraw();
PPGeDrawRect(0, 0, 480, 272, CalcFadedColor(0xC0C8B2AC));
DrawBanner();
DrawIndicator();
// TODO: Not sure what should happen here.. may be something like this https://pastebin.com/1eW48zBb ? but we can do test on Open DNAS Server later https://dnas.hashsploit.net/us-gw/
// DNAS dialog
if (step >= 2 && now - startTime > NP_RUNNING_DELAY_US) {
DrawLogo();
DisplayMessage2(di->T("PleaseWait", "Please wait..."));
step++;
}
// Signin dialog
else {
// Skipping the Select Connection screen since we only have 1 fake profile
DisplayMessage2(di->T("SigninPleaseWait", "Signing in...\nPlease wait."));
}
DisplayButtons(DS_BUTTON_CANCEL, di->T("Cancel"));
if (step >= 2 && now - startTime > NP_RUNNING_DELAY_US*2) {
if (pendingStatus != SCE_UTILITY_STATUS_FINISHED) {
StartFade(false);
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NP_SHUTDOWN_DELAY_US);
step++;
}
}
else if (step == 1 && now - startTime > NP_RUNNING_DELAY_US) {
// Switch to the next message (with DNAS logo)
StartFade(true);
step++;
}
else if (step == 0) {
/*if (npAuthResult < 0 && request.NpSigninData.IsValid()) {
npAuthResult = sceNpAuthCreateStartRequest(request.NpSigninData->paramAddr);
}*/
step++;
}
if (/*npAuthResult >= 0 &&*/ IsButtonPressed(cancelButtonFlag)) {
StartFade(false);
//sceNpAuthAbortRequest(npAuthResult);
//sceNpAuthDestroyRequest(npAuthResult);
ChangeStatus(SCE_UTILITY_STATUS_FINISHED, NP_SHUTDOWN_DELAY_US);
request.common.result = SCE_UTILITY_DIALOG_RESULT_ABORT;
request.npSigninStatus = NP_SIGNIN_STATUS_CANCELED;
//step = 0;
}
EndDraw();
}
if (ReadStatus() == SCE_UTILITY_STATUS_FINISHED || pendingStatus == SCE_UTILITY_STATUS_FINISHED) {
npSigninState = NP_SIGNIN_STATUS_SUCCESS;
__RtcTimeOfDay(&npSigninTimestamp);
request.npSigninStatus = npSigninState;
}
return 0;
}
int PSPNpSigninDialog::Shutdown(bool force) {
if (ReadStatus() != SCE_UTILITY_STATUS_FINISHED && !force)
return SCE_ERROR_UTILITY_INVALID_STATUS;
PSPDialog::Shutdown(force);
if (!force) {
ChangeStatusShutdown(NP_SHUTDOWN_DELAY_US);
}
// FIXME: This should probably be done within FinishShutdown to prevent some games (ie. UNO) from progressing further while the Dialog is still being faded-out, since we can't override non-virtual method... so here is the closes one to FinishShutdown.
if (Memory::IsValidAddress(requestAddr)) // Need to validate first to prevent Invalid address when the game is being Shutdown/Exited to menu
Memory::Memcpy(requestAddr, &request, std::min((u32)request.common.size, (u32)sizeof(request)), "NpSigninDialogParam");
return 0;
}
void PSPNpSigninDialog::DoState(PointerWrap &p) {
PSPDialog::DoState(p);
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 = (u64)(time_now_d() * 1000000.0);
}
}
pspUtilityDialogCommon* PSPNpSigninDialog::GetCommonParam()
{
return &request.common;
}