mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-07-11 01:25:07 +02:00
Trash handling is too high level for FileUtil, move it up.
This commit is contained in:
@@ -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 <sys/utime.h>
|
||||
#include <shlobj.h> // for SHGetFolderPath
|
||||
#include <shellapi.h>
|
||||
#include <commdlg.h> // for GetSaveFileName
|
||||
#include <io.h>
|
||||
#include <direct.h> // 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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
+6
-1
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
+3
-3
@@ -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);
|
||||
|
||||
@@ -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() {
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
#include <optional>
|
||||
|
||||
#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);
|
||||
|
||||
+28
-7
@@ -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<Path> 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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "ShellUtil.h"
|
||||
|
||||
#include <shobjidl.h> // For IFileDialog and related interfaces
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h>
|
||||
#include <commdlg.h>
|
||||
#include <cderr.h>
|
||||
@@ -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);
|
||||
|
||||
@@ -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<std::string> 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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
Submodule pspautotests updated: 2d09fac64f...4374348477
Reference in New Issue
Block a user