diff --git a/Common/File/FileUtil.cpp b/Common/File/FileUtil.cpp index a74b9d722d..6840221dac 100644 --- a/Common/File/FileUtil.cpp +++ b/Common/File/FileUtil.cpp @@ -42,13 +42,12 @@ #include "Common/StringUtils.h" #include "Common/TimeUtil.h" #include "Common/SysError.h" +#include "Common/System/Request.h" #ifdef _WIN32 #include "Common/CommonWindows.h" #include -#include // for SHGetFolderPath #include -#include // for GetSaveFileName #include #include // getcwd #if PPSSPP_PLATFORM(UWP) @@ -1371,56 +1370,4 @@ bool IsProbablyInDownloadsFolder(const Path &filename) { return filename.FilePathContainsNoCase("download"); } -// The Win32 implementation kinda belongs in ShellUtil.cpp but that's in the wrong project. -// Some reorganization is in order... -bool MoveFileToTrash(const Path &path) { -#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) - IFileOperation *pFileOp = nullptr; - HRESULT hr = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pFileOp)); - if (FAILED(hr)) { - return false; - } - - // Set operation flags - hr = pFileOp->SetOperationFlags(FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT); - if (FAILED(hr)) { - pFileOp->Release(); - CoUninitialize(); - return false; - } - - // Create a shell item from the file path - IShellItem* pItem = nullptr; - hr = SHCreateItemFromParsingName(path.ToWString().c_str(), nullptr, IID_PPV_ARGS(&pItem)); - if (SUCCEEDED(hr)) { - // Schedule the delete (move to recycle bin) - hr = pFileOp->DeleteItem(pItem, nullptr); - if (SUCCEEDED(hr)) { - hr = pFileOp->PerformOperations(); // Execute - } - pItem->Release(); - } - pFileOp->Release(); - return true; -#else - return false; -#endif -} - -bool MoveFileToTrashOrDelete(const Path &path) { -#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) - return MoveFileToTrash(path); -#else - return Delete(path); -#endif -} - -bool MoveDirectoryTreeToTrashOrDelete(const Path &path) { -#if PPSSPP_PLATFORM(WINDOWS) && !PPSSPP_PLATFORM(UWP) - return MoveFileToTrash(path); // works with directories -#else - return DeleteDirRecursively(path); -#endif -} - } // namespace File diff --git a/Common/File/FileUtil.h b/Common/File/FileUtil.h index 96913efe43..10ae5b797f 100644 --- a/Common/File/FileUtil.h +++ b/Common/File/FileUtil.h @@ -233,12 +233,4 @@ inline bool ReadTextFileToString(const Path &path, std::string *str) { // Return value must be delete[]-d. uint8_t *ReadLocalFile(const Path &path, size_t *size); -// Moves to trash/recycle bin. -// This is only functional if SYSPROP_HAS_TRASH_BIN is true, otherwise will return false. -bool MoveFileToTrash(const Path &path); - -// These move to trash if possible, otherwise permanently delete. -bool MoveFileToTrashOrDelete(const Path &path); -bool MoveDirectoryTreeToTrashOrDelete(const Path &path); - } // namespace diff --git a/Common/System/Request.cpp b/Common/System/Request.cpp index 687ac40f33..5a0a5e3c71 100644 --- a/Common/System/Request.cpp +++ b/Common/System/Request.cpp @@ -150,3 +150,8 @@ void System_RunCallbackInWndProc(void (*callback)(void *, void *), void *userdat int64_t castUserData = (int64_t)userdata; g_requestManager.MakeSystemRequest(SystemRequestType::RUN_CALLBACK_IN_WNDPROC, NO_REQUESTER_TOKEN, nullptr, nullptr, "", "", castPtr, castUserData); } + +void System_MoveToTrash(const Path &path) { + g_requestManager.MakeSystemRequest(SystemRequestType::MOVE_TO_TRASH, NO_REQUESTER_TOKEN, nullptr, nullptr, path.ToString(), "", 0); +} + diff --git a/Common/System/Request.h b/Common/System/Request.h index 9d408ca39c..a6fce9a8a9 100644 --- a/Common/System/Request.h +++ b/Common/System/Request.h @@ -189,6 +189,7 @@ inline void System_SendDebugScreenshot(std::string_view data, int height) { g_requestManager.MakeSystemRequest(SystemRequestType::SEND_DEBUG_SCREENSHOT, NO_REQUESTER_TOKEN, nullptr, nullptr, data, "", height); } +void System_MoveToTrash(const Path &path); void System_RunCallbackInWndProc(void (*callback)(void *, void *), void *userdata); // Non-inline to avoid including Path.h diff --git a/Common/System/System.h b/Common/System/System.h index 8858b947e3..de592b90bd 100644 --- a/Common/System/System.h +++ b/Common/System/System.h @@ -98,6 +98,8 @@ enum class SystemRequestType { MICROPHONE_COMMAND, RUN_CALLBACK_IN_WNDPROC, + + MOVE_TO_TRASH, }; // Run a closure on the main thread. Used to safely implement UI that runs on another thread. diff --git a/Core/Config.cpp b/Core/Config.cpp index 1e8485a05e..a264fef029 100644 --- a/Core/Config.cpp +++ b/Core/Config.cpp @@ -49,6 +49,7 @@ #include "Common/GPU/Vulkan/VulkanLoader.h" #include "Common/VR/PPSSPPVR.h" #include "Common/System/OSD.h" +#include "Common/System/Request.h" #include "Core/Config.h" #include "Core/ConfigSettings.h" #include "Core/ConfigValues.h" @@ -1690,7 +1691,11 @@ bool Config::deleteGameConfig(const std::string& pGameId) { Path fullIniFilePath = Path(getGameConfigFile(pGameId, &exists)); if (exists) { - File::MoveFileToTrashOrDelete(fullIniFilePath); + if (System_GetPropertyBool(SYSPROP_HAS_TRASH_BIN)) { + System_MoveToTrash(fullIniFilePath); + } else { + File::Delete(fullIniFilePath); + } } return true; } diff --git a/Core/Loaders.cpp b/Core/Loaders.cpp index 2fd57ae3af..980d41fff4 100644 --- a/Core/Loaders.cpp +++ b/Core/Loaders.cpp @@ -318,6 +318,32 @@ bool UmdReplace(const Path &filepath, FileLoader **fileLoader, std::string &erro return true; } +// Close the return value with ZipClose (if non-null, of course). +struct zip *ZipOpenPath(const Path &fileName) { + int error = 0; + // Need to special case for content URI here, similar to OpenCFile. + struct zip *z; +#if PPSSPP_PLATFORM(ANDROID) + if (fileName.Type() == PathType::CONTENT_URI) { + int fd = File::OpenFD(fileName, File::OPEN_READ); + z = zip_fdopen(fd, 0, &error); + } else +#endif + { // continuation of above else in the ifdef + z = zip_open(fileName.c_str(), 0, &error); + } + + if (!z) { + ERROR_LOG(Log::HLE, "Failed to open ZIP file '%s', error code=%i", fileName.c_str(), error); + } + return z; +} + +void ZipClose(struct zip *z) { + if (z) + zip_close(z); +} + bool DetectZipFileContents(const Path &fileName, ZipFileInfo *info) { struct zip *z = ZipOpenPath(fileName); if (!z) { diff --git a/Core/Loaders.h b/Core/Loaders.h index a50ad11773..0063aba1af 100644 --- a/Core/Loaders.h +++ b/Core/Loaders.h @@ -155,9 +155,6 @@ Path ResolvePBPFile(const Path &filename); IdentifiedFileType Identify_File(FileLoader *fileLoader, std::string *errorString); -// Can modify the string filename, as it calls IdentifyFile above. -bool LoadFile(FileLoader **fileLoaderPtr, IdentifiedFileType type, std::string *error_string); - bool UmdReplace(const Path &filepath, FileLoader **fileLoader, std::string &error); @@ -187,5 +184,8 @@ struct ZipFileInfo { std::string contentName; }; +struct zip *ZipOpenPath(const Path &fileName); +void ZipClose(zip *z); + bool DetectZipFileContents(const Path &fileName, ZipFileInfo *info); void DetectZipFileContents(struct zip *z, ZipFileInfo *info); diff --git a/Core/Util/GameManager.cpp b/Core/Util/GameManager.cpp index a3cbf82314..e3a74abcc2 100644 --- a/Core/Util/GameManager.cpp +++ b/Core/Util/GameManager.cpp @@ -52,32 +52,6 @@ GameManager g_GameManager; -// Close the return value with ZipClose (if non-null, of course). -struct zip *ZipOpenPath(Path fileName) { - int error = 0; - // Need to special case for content URI here, similar to OpenCFile. - struct zip *z; -#if PPSSPP_PLATFORM(ANDROID) - if (fileName.Type() == PathType::CONTENT_URI) { - int fd = File::OpenFD(fileName, File::OPEN_READ); - z = zip_fdopen(fd, 0, &error); - } else -#endif - { // continuation of above else in the ifdef - z = zip_open(fileName.c_str(), 0, &error); - } - - if (!z) { - ERROR_LOG(Log::HLE, "Failed to open ZIP file '%s', error code=%i", fileName.c_str(), error); - } - return z; -} - -void ZipClose(struct zip *z) { - if (z) - zip_close(z); -} - GameManager::GameManager() { } diff --git a/Core/Util/GameManager.h b/Core/Util/GameManager.h index 7bbb315dc9..3bc9dbf70b 100644 --- a/Core/Util/GameManager.h +++ b/Core/Util/GameManager.h @@ -27,7 +27,6 @@ #include #include "Common/Net/HTTPClient.h" -#include "Common/File/Path.h" enum class GameManagerState { IDLE, @@ -45,6 +44,7 @@ struct ZipFileTask { struct zip; class FileLoader; +class Path; struct ZipFileInfo; class GameManager { @@ -119,7 +119,4 @@ private: extern GameManager g_GameManager; -struct zip *ZipOpenPath(Path fileName); -void ZipClose(zip *z); - bool CanExtractWithoutOverwrite(struct zip *z, const Path &destination, int maxOkFiles); diff --git a/UI/GameInfoCache.cpp b/UI/GameInfoCache.cpp index d1bbc0f9ba..cd59be0685 100644 --- a/UI/GameInfoCache.cpp +++ b/UI/GameInfoCache.cpp @@ -30,6 +30,7 @@ #include "Common/File/FileUtil.h" #include "Common/File/Path.h" #include "Common/Render/ManagedTexture.h" +#include "Common/System/Request.h" #include "Common/StringUtils.h" #include "Common/TimeUtil.h" #include "Core/FileSystems/ISOFileSystem.h" @@ -86,6 +87,26 @@ bool IsReasonableEbootDirectory(Path path) { return true; } +static bool MoveFileToTrashOrDelete(const Path &path) { + if (System_GetPropertyBool(SYSPROP_HAS_TRASH_BIN)) { + // TODO: Way to see if it succeeded + System_MoveToTrash(path); + return true; + } else { + return File::Delete(path); + } +} + +static bool MoveDirectoryTreeToTrashOrDelete(const Path &path) { + if (System_GetPropertyBool(SYSPROP_HAS_TRASH_BIN)) { + // TODO: Way to see if it succeeded + System_MoveToTrash(path); + return true; + } else { + return File::DeleteDirRecursively(path); + } +} + bool GameInfo::Delete() { switch (fileType) { case IdentifiedFileType::PSP_ISO: @@ -94,7 +115,7 @@ bool GameInfo::Delete() { // Just delete the one file (TODO: handle two-disk games as well somehow). Path fileToRemove = filePath_; INFO_LOG(Log::System, "Deleting file %s", fileToRemove.c_str()); - File::MoveFileToTrashOrDelete(fileToRemove); + MoveFileToTrashOrDelete(fileToRemove); g_recentFiles.Remove(filePath_.ToString()); return true; } @@ -108,14 +129,14 @@ bool GameInfo::Delete() { // This can happen if the PBP is misplaced, see issue #20187 if (!IsReasonableEbootDirectory(directoryToRemove)) { // Just delete the eboot. - File::MoveFileToTrashOrDelete(filePath_); + MoveFileToTrashOrDelete(filePath_); g_recentFiles.Remove(filePath_.ToString()); return true; } // Delete the whole tree. We better be sure, see IsReasonableEbootDirectory. INFO_LOG(Log::System, "Deleting directory %s", directoryToRemove.c_str()); - if (!File::MoveDirectoryTreeToTrashOrDelete(directoryToRemove)) { + if (!MoveDirectoryTreeToTrashOrDelete(directoryToRemove)) { ERROR_LOG(Log::System, "Failed to delete file"); return false; } @@ -133,7 +154,7 @@ bool GameInfo::Delete() { { const Path &fileToRemove = filePath_; INFO_LOG(Log::System, "Deleting file %s", fileToRemove.c_str()); - File::MoveFileToTrashOrDelete(fileToRemove); + MoveFileToTrashOrDelete(fileToRemove); g_recentFiles.Remove(filePath_.ToString()); return true; } @@ -142,10 +163,10 @@ bool GameInfo::Delete() { { const Path &ppstPath = filePath_; INFO_LOG(Log::System, "Deleting file %s", ppstPath.c_str()); - File::MoveFileToTrashOrDelete(ppstPath); + MoveFileToTrashOrDelete(ppstPath); const Path screenshotPath = filePath_.WithReplacedExtension(".ppst", ".jpg"); if (File::Exists(screenshotPath)) { - File::MoveFileToTrashOrDelete(screenshotPath); + MoveFileToTrashOrDelete(screenshotPath); } return true; } @@ -331,7 +352,7 @@ bool GameInfo::DeleteAllSaveData() { std::vector saveDataDir = GetSaveDataDirectories(); for (size_t j = 0; j < saveDataDir.size(); j++) { INFO_LOG(Log::System, "Deleting savedata from %s", saveDataDir[j].c_str()); - if (!File::MoveDirectoryTreeToTrashOrDelete(saveDataDir[j])) { + if (!MoveDirectoryTreeToTrashOrDelete(saveDataDir[j])) { ERROR_LOG(Log::System, "Failed to delete savedata %s", saveDataDir[j].c_str()); } } diff --git a/Windows/W32Util/ShellUtil.cpp b/Windows/W32Util/ShellUtil.cpp index 5af48943de..d9b659ab95 100644 --- a/Windows/W32Util/ShellUtil.cpp +++ b/Windows/W32Util/ShellUtil.cpp @@ -11,6 +11,7 @@ #include "ShellUtil.h" #include // For IFileDialog and related interfaces +#include #include #include #include @@ -18,6 +19,43 @@ namespace W32Util { +bool MoveToTrash(const Path &path) { + IFileOperation *pFileOp = nullptr; + + HRESULT hr = CoCreateInstance(CLSID_FileOperation, nullptr, CLSCTX_ALL, IID_PPV_ARGS(&pFileOp)); + if (FAILED(hr)) { + return false; + } + + // Set operation flags + hr = pFileOp->SetOperationFlags(FOF_ALLOWUNDO | FOF_NOCONFIRMATION | FOF_SILENT); + if (FAILED(hr)) { + pFileOp->Release(); + return false; + } + + // Create a shell item from the file path + IShellItem* pItem = nullptr; + hr = SHCreateItemFromParsingName(path.ToWString().c_str(), nullptr, IID_PPV_ARGS(&pItem)); + if (SUCCEEDED(hr)) { + // Schedule the delete (move to recycle bin) + hr = pFileOp->DeleteItem(pItem, nullptr); + if (SUCCEEDED(hr)) { + hr = pFileOp->PerformOperations(); // Execute + } + pItem->Release(); + } + pFileOp->Release(); + + if (SUCCEEDED(hr)) { + INFO_LOG(Log::IO, "Moved file to trash successfully: %s", path.c_str()); + return true; + } else { + WARN_LOG(Log::IO, "Failed to move file to trash: %s", path.c_str()); + return false; + } +} + std::string BrowseForFolder2(HWND parent, std::string_view title, std::string_view initialPath) { const std::wstring wtitle = ConvertUTF8ToWString(title); const std::wstring initialDir = ConvertUTF8ToWString(initialPath); diff --git a/Windows/W32Util/ShellUtil.h b/Windows/W32Util/ShellUtil.h index 44cd52a6f3..1f17b86562 100644 --- a/Windows/W32Util/ShellUtil.h +++ b/Windows/W32Util/ShellUtil.h @@ -10,13 +10,13 @@ class Path; namespace W32Util { std::string BrowseForFolder2(HWND parent, std::string_view title, std::string_view initialPath); - bool BrowseForFileName(bool _bLoad, HWND _hParent, const wchar_t*_pTitle, const wchar_t *_pInitialFolder, const wchar_t *_pFilter, const wchar_t*_pExtension, std::string& _strFileName); std::vector BrowseForFileNameMultiSelect(bool _bLoad, HWND _hParent, const wchar_t*_pTitle, const wchar_t*_pInitialFolder, const wchar_t*_pFilter, const wchar_t*_pExtension); +bool MoveToTrash(const Path &path); std::string UserDocumentsPath(); bool CreateDesktopShortcut(std::string_view argumentPath, std::string_view gameTitle, const Path &icoFile); diff --git a/Windows/main.cpp b/Windows/main.cpp index 19c05d502c..b46c3e3c01 100644 --- a/Windows/main.cpp +++ b/Windows/main.cpp @@ -720,6 +720,11 @@ bool System_MakeRequest(SystemRequestType type, int requestId, const std::string MainWindow::RunCallbackInWndProc(func, userdata); return true; } + case SystemRequestType::MOVE_TO_TRASH: + { + W32Util::MoveToTrash(Path(param1)); + return true; + } default: return false; } diff --git a/pspautotests b/pspautotests index 2d09fac64f..4374348477 160000 --- a/pspautotests +++ b/pspautotests @@ -1 +1 @@ -Subproject commit 2d09fac64fa70355f119434b8555352165aba371 +Subproject commit 4374348477b425f48f99c8fd58a853c439f78acd