From 14815d93302f9473a1d95e814b2f9af4c5defce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 29 Nov 2024 14:03:46 +0100 Subject: [PATCH 1/7] Fix wrong homebrew name generation --- Core/PSPLoaders.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/Core/PSPLoaders.cpp b/Core/PSPLoaders.cpp index 57c8969d89c3..77f3cbfa42b2 100644 --- a/Core/PSPLoaders.cpp +++ b/Core/PSPLoaders.cpp @@ -441,13 +441,11 @@ bool Load_PSP_ELF_PBP(FileLoader *fileLoader, std::string *error_string) { std::string homebrewName = PSP_CoreParameter().fileToStart.ToVisualString(); std::size_t lslash = homebrewName.find_last_of('/'); -#if PPSSPP_PLATFORM(UWP) - if (lslash == homebrewName.npos) { - lslash = homebrewName.find_last_of("\\"); - } -#endif + std::size_t rslash = homebrewName.find_last_of('\\'); if (lslash != homebrewName.npos) homebrewName = homebrewName.substr(lslash + 1); + if (rslash != homebrewName.npos) + homebrewName = homebrewName.substr(rslash + 1); std::string homebrewTitle = g_paramSFO.GetValueString("TITLE"); if (homebrewTitle.empty()) homebrewTitle = homebrewName; From b6f648eecce40b33fbb09238df771c6ff2420a9d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 29 Nov 2024 14:13:41 +0100 Subject: [PATCH 2/7] MetaFileSystem: Clean up unused function --- Common/File/DiskFree.h | 3 ++- Core/FileSystems/MetaFileSystem.cpp | 6 ++++++ Core/FileSystems/MetaFileSystem.h | 11 +---------- 3 files changed, 9 insertions(+), 11 deletions(-) diff --git a/Common/File/DiskFree.h b/Common/File/DiskFree.h index a5a5fb3c6831..97cc8b652b07 100644 --- a/Common/File/DiskFree.h +++ b/Common/File/DiskFree.h @@ -5,5 +5,6 @@ #include "Common/File/Path.h" // If this fails, false is returned and space is negative. -// Try to avoid calling this from the main thread, if possible. Can be SLOW. +// Try to avoid calling this from the main thread, if possible. Not super fast, +// but is also not allowed to do things like scan the entire disk. bool free_disk_space(const Path &path, int64_t &space); diff --git a/Core/FileSystems/MetaFileSystem.cpp b/Core/FileSystems/MetaFileSystem.cpp index 1ab0e2cb9483..03e211392ecb 100644 --- a/Core/FileSystems/MetaFileSystem.cpp +++ b/Core/FileSystems/MetaFileSystem.cpp @@ -682,3 +682,9 @@ int64_t MetaFileSystem::ComputeRecursiveDirectorySize(const std::string &filenam return false; } } + +bool MetaFileSystem::ComputeRecursiveDirSizeIfFast(const std::string &path, int64_t *size) { + // Shouldn't be called. Can't recurse MetaFileSystem. + _dbg_assert_(false); + return false; +} diff --git a/Core/FileSystems/MetaFileSystem.h b/Core/FileSystems/MetaFileSystem.h index 3736e91204bb..41390e8ac217 100644 --- a/Core/FileSystems/MetaFileSystem.h +++ b/Core/FileSystems/MetaFileSystem.h @@ -144,16 +144,7 @@ class MetaFileSystem : public IHandleAllocator, public IFileSystem { int64_t ComputeRecursiveDirectorySize(const std::string &dirPath); - // Shouldn't ever be called, but meh. - bool ComputeRecursiveDirSizeIfFast(const std::string &path, int64_t *size) override { - int64_t sizeTemp = ComputeRecursiveDirectorySize(path); - if (sizeTemp >= 0) { - *size = sizeTemp; - return true; - } else { - return false; - } - } + bool ComputeRecursiveDirSizeIfFast(const std::string &path, int64_t *size) override; void Describe(char *buf, size_t size) const override { snprintf(buf, size, "Meta"); } From 6d2826dcb0acbb55a678c6f562f2105aeef8fd59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 29 Nov 2024 14:14:32 +0100 Subject: [PATCH 3/7] Move the MemoryStick free space calculation to a task instead of a thread --- Core/HW/MemoryStick.cpp | 60 +++++++++++++++-------------------------- assets/compat.ini | 1 + 2 files changed, 23 insertions(+), 38 deletions(-) diff --git a/Core/HW/MemoryStick.cpp b/Core/HW/MemoryStick.cpp index 8d503f86949d..000e067289c8 100644 --- a/Core/HW/MemoryStick.cpp +++ b/Core/HW/MemoryStick.cpp @@ -29,12 +29,14 @@ #include "Core/FileSystems/MetaFileSystem.h" #include "Core/HW/MemoryStick.h" #include "Core/System.h" +#include "Common/CommonTypes.h" +#include "Common/Thread/Promise.h" // MS and FatMS states. static MemStickState memStickState; static MemStickFatState memStickFatState; static bool memStickNeedsAssign = false; -static u64 memStickInsertedAt = 0; +static uint64_t memStickInsertedAt = 0; static uint64_t memstickInitialFree = 0; static uint64_t memstickCurrentUse = 0; static bool memstickCurrentUseValid = false; @@ -46,13 +48,10 @@ enum FreeCalcStatus { CLEANED_UP, }; -static std::thread freeCalcThread; -static std::condition_variable freeCalcCond; -static std::mutex freeCalcMutex; -static FreeCalcStatus freeCalcStatus = FreeCalcStatus::NONE; +static const uint64_t normalMemstickSize = 9ULL * 1024 * 1024 * 1024; +static const uint64_t smallMemstickSize = 1ULL * 1024 * 1024 * 1024; -static const u64 normalMemstickSize = 9ULL * 1024 * 1024 * 1024; -static const u64 smallMemstickSize = 1ULL * 1024 * 1024 * 1024; +static Promise *g_initialMemstickSizePromise = nullptr; void MemoryStick_DoState(PointerWrap &p) { auto s = p.Section("MemoryStick", 1, 5); @@ -95,36 +94,9 @@ u64 MemoryStick_SectorSize() { return 32 * 1024; // 32KB } -static void MemoryStick_CalcInitialFree() { - std::unique_lock guard(freeCalcMutex); - freeCalcStatus = FreeCalcStatus::RUNNING; - freeCalcThread = std::thread([] { - SetCurrentThreadName("CalcInitialFree"); - - AndroidJNIThreadContext jniContext; - - memstickInitialFree = pspFileSystem.FreeDiskSpace("ms0:/") + pspFileSystem.ComputeRecursiveDirectorySize("ms0:/PSP/SAVEDATA/"); - - std::unique_lock guard(freeCalcMutex); - freeCalcStatus = FreeCalcStatus::DONE; - freeCalcCond.notify_all(); - }); -} - -static void MemoryStick_WaitInitialFree() { - std::unique_lock guard(freeCalcMutex); - while (freeCalcStatus == FreeCalcStatus::RUNNING) { - freeCalcCond.wait(guard); - } - if (freeCalcStatus == FreeCalcStatus::DONE) - freeCalcThread.join(); - freeCalcStatus = FreeCalcStatus::CLEANED_UP; -} - u64 MemoryStick_FreeSpace() { - NOTICE_LOG(Log::IO, "Calculated free disk space"); - - MemoryStick_WaitInitialFree(); + INFO_LOG(Log::IO, "Calculating free disk space"); + const u64 memstickInitialFree = g_initialMemstickSizePromise->BlockUntilReady(); const CompatFlags &flags = PSP_CoreParameter().compat.flags(); u64 realFreeSpace = pspFileSystem.FreeDiskSpace("ms0:/"); @@ -149,7 +121,9 @@ u64 MemoryStick_FreeSpace() { simulatedFreeSpace = smallMemstickSize / 2; // just pick a value. } if (flags.MemstickFixedFree) { + _dbg_assert_(g_initialMemstickSizePromise); // Assassin's Creed: Bloodlines fails to save if free space changes incorrectly during game. + // See issue #12761 realFreeSpace = 0; if (memstickCurrentUse <= memstickInitialFree) { realFreeSpace = memstickInitialFree - memstickCurrentUse; @@ -194,9 +168,19 @@ void MemoryStick_Init() { } memStickNeedsAssign = false; - MemoryStick_CalcInitialFree(); + + const CompatFlags &flags = PSP_CoreParameter().compat.flags(); + // See issue #12761 + g_initialMemstickSizePromise = Promise::Spawn(&g_threadManager, []() -> uint64_t { + INFO_LOG(Log::System, "Calculating initial savedata size..."); + return pspFileSystem.FreeDiskSpace("ms0:/") + pspFileSystem.ComputeRecursiveDirectorySize("ms0:/PSP/SAVEDATA/"); + }, TaskType::IO_BLOCKING); } void MemoryStick_Shutdown() { - MemoryStick_WaitInitialFree(); + if (g_initialMemstickSizePromise) { + g_initialMemstickSizePromise->BlockUntilReady(); + } + delete g_initialMemstickSizePromise; + g_initialMemstickSizePromise = nullptr; } diff --git a/assets/compat.ini b/assets/compat.ini index 3351dcb9081b..8a374753efb2 100644 --- a/assets/compat.ini +++ b/assets/compat.ini @@ -1135,6 +1135,7 @@ UCJS10039 = true UCJS18012 = true [MemstickFixedFree] +# Assassin's Creed : Bloodlines - issue #12761 ULJM05571 = true ULES01367 = true NPEH00029 = true From 7cbb60fd22f91320bee7f07ebf65cc10a38ef836 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 29 Nov 2024 14:43:13 +0100 Subject: [PATCH 4/7] Bypass the PSP file system when calculating the savedata size to avoid a lock --- Common/Thread/ThreadManager.cpp | 4 ++-- Core/HLE/sceIo.cpp | 4 ++-- Core/HW/MemoryStick.cpp | 19 +++++++++++++++---- Core/HW/MemoryStick.h | 4 +++- 4 files changed, 22 insertions(+), 9 deletions(-) diff --git a/Common/Thread/ThreadManager.cpp b/Common/Thread/ThreadManager.cpp index f1224a64a8d6..547f13f75f8c 100644 --- a/Common/Thread/ThreadManager.cpp +++ b/Common/Thread/ThreadManager.cpp @@ -130,10 +130,10 @@ bool ThreadManager::TeardownTask(Task *task, bool enqueue) { static void WorkerThreadFunc(GlobalThreadContext *global, TaskThreadContext *thread) { if (thread->type == TaskType::CPU_COMPUTE) { - snprintf(thread->name, sizeof(thread->name), "PoolWorker %d", thread->index); + snprintf(thread->name, sizeof(thread->name), "PoolW %d", thread->index); } else { _assert_(thread->type == TaskType::IO_BLOCKING); - snprintf(thread->name, sizeof(thread->name), "PoolWorkerIO %d", thread->index); + snprintf(thread->name, sizeof(thread->name), "PoolW IO %d", thread->index); } SetCurrentThreadName(thread->name); diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 292a3eba4a30..58b43b747fca 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -675,8 +675,8 @@ void __IoInit() { pspFileSystem.Mount("flash0:", flash0System); + const std::string gameId = g_paramSFO.GetDiscID(); if (g_RemasterMode) { - const std::string gameId = g_paramSFO.GetDiscID(); const Path exdataPath = GetSysDirectory(DIRECTORY_EXDATA) / gameId; if (File::Exists(exdataPath)) { auto exdataSystem = std::make_shared(&pspFileSystem, exdataPath, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD); @@ -698,7 +698,7 @@ void __IoInit() { __KernelRegisterWaitTypeFuncs(WAITTYPE_ASYNCIO, __IoAsyncBeginCallback, __IoAsyncEndCallback); - MemoryStick_Init(); + MemoryStick_Init(gameId); lastMemStickState = MemoryStick_State(); lastMemStickFatState = MemoryStick_FatState(); __DisplayListenVblank(__IoVblank); diff --git a/Core/HW/MemoryStick.cpp b/Core/HW/MemoryStick.cpp index 000e067289c8..fcfbc5b642b2 100644 --- a/Core/HW/MemoryStick.cpp +++ b/Core/HW/MemoryStick.cpp @@ -23,6 +23,8 @@ #include "Common/Serialize/Serializer.h" #include "Common/Serialize/SerializeFuncs.h" #include "Common/Thread/ThreadUtil.h" +#include "Common/File/DiskFree.h" +#include "Common/File/FileUtil.h" #include "Core/Config.h" #include "Core/CoreTiming.h" #include "Core/Compatibility.h" @@ -158,7 +160,7 @@ void MemoryStick_SetState(MemStickState state) { } } -void MemoryStick_Init() { +void MemoryStick_Init(std::string gameID) { if (g_Config.bMemStickInserted) { memStickState = PSP_MEMORYSTICK_STATE_INSERTED; memStickFatState = PSP_FAT_MEMORYSTICK_STATE_ASSIGNED; @@ -170,10 +172,19 @@ void MemoryStick_Init() { memStickNeedsAssign = false; const CompatFlags &flags = PSP_CoreParameter().compat.flags(); + //if (flags.MemstickFixedFree) { + // See issue #12761 - g_initialMemstickSizePromise = Promise::Spawn(&g_threadManager, []() -> uint64_t { - INFO_LOG(Log::System, "Calculating initial savedata size..."); - return pspFileSystem.FreeDiskSpace("ms0:/") + pspFileSystem.ComputeRecursiveDirectorySize("ms0:/PSP/SAVEDATA/"); + g_initialMemstickSizePromise = Promise::Spawn(&g_threadManager, [gameID]() -> uint64_t { + INFO_LOG(Log::System, "Calculating initial savedata size for %s...", gameID.c_str()); + // NOTE: We only really need to sum up the diskspace for subfolders related to this game, and add it to the actual free space, + // to obtain the free space with the save data removed. + // We previously went through the meta file system here, but the memstick is always a directory so no need. + Path saveFolder = GetSysDirectory(DIRECTORY_SAVEDATA); + int64_t freeSpace = 0; + free_disk_space(saveFolder, freeSpace); + freeSpace += File::ComputeRecursiveDirectorySize(saveFolder); + return freeSpace; }, TaskType::IO_BLOCKING); } diff --git a/Core/HW/MemoryStick.h b/Core/HW/MemoryStick.h index 00ecfefba280..48b21e445d67 100644 --- a/Core/HW/MemoryStick.h +++ b/Core/HW/MemoryStick.h @@ -17,6 +17,8 @@ #pragma once +#include + #include "Common/CommonTypes.h" class PointerWrap; @@ -40,7 +42,7 @@ enum MemStickDriverState { PSP_MEMORYSTICK_STATE_DEVICE_REMOVED = 8, }; -void MemoryStick_Init(); +void MemoryStick_Init(std::string gameID); void MemoryStick_Shutdown(); void MemoryStick_DoState(PointerWrap &p); MemStickState MemoryStick_State(); From 7e194d15e25db172ba53f41113ccd006c137783c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 29 Nov 2024 15:34:22 +0100 Subject: [PATCH 5/7] For the free-space savedata scan, only scan relevant subdirectories. --- Core/Dialog/SavedataParam.cpp | 15 +++--- Core/FileSystems/DirectoryFileSystem.cpp | 11 +++-- Core/HLE/sceIo.cpp | 4 +- Core/HW/MemoryStick.cpp | 62 ++++++++++++++++-------- Core/HW/MemoryStick.h | 7 ++- 5 files changed, 64 insertions(+), 35 deletions(-) diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index 26c0a4949e7d..b9ec27ed031f 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -327,7 +327,7 @@ bool SavedataParam::Delete(SceUtilitySavedataParam* param, int saveId) { } // Sanity check, preventing full delete of savedata/ in MGS PW demo (!) - if (!strlen(param->gameName) && param->mode != SCE_UTILITY_SAVEDATA_TYPE_LISTALLDELETE) { + if (!strnlen(param->gameName, sizeof(param->gameName)) && param->mode != SCE_UTILITY_SAVEDATA_TYPE_LISTALLDELETE) { ERROR_LOG(Log::sceUtility, "Bad param with gameName empty - cannot delete save directory"); return false; } @@ -1119,17 +1119,15 @@ inline std::string FmtPspTime(const ScePspDateTime &dt) { return StringFromFormat("%04d-%02d-%02d %02d:%02d:%02d.%06d", dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, dt.microsecond); } -int SavedataParam::GetSizes(SceUtilitySavedataParam *param) -{ +int SavedataParam::GetSizes(SceUtilitySavedataParam *param) { if (!param) { return SCE_UTILITY_SAVEDATA_ERROR_SIZES_NO_DATA; } int ret = 0; - if (param->msFree.IsValid()) { - const u64 freeBytes = MemoryStick_FreeSpace(); + const u64 freeBytes = MemoryStick_FreeSpace(GetGameName(param)); param->msFree->clusterSize = (u32)MemoryStick_SectorSize(); param->msFree->freeClusters = (u32)(freeBytes / MemoryStick_SectorSize()); param->msFree->freeSpaceKB = (u32)(freeBytes / 0x400); @@ -1419,7 +1417,7 @@ bool SavedataParam::GetSize(SceUtilitySavedataParam *param) { if (param->sizeInfo.IsValid()) { auto listing = pspFileSystem.GetDirListing(saveDir, &exists); - const u64 freeBytes = MemoryStick_FreeSpace(); + const u64 freeBytes = MemoryStick_FreeSpace(GetGameName(param)); s64 overwriteBytes = 0; s64 writeBytes = 0; @@ -1511,6 +1509,11 @@ int SavedataParam::SetPspParam(SceUtilitySavedataParam *param) return 0; } + std::string gameName = GetGameName(param); + if (!gameName.empty()) { + MemoryStick_NotifyGameName(gameName); + } + if (param->mode == SCE_UTILITY_SAVEDATA_TYPE_LISTALLDELETE) { Clear(); int realCount = 0; diff --git a/Core/FileSystems/DirectoryFileSystem.cpp b/Core/FileSystems/DirectoryFileSystem.cpp index ed44591f2ccf..ec9e83b92f76 100644 --- a/Core/FileSystems/DirectoryFileSystem.cpp +++ b/Core/FileSystems/DirectoryFileSystem.cpp @@ -366,12 +366,15 @@ size_t DirectoryFileHandle::Write(const u8* pointer, s64 size) g_OSD.Show(OSDType::MESSAGE_ERROR, err->T("Disk full while writing data"), 0.0f, "diskfull"); // We only return an error when the disk is actually full. // When writing this would cause the disk to be full, so it wasn't written, we return 0. - if (MemoryStick_FreeSpace() == 0) { - // Sign extend on 64-bit. - return (size_t)(s64)(s32)SCE_KERNEL_ERROR_ERRNO_DEVICE_NO_FREE_SPACE; + Path saveFolder = GetSysDirectory(DIRECTORY_SAVEDATA); + int64_t space; + if (free_disk_space(saveFolder, space)) { + if (space < size) { + // Sign extend to a 64-bit value. + return (size_t)(s64)(s32)SCE_KERNEL_ERROR_ERRNO_DEVICE_NO_FREE_SPACE; + } } } - return bytesWritten; } diff --git a/Core/HLE/sceIo.cpp b/Core/HLE/sceIo.cpp index 58b43b747fca..292a3eba4a30 100644 --- a/Core/HLE/sceIo.cpp +++ b/Core/HLE/sceIo.cpp @@ -675,8 +675,8 @@ void __IoInit() { pspFileSystem.Mount("flash0:", flash0System); - const std::string gameId = g_paramSFO.GetDiscID(); if (g_RemasterMode) { + const std::string gameId = g_paramSFO.GetDiscID(); const Path exdataPath = GetSysDirectory(DIRECTORY_EXDATA) / gameId; if (File::Exists(exdataPath)) { auto exdataSystem = std::make_shared(&pspFileSystem, exdataPath, FileSystemFlags::SIMULATE_FAT32 | FileSystemFlags::CARD); @@ -698,7 +698,7 @@ void __IoInit() { __KernelRegisterWaitTypeFuncs(WAITTYPE_ASYNCIO, __IoAsyncBeginCallback, __IoAsyncEndCallback); - MemoryStick_Init(gameId); + MemoryStick_Init(); lastMemStickState = MemoryStick_State(); lastMemStickFatState = MemoryStick_FatState(); __DisplayListenVblank(__IoVblank); diff --git a/Core/HW/MemoryStick.cpp b/Core/HW/MemoryStick.cpp index fcfbc5b642b2..ec9d2a59d9b0 100644 --- a/Core/HW/MemoryStick.cpp +++ b/Core/HW/MemoryStick.cpp @@ -25,6 +25,7 @@ #include "Common/Thread/ThreadUtil.h" #include "Common/File/DiskFree.h" #include "Common/File/FileUtil.h" +#include "Common/File/DirListing.h" #include "Core/Config.h" #include "Core/CoreTiming.h" #include "Core/Compatibility.h" @@ -96,8 +97,20 @@ u64 MemoryStick_SectorSize() { return 32 * 1024; // 32KB } -u64 MemoryStick_FreeSpace() { - INFO_LOG(Log::IO, "Calculating free disk space"); +static uint64_t ComputeSizeOfSavedataForGame(const Path &saveFolder, const std::string_view gameID) { + uint64_t space = 0; + std::vector subDirs; + File::GetFilesInDir(saveFolder, &subDirs, nullptr, 0, gameID); // gameID as directory prefix. + for (auto &dir : subDirs) { + if (!dir.isDirectory) + continue; + space += File::ComputeRecursiveDirectorySize(saveFolder / dir.name); + } + return space; +} + +u64 MemoryStick_FreeSpace(std::string gameID) { + INFO_LOG(Log::IO, "Calculating free disk space (%s)", gameID.c_str()); const u64 memstickInitialFree = g_initialMemstickSizePromise->BlockUntilReady(); const CompatFlags &flags = PSP_CoreParameter().compat.flags(); @@ -108,9 +121,10 @@ u64 MemoryStick_FreeSpace() { // We have a compat setting to make it even smaller for Harry Potter : Goblet of Fire, see #13266. const u64 memStickSize = flags.ReportSmallMemstick ? smallMemstickSize : (u64)g_Config.iMemStickSizeGB * 1024 * 1024 * 1024; - // Assume the memory stick is only used to store savedata. + // Assume the memory stick is only used to store savedata, for the current game only. if (!memstickCurrentUseValid) { - memstickCurrentUse = pspFileSystem.ComputeRecursiveDirectorySize("ms0:/PSP/SAVEDATA/"); + Path saveFolder = GetSysDirectory(DIRECTORY_SAVEDATA); + memstickCurrentUse = ComputeSizeOfSavedataForGame(saveFolder, gameID); memstickCurrentUseValid = true; } @@ -160,7 +174,29 @@ void MemoryStick_SetState(MemStickState state) { } } -void MemoryStick_Init(std::string gameID) { +void MemoryStick_NotifyGameName(std::string gameID) { + // const CompatFlags &flags = PSP_CoreParameter().compat.flags(); + //if (flags.MemstickFixedFree) { + // See issue #12761 + + if (!g_initialMemstickSizePromise) { + g_initialMemstickSizePromise = Promise::Spawn(&g_threadManager, [gameID]() -> uint64_t { + INFO_LOG(Log::System, "Calculating initial savedata size for %s...", gameID.c_str()); + // NOTE: We only really need to sum up the diskspace for subfolders related to this game, and add it to the actual free space, + // to obtain the free space with the save data removed. + // We previously went through the meta file system here, but the memstick is always a directory so no need. + Path saveFolder = GetSysDirectory(DIRECTORY_SAVEDATA); + int64_t freeSpace = 0; + free_disk_space(saveFolder, freeSpace); + + // Only add the space of the folders that the game itself touches. + freeSpace += ComputeSizeOfSavedataForGame(saveFolder, gameID); + return freeSpace; + }, TaskType::IO_BLOCKING); + } +} + +void MemoryStick_Init() { if (g_Config.bMemStickInserted) { memStickState = PSP_MEMORYSTICK_STATE_INSERTED; memStickFatState = PSP_FAT_MEMORYSTICK_STATE_ASSIGNED; @@ -170,22 +206,6 @@ void MemoryStick_Init(std::string gameID) { } memStickNeedsAssign = false; - - const CompatFlags &flags = PSP_CoreParameter().compat.flags(); - //if (flags.MemstickFixedFree) { - - // See issue #12761 - g_initialMemstickSizePromise = Promise::Spawn(&g_threadManager, [gameID]() -> uint64_t { - INFO_LOG(Log::System, "Calculating initial savedata size for %s...", gameID.c_str()); - // NOTE: We only really need to sum up the diskspace for subfolders related to this game, and add it to the actual free space, - // to obtain the free space with the save data removed. - // We previously went through the meta file system here, but the memstick is always a directory so no need. - Path saveFolder = GetSysDirectory(DIRECTORY_SAVEDATA); - int64_t freeSpace = 0; - free_disk_space(saveFolder, freeSpace); - freeSpace += File::ComputeRecursiveDirectorySize(saveFolder); - return freeSpace; - }, TaskType::IO_BLOCKING); } void MemoryStick_Shutdown() { diff --git a/Core/HW/MemoryStick.h b/Core/HW/MemoryStick.h index 48b21e445d67..d0b6a24f0ac1 100644 --- a/Core/HW/MemoryStick.h +++ b/Core/HW/MemoryStick.h @@ -42,8 +42,11 @@ enum MemStickDriverState { PSP_MEMORYSTICK_STATE_DEVICE_REMOVED = 8, }; -void MemoryStick_Init(std::string gameID); +void MemoryStick_Init(); void MemoryStick_Shutdown(); + +void MemoryStick_NotifyGameName(std::string gameName); + void MemoryStick_DoState(PointerWrap &p); MemStickState MemoryStick_State(); MemStickFatState MemoryStick_FatState(); @@ -52,5 +55,5 @@ void MemoryStick_SetState(MemStickState state); void MemoryStick_SetFatState(MemStickFatState state); u64 MemoryStick_SectorSize(); -u64 MemoryStick_FreeSpace(); +u64 MemoryStick_FreeSpace(std::string gameID); void MemoryStick_NotifyWrite(); From 84154e837e96acf6f47c806fb51573e418df60f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 29 Nov 2024 15:37:45 +0100 Subject: [PATCH 6/7] Avoid running the initial savedata size scan except on games that need it. --- Core/Dialog/SavedataParam.cpp | 2 ++ Core/HW/MemoryStick.cpp | 10 ++++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Core/Dialog/SavedataParam.cpp b/Core/Dialog/SavedataParam.cpp index b9ec27ed031f..9d1ef08bd4a1 100644 --- a/Core/Dialog/SavedataParam.cpp +++ b/Core/Dialog/SavedataParam.cpp @@ -1228,6 +1228,8 @@ bool SavedataParam::GetList(SceUtilitySavedataParam *param) std::vector validDir; std::vector sfoFiles; + + // TODO: Here we can filter by prefix - only the savename in param is likely to be a regex. std::vector allDir = pspFileSystem.GetDirListing(savePath); std::string searchString = GetGameName(param) + GetSaveName(param); diff --git a/Core/HW/MemoryStick.cpp b/Core/HW/MemoryStick.cpp index ec9d2a59d9b0..b99e47a7530e 100644 --- a/Core/HW/MemoryStick.cpp +++ b/Core/HW/MemoryStick.cpp @@ -111,7 +111,6 @@ static uint64_t ComputeSizeOfSavedataForGame(const Path &saveFolder, const std:: u64 MemoryStick_FreeSpace(std::string gameID) { INFO_LOG(Log::IO, "Calculating free disk space (%s)", gameID.c_str()); - const u64 memstickInitialFree = g_initialMemstickSizePromise->BlockUntilReady(); const CompatFlags &flags = PSP_CoreParameter().compat.flags(); u64 realFreeSpace = pspFileSystem.FreeDiskSpace("ms0:/"); @@ -137,6 +136,7 @@ u64 MemoryStick_FreeSpace(std::string gameID) { simulatedFreeSpace = smallMemstickSize / 2; // just pick a value. } if (flags.MemstickFixedFree) { + const u64 memstickInitialFree = g_initialMemstickSizePromise->BlockUntilReady(); _dbg_assert_(g_initialMemstickSizePromise); // Assassin's Creed: Bloodlines fails to save if free space changes incorrectly during game. // See issue #12761 @@ -175,10 +175,12 @@ void MemoryStick_SetState(MemStickState state) { } void MemoryStick_NotifyGameName(std::string gameID) { - // const CompatFlags &flags = PSP_CoreParameter().compat.flags(); - //if (flags.MemstickFixedFree) { - // See issue #12761 + const CompatFlags &flags = PSP_CoreParameter().compat.flags(); + if (!flags.MemstickFixedFree) { + return; + } + // See issue #12761 if (!g_initialMemstickSizePromise) { g_initialMemstickSizePromise = Promise::Spawn(&g_threadManager, [gameID]() -> uint64_t { INFO_LOG(Log::System, "Calculating initial savedata size for %s...", gameID.c_str()); From 48bc04b7e1b80340d5e90d06c8bf8bfda66c1349 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Fri, 29 Nov 2024 16:00:34 +0100 Subject: [PATCH 7/7] Add a separate bool for I/O logging --- Common/File/DirListing.cpp | 18 +++++++++++++++-- Common/File/FileUtil.cpp | 41 +++++++++++++++++++++++++++++--------- 2 files changed, 48 insertions(+), 11 deletions(-) diff --git a/Common/File/DirListing.cpp b/Common/File/DirListing.cpp index f17675fe2cec..0f9d10aa859b 100644 --- a/Common/File/DirListing.cpp +++ b/Common/File/DirListing.cpp @@ -43,7 +43,12 @@ #endif // HAVE_LIBNX // NOTE: There's another one in FileUtil.cpp. +#ifdef _WIN32 constexpr bool SIMULATE_SLOW_IO = false; +#else +constexpr bool SIMULATE_SLOW_IO = false; +#endif +constexpr bool LOG_IO = false; namespace File { @@ -57,8 +62,10 @@ static uint64_t FiletimeToStatTime(FILETIME ft) { #endif bool GetFileInfo(const Path &path, FileInfo * fileInfo) { - if (SIMULATE_SLOW_IO) { + if (LOG_IO) { INFO_LOG(Log::System, "GetFileInfo %s", path.c_str()); + } + if (SIMULATE_SLOW_IO) { sleep_ms(300, "slow-io-sim"); } @@ -184,8 +191,10 @@ std::vector ApplyFilter(std::vector files, const } bool GetFilesInDir(const Path &directory, std::vector *files, const char *filter, int flags, std::string_view prefix) { - if (SIMULATE_SLOW_IO) { + if (LOG_IO) { INFO_LOG(Log::System, "GetFilesInDir %s (ext %s, prefix %.*s)", directory.c_str(), filter, (int)prefix.size(), prefix.data()); + } + if (SIMULATE_SLOW_IO) { sleep_ms(300, "slow-io-sim"); } @@ -267,10 +276,12 @@ bool GetFilesInDir(const Path &directory, std::vector *files, const ch continue; } + /* if (SIMULATE_SLOW_IO) { INFO_LOG(Log::System, "GetFilesInDir item %s", virtualName.c_str()); sleep_ms(50, "slow-io-sim"); } + */ FileInfo info; info.name = virtualName; @@ -345,6 +356,9 @@ bool GetFilesInDir(const Path &directory, std::vector *files, const ch closedir(dirp); #endif std::sort(files->begin(), files->end()); + if (LOG_IO) { + INFO_LOG(Log::System, "GetFilesInDir: Found %d files", (int)files->size()); + } return true; } diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index 9bcdace738bd..74e490dc1a82 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -91,7 +91,12 @@ #include // NOTE: There's another one in DirListing.cpp. +#ifdef _WIN32 +constexpr bool SIMULATE_SLOW_IO = false; +#else constexpr bool SIMULATE_SLOW_IO = false; +#endif +constexpr bool LOG_IO = false; #ifndef S_ISDIR #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) @@ -115,8 +120,10 @@ constexpr bool SIMULATE_SLOW_IO = false; namespace File { FILE *OpenCFile(const Path &path, const char *mode) { - if (SIMULATE_SLOW_IO) { + if (LOG_IO) { INFO_LOG(Log::System, "OpenCFile %s, %s", path.c_str(), mode); + } + if (SIMULATE_SLOW_IO) { sleep_ms(300, "slow-io-sim"); } switch (path.Type()) { @@ -217,8 +224,10 @@ static std::string OpenFlagToString(OpenFlag flags) { } int OpenFD(const Path &path, OpenFlag flags) { - if (SIMULATE_SLOW_IO) { + if (LOG_IO) { INFO_LOG(Log::System, "OpenFD %s, %d", path.c_str(), flags); + } + if (SIMULATE_SLOW_IO) { sleep_ms(300, "slow-io-sim"); } @@ -315,8 +324,10 @@ static bool ResolvePathVista(const std::wstring &path, wchar_t *buf, DWORD bufSi #endif std::string ResolvePath(const std::string &path) { - if (SIMULATE_SLOW_IO) { + if (LOG_IO) { INFO_LOG(Log::System, "ResolvePath %s", path.c_str()); + } + if (SIMULATE_SLOW_IO) { sleep_ms(100, "slow-io-sim"); } @@ -408,9 +419,11 @@ bool ExistsInDir(const Path &path, const std::string &filename) { } bool Exists(const Path &path) { + if (LOG_IO) { + INFO_LOG(Log::System, "Exists %s", path.c_str()); + } if (SIMULATE_SLOW_IO) { sleep_ms(200, "slow-io-sim"); - INFO_LOG(Log::System, "Exists %s", path.c_str()); } if (path.Type() == PathType::CONTENT_URI) { @@ -445,9 +458,11 @@ bool Exists(const Path &path) { // Returns true if filename exists and is a directory bool IsDirectory(const Path &path) { + if (LOG_IO) { + INFO_LOG(Log::System, "IsDirectory %s", path.c_str()); + } if (SIMULATE_SLOW_IO) { sleep_ms(100, "slow-io-sim"); - INFO_LOG(Log::System, "IsDirectory %s", path.c_str()); } switch (path.Type()) { @@ -660,9 +675,11 @@ bool CreateFullPath(const Path &path) { // renames file srcFilename to destFilename, returns true on success bool Rename(const Path &srcFilename, const Path &destFilename) { + if (LOG_IO) { + INFO_LOG(Log::System, "Rename %s -> %s", srcFilename.c_str(), destFilename.c_str()); + } if (SIMULATE_SLOW_IO) { sleep_ms(100, "slow-io-sim"); - INFO_LOG(Log::System, "Rename %s -> %s", srcFilename.c_str(), destFilename.c_str()); } if (srcFilename.Type() != destFilename.Type()) { @@ -713,9 +730,11 @@ bool Rename(const Path &srcFilename, const Path &destFilename) { // copies file srcFilename to destFilename, returns true on success bool Copy(const Path &srcFilename, const Path &destFilename) { + if (LOG_IO) { + INFO_LOG(Log::System, "Copy %s -> %s", srcFilename.c_str(), destFilename.c_str()); + } if (SIMULATE_SLOW_IO) { sleep_ms(100, "slow-io-sim"); - INFO_LOG(Log::System, "Copy %s -> %s", srcFilename.c_str(), destFilename.c_str()); } switch (srcFilename.Type()) { case PathType::NATIVE: @@ -857,9 +876,11 @@ bool MoveIfFast(const Path &srcFilename, const Path &destFilename) { // Returns the size of file (64bit) // TODO: Add a way to return an error. uint64_t GetFileSize(const Path &filename) { + if (LOG_IO) { + INFO_LOG(Log::System, "GetFileSize %s", filename.c_str()); + } if (SIMULATE_SLOW_IO) { sleep_ms(100, "slow-io-sim"); - INFO_LOG(Log::System, "GetFileSize %s", filename.c_str()); } switch (filename.Type()) { case PathType::NATIVE: @@ -967,9 +988,11 @@ bool CreateEmptyFile(const Path &filename) { // Deletes an empty directory, returns true on success // WARNING: On Android with content URIs, it will delete recursively! bool DeleteDir(const Path &path) { + if (LOG_IO) { + INFO_LOG(Log::System, "DeleteDir %s", path.c_str()); + } if (SIMULATE_SLOW_IO) { sleep_ms(100, "slow-io-sim"); - INFO_LOG(Log::System, "DeleteDir %s", path.c_str()); } switch (path.Type()) { case PathType::NATIVE: