diff --git a/Core/Dialog/PSPDialog.cpp b/Core/Dialog/PSPDialog.cpp index 47b68c1018..069e452bda 100644 --- a/Core/Dialog/PSPDialog.cpp +++ b/Core/Dialog/PSPDialog.cpp @@ -41,15 +41,10 @@ PSPDialog::DialogStatus PSPDialog::GetStatus() { if (pendingStatusTicks != 0 && CoreTiming::GetTicks() >= pendingStatusTicks) { bool changeAllowed = true; if (pendingStatus == SCE_UTILITY_STATUS_NONE && status == SCE_UTILITY_STATUS_SHUTDOWN) { - if (volatileLocked_) { - FinishVolatile(); - UtilityCancelVolatileUnlock(); - volatileLocked_ = false; - } + FinishVolatile(); } else if (pendingStatus == SCE_UTILITY_STATUS_RUNNING && status == SCE_UTILITY_STATUS_INITIALIZE) { if (!volatileLocked_) { volatileLocked_ = KernelVolatileMemLock(0, 0, 0) == 0; - UtilityCancelVolatileUnlock(); changeAllowed = volatileLocked_; } } @@ -72,16 +67,11 @@ PSPDialog::DialogStatus PSPDialog::GetStatus() { void PSPDialog::ChangeStatus(DialogStatus newStatus, int delayUs) { if (delayUs <= 0) { if (newStatus == SCE_UTILITY_STATUS_NONE && status == SCE_UTILITY_STATUS_SHUTDOWN) { - if (volatileLocked_) { - FinishVolatile(); - UtilityCancelVolatileUnlock(); - volatileLocked_ = false; - } + FinishVolatile(); } else if (newStatus == SCE_UTILITY_STATUS_RUNNING && status == SCE_UTILITY_STATUS_INITIALIZE) { if (!volatileLocked_) { // TODO: Should probably make the status pending instead? volatileLocked_ = KernelVolatileMemLock(0, 0, 0) == 0; - UtilityCancelVolatileUnlock(); } } status = newStatus; @@ -89,20 +79,27 @@ void PSPDialog::ChangeStatus(DialogStatus newStatus, int delayUs) { } else { pendingStatus = newStatus; pendingStatusTicks = CoreTiming::GetTicks() + usToCycles(delayUs); - if (volatileLocked_ && newStatus == SCE_UTILITY_STATUS_NONE && status == SCE_UTILITY_STATUS_SHUTDOWN) { - UtilityScheduleVolatileUnlock(usToCycles(delayUs)); - } } } void PSPDialog::FinishVolatile() { - if (volatileLocked_ && KernelVolatileMemUnlock(0) == 0) { + if (!volatileLocked_) + return; + + if (KernelVolatileMemUnlock(0) == 0) { volatileLocked_ = false; // Simulate modifications to volatile memory. Memory::Memset(PSP_GetVolatileMemoryStart(), 0, PSP_GetVolatileMemoryEnd() - PSP_GetVolatileMemoryStart()); } } +int PSPDialog::FinishShutdown() { + if (ReadStatus() != SCE_UTILITY_STATUS_SHUTDOWN) + return -1; + ChangeStatus(SCE_UTILITY_STATUS_NONE, 0); + return 0; +} + void PSPDialog::ChangeStatusInit(int delayUs) { status = SCE_UTILITY_STATUS_INITIALIZE; ChangeStatus(SCE_UTILITY_STATUS_RUNNING, delayUs); @@ -110,7 +107,11 @@ void PSPDialog::ChangeStatusInit(int delayUs) { void PSPDialog::ChangeStatusShutdown(int delayUs) { status = SCE_UTILITY_STATUS_SHUTDOWN; - ChangeStatus(SCE_UTILITY_STATUS_NONE, delayUs); + auto params = GetCommonParam(); + if (params) + UtilityDialogShutdown(DialogType(), delayUs, params->accessThread); + else + ChangeStatus(SCE_UTILITY_STATUS_NONE, delayUs); } void PSPDialog::StartDraw() diff --git a/Core/Dialog/PSPDialog.h b/Core/Dialog/PSPDialog.h index a6208d9ab3..c7ca35d153 100644 --- a/Core/Dialog/PSPDialog.h +++ b/Core/Dialog/PSPDialog.h @@ -86,6 +86,7 @@ public: void EndDraw(); void FinishVolatile(); + int FinishShutdown(); protected: PPGeStyle FadedStyle(PPGeAlign align, float scale); diff --git a/Core/HLE/sceUtility.cpp b/Core/HLE/sceUtility.cpp index 5886ec1d5c..40f48848cd 100644 --- a/Core/HLE/sceUtility.cpp +++ b/Core/HLE/sceUtility.cpp @@ -27,8 +27,10 @@ #include "Core/Config.h" #include "Core/CoreTiming.h" #include "Core/HLE/HLE.h" +#include "Core/HLE/HLEHelperThread.h" #include "Core/HLE/FunctionWrappers.h" #include "Core/MIPS/MIPS.h" +#include "Core/MIPS/MIPSCodeUtils.h" #include "Core/Reporting.h" #include "Core/System.h" @@ -142,18 +144,28 @@ static PSPGamedataInstallDialog gamedataInstallDialog(UTILITY_DIALOG_GAMEDATAINS static int oldStatus = 100; //random value static std::map currentlyLoadedModules; static int volatileUnlockEvent = -1; +static HLEHelperThread *accessThread = nullptr; + +static void CleanupDialogThreads() { + if (accessThread && accessThread->Stopped()) { + delete accessThread; + accessThread = nullptr; + } +} static void ActivateDialog(UtilityDialogType type) { if (!currentDialogActive) { currentDialogType = type; currentDialogActive = true; } + CleanupDialogThreads(); } static void DeactivateDialog() { if (currentDialogActive) { currentDialogActive = false; } + CleanupDialogThreads(); } static void UtilityVolatileUnlock(u64 userdata, int cyclesLate) { @@ -175,7 +187,7 @@ void __UtilityInit() { } void __UtilityDoState(PointerWrap &p) { - auto s = p.Section("sceUtility", 1, 3); + auto s = p.Section("sceUtility", 1, 4); if (!s) { return; } @@ -205,6 +217,22 @@ void __UtilityDoState(PointerWrap &p) { volatileUnlockEvent = -1; } CoreTiming::RestoreRegisterEvent(volatileUnlockEvent, "UtilityVolatileUnlock", UtilityVolatileUnlock); + + bool hasAccessThread = accessThread != nullptr; + if (s >= 4) { + Do(p, hasAccessThread); + if (hasAccessThread) { + Do(p, accessThread); + } + } else { + hasAccessThread = false; + } + + if (!hasAccessThread && accessThread) { + accessThread->Forget(); + delete accessThread; + accessThread = nullptr; + } } void __UtilityShutdown() { @@ -214,14 +242,67 @@ void __UtilityShutdown() { netDialog.Shutdown(true); screenshotDialog.Shutdown(true); gamedataInstallDialog.Shutdown(true); + + if (accessThread) { + delete accessThread; + accessThread = nullptr; + } } -void UtilityScheduleVolatileUnlock(s64 cyclesIntoFuture) { - CoreTiming::ScheduleEvent(cyclesIntoFuture, volatileUnlockEvent, 0); +void UtilityDialogShutdown(int type, int delayUs, int priority) { + // Break it up so better-priority rescheduling happens. + // The windows aren't this regular, but close. + int partDelay = delayUs / 4; + const u32_le insts[] = { + // Make sure we don't discard/deadbeef 'em. + (u32_le)MIPS_MAKE_ORI(MIPS_REG_S0, MIPS_REG_A0, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"), + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"), + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"), + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_S0, 0), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityWorkUs"), + + (u32_le)MIPS_MAKE_ORI(MIPS_REG_A0, MIPS_REG_ZERO, type), + (u32_le)MIPS_MAKE_JR_RA(), + (u32_le)MIPS_MAKE_SYSCALL("sceUtility", "__UtilityFinishDialog"), + }; + + CleanupDialogThreads(); + _assert_(accessThread == nullptr); + accessThread = new HLEHelperThread("ScePafJob", insts, (uint32_t)ARRAY_SIZE(insts), priority, 0x200); + accessThread->Start(partDelay, 0); } -void UtilityCancelVolatileUnlock() { - CoreTiming::UnscheduleEvent(volatileUnlockEvent, 0); +static int UtilityWorkUs(int us) { + // This blocks, but other better priority threads can get time. + // Simulate this by allowing a reschedule. + hleEatMicro(us); + hleReSchedule("utility work"); + return 0; +} + +static int UtilityFinishDialog(int type) { + switch (type) { + case UTILITY_DIALOG_NONE: + break; + case UTILITY_DIALOG_SAVEDATA: + return hleLogSuccessI(SCEUTILITY, saveDialog.FinishShutdown()); + case UTILITY_DIALOG_MSG: + return hleLogSuccessI(SCEUTILITY, msgDialog.FinishShutdown()); + case UTILITY_DIALOG_OSK: + return hleLogSuccessI(SCEUTILITY, oskDialog.FinishShutdown()); + case UTILITY_DIALOG_NET: + return hleLogSuccessI(SCEUTILITY, netDialog.FinishShutdown()); + case UTILITY_DIALOG_SCREENSHOT: + return hleLogSuccessI(SCEUTILITY, screenshotDialog.FinishShutdown()); + case UTILITY_DIALOG_GAMESHARING: + return hleLogError(SCEUTILITY, -1, "unimplemented"); + case UTILITY_DIALOG_GAMEDATAINSTALL: + return hleLogSuccessI(SCEUTILITY, gamedataInstallDialog.FinishShutdown()); + } + return hleLogError(SCEUTILITY, 0, "invalid dialog type?"); } static int sceUtilitySavedataInitStart(u32 paramAddr) @@ -251,7 +332,7 @@ static int sceUtilitySavedataShutdownStart() { DeactivateDialog(); int ret = saveDialog.Shutdown(); - hleEatCycles(90000); + hleEatCycles(30000); return hleLogSuccessX(SCEUTILITY, ret); } @@ -270,6 +351,7 @@ static int sceUtilitySavedataGetStatus() DEBUG_LOG(SCEUTILITY, "%08x=sceUtilitySavedataGetStatus()", status); } hleEatCycles(200); + CleanupDialogThreads(); return status; } @@ -429,6 +511,7 @@ static int sceUtilityMsgDialogGetStatus() oldStatus = status; DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityMsgDialogGetStatus()", status); } + CleanupDialogThreads(); return status; } @@ -502,6 +585,7 @@ static int sceUtilityOskGetStatus() oldStatus = status; DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityOskGetStatus()", status); } + CleanupDialogThreads(); return status; } @@ -544,6 +628,7 @@ static int sceUtilityNetconfGetStatus() { oldStatus = status; return hleLogSuccessI(SCEUTILITY, status); } + CleanupDialogThreads(); return hleLogSuccessVerboseI(SCEUTILITY, status); } @@ -613,6 +698,7 @@ static int sceUtilityScreenshotGetStatus() oldStatus = status; WARN_LOG(SCEUTILITY, "%08x=sceUtilityScreenshotGetStatus()", status); } + CleanupDialogThreads(); return status; } @@ -678,6 +764,7 @@ static int sceUtilityGamedataInstallGetStatus() int status = gamedataInstallDialog.GetStatus(); DEBUG_LOG(SCEUTILITY, "%08x=sceUtilityGamedataInstallGetStatus()", status); + CleanupDialogThreads(); return status; } @@ -893,6 +980,7 @@ static int sceUtilityGameSharingGetStatus() } ERROR_LOG(SCEUTILITY, "UNIMPL sceUtilityGameSharingGetStatus()"); + CleanupDialogThreads(); return 0; } @@ -1055,6 +1143,10 @@ const HLEFunction sceUtility[] = {0X70267ADF, nullptr, "sceUtility_70267ADF", '?', "" }, {0XECE1D3E5, nullptr, "sceUtility_ECE1D3E5", '?', "" }, {0XEF3582B2, nullptr, "sceUtility_EF3582B2", '?', "" }, + + // Fake functions for PPSSPP's use. + {0xC0DE0001, &WrapI_I, "__UtilityFinishDialog", 'i', "i" }, + {0xC0DE0002, &WrapI_I, "__UtilityWorkUs", 'i', "i" }, }; void Register_sceUtility() diff --git a/Core/HLE/sceUtility.h b/Core/HLE/sceUtility.h index 593d45d8fd..93b30cbc71 100644 --- a/Core/HLE/sceUtility.h +++ b/Core/HLE/sceUtility.h @@ -83,7 +83,6 @@ void __UtilityInit(); void __UtilityDoState(PointerWrap &p); void __UtilityShutdown(); -void UtilityScheduleVolatileUnlock(s64 cyclesIntoFuture); -void UtilityCancelVolatileUnlock(); +void UtilityDialogShutdown(int type, int delayUs, int priority); void Register_sceUtility();