diff --git a/common/FileSystem.cpp b/common/FileSystem.cpp index 132e841430..61873579f7 100644 --- a/common/FileSystem.cpp +++ b/common/FileSystem.cpp @@ -37,6 +37,7 @@ #if defined(_WIN32) #include "RedtapeWindows.h" #include +#include #include #if defined(_UWP) @@ -595,12 +596,29 @@ std::string Path::Combine(const std::string_view& base, const std::string_view& } #ifdef _UWP -static std::FILE* OpenCFileUWP(const wchar_t* wfilename, const wchar_t* mode) +static std::FILE* OpenCFileUWP(const wchar_t* wfilename, const wchar_t* mode, FileSystem::FileShareMode share_mode) { DWORD access = 0; DWORD share = 0; DWORD disposition = 0; + switch (share_mode) + { + case FileSystem::FileShareMode::DenyNone: + share = FILE_SHARE_READ | FILE_SHARE_WRITE; + break; + case FileSystem::FileShareMode::DenyRead: + share = FILE_SHARE_WRITE; + break; + case FileSystem::FileShareMode::DenyWrite: + share = FILE_SHARE_READ; + break; + case FileSystem::FileShareMode::DenyReadWrite: + default: + share = 0; + break; + } + int flags = 0; const wchar_t* tmode = mode; while (*tmode) @@ -608,7 +626,6 @@ static std::FILE* OpenCFileUWP(const wchar_t* wfilename, const wchar_t* mode) if (*tmode == L'r' && *(tmode + 1) == L'+') { access = GENERIC_READ | GENERIC_WRITE; - share = 0; disposition = OPEN_EXISTING; flags |= _O_RDWR; tmode += 2; @@ -616,7 +633,6 @@ static std::FILE* OpenCFileUWP(const wchar_t* wfilename, const wchar_t* mode) else if (*tmode == L'w' && *(tmode + 1) == L'+') { access = GENERIC_READ | GENERIC_WRITE; - share = 0; disposition = CREATE_ALWAYS; flags |= _O_RDWR | _O_CREAT | _O_TRUNC; tmode += 2; @@ -624,7 +640,6 @@ static std::FILE* OpenCFileUWP(const wchar_t* wfilename, const wchar_t* mode) else if (*tmode == L'a' && *(tmode + 1) == L'+') { access = GENERIC_READ | GENERIC_WRITE; - share = 0; disposition = CREATE_ALWAYS; flags |= _O_RDWR | _O_APPEND | _O_CREAT | _O_TRUNC; tmode += 2; @@ -632,7 +647,6 @@ static std::FILE* OpenCFileUWP(const wchar_t* wfilename, const wchar_t* mode) else if (*tmode == L'r') { access = GENERIC_READ; - share = 0; disposition = OPEN_EXISTING; flags |= _O_RDONLY; tmode++; @@ -640,7 +654,6 @@ static std::FILE* OpenCFileUWP(const wchar_t* wfilename, const wchar_t* mode) else if (*tmode == L'w') { access = GENERIC_WRITE; - share = 0; disposition = CREATE_ALWAYS; flags |= _O_WRONLY | _O_CREAT | _O_TRUNC; tmode++; @@ -648,7 +661,6 @@ static std::FILE* OpenCFileUWP(const wchar_t* wfilename, const wchar_t* mode) else if (*tmode == L'a') { access = GENERIC_READ | GENERIC_WRITE; - share = 0; disposition = CREATE_ALWAYS; flags |= _O_WRONLY | _O_APPEND | _O_CREAT | _O_TRUNC; tmode++; @@ -670,7 +682,7 @@ static std::FILE* OpenCFileUWP(const wchar_t* wfilename, const wchar_t* mode) } else { - Log_ErrorPrintf("Unknown mode flags: '%s'", StringUtil::WideStringToUTF8String(mode).c_str()); + Console.Error("Unknown mode flags: '%s'", StringUtil::WideStringToUTF8String(mode).c_str()); return nullptr; } } @@ -681,7 +693,7 @@ static std::FILE* OpenCFileUWP(const wchar_t* wfilename, const wchar_t* mode) if (flags & _O_APPEND && !SetFilePointerEx(hFile, LARGE_INTEGER{}, nullptr, FILE_END)) { - Log_ErrorPrintf("SetFilePointerEx() failed: %08X", GetLastError()); + Console.Error("SetFilePointerEx() failed: %08X", GetLastError()); CloseHandle(hFile); return nullptr; } @@ -715,7 +727,7 @@ std::FILE* FileSystem::OpenCFile(const char* filename, const char* mode) if (_wfopen_s(&fp, wfilename.c_str(), wmode.c_str()) != 0) { #ifdef _UWP - return OpenCFileUWP(wfilename.c_str(), wmode.c_str()); + return OpenCFileUWP(wfilename.c_str(), wmode.c_str(), FileShareMode::DenyReadWrite); #else return nullptr; #endif @@ -755,6 +767,51 @@ FileSystem::ManagedCFilePtr FileSystem::OpenManagedCFile(const char* filename, c return ManagedCFilePtr(OpenCFile(filename, mode), [](std::FILE* fp) { std::fclose(fp); }); } +std::FILE* FileSystem::OpenSharedCFile(const char* filename, const char* mode, FileShareMode share_mode) +{ +#ifdef _WIN32 + const std::wstring wfilename(StringUtil::UTF8StringToWideString(filename)); + const std::wstring wmode(StringUtil::UTF8StringToWideString(mode)); + if (wfilename.empty() || wmode.empty()) + return nullptr; + + int share_flags = 0; + switch (share_mode) + { + case FileShareMode::DenyNone: + share_flags = _SH_DENYNO; + break; + case FileShareMode::DenyRead: + share_flags = _SH_DENYRD; + break; + case FileShareMode::DenyWrite: + share_flags = _SH_DENYWR; + break; + case FileShareMode::DenyReadWrite: + default: + share_flags = _SH_DENYRW; + break; + } + + std::FILE* fp = _wfsopen(wfilename.c_str(), wmode.c_str(), share_flags); + if (fp) + return fp; + +#ifdef _UWP + return OpenCFileUWP(wfilename.c_str(), wmode.c_str(), share_mode); +#else + return nullptr; +#endif +#else + return std::fopen(filename, mode); +#endif +} + +FileSystem::ManagedCFilePtr FileSystem::OpenManagedSharedCFile(const char* filename, const char* mode, FileShareMode share_mode) +{ + return ManagedCFilePtr(OpenSharedCFile(filename, mode, share_mode), [](std::FILE* fp) { std::fclose(fp); }); +} + int FileSystem::FSeek64(std::FILE* fp, s64 offset, int whence) { #ifdef _WIN32 diff --git a/common/FileSystem.h b/common/FileSystem.h index 9314e32f48..958e09ad55 100644 --- a/common/FileSystem.h +++ b/common/FileSystem.h @@ -110,6 +110,20 @@ namespace FileSystem int OpenFDFile(const char* filename, int flags, int mode); + /// Sharing modes for OpenSharedCFile(). + enum class FileShareMode + { + DenyReadWrite, /// Exclusive access. + DenyWrite, /// Other processes can read from this file. + DenyRead, /// Other processes can write to this file. + DenyNone, /// Other processes can read and write to this file. + }; + + /// Opens a file in shareable mode (where other processes can access it concurrently). + /// Only has an effect on Windows systems. + ManagedCFilePtr OpenManagedSharedCFile(const char* filename, const char* mode, FileShareMode share_mode); + std::FILE* OpenSharedCFile(const char* filename, const char* mode, FileShareMode share_mode); + std::optional> ReadBinaryFile(const char* filename); std::optional> ReadBinaryFile(std::FILE* fp); std::optional ReadFileToString(const char* filename);