mirror of
https://github.com/Lime3DS/Lime3DS.git
synced 2026-03-27 13:50:22 -06:00
file_util: Fix file behaviour on Windows (#1841)
This commit is contained in:
parent
ced1ec0112
commit
1e0df67cc4
@ -211,10 +211,33 @@ bool Delete(const std::string& filename) {
|
||||
}
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) {
|
||||
LOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg());
|
||||
return false;
|
||||
// On windows, if we delete a file with an open handle (pending to be deleted) and
|
||||
// then try to open the same file name again, it will fail. On linux this doesn't happen
|
||||
// as the new file will be a different inode, even if it has the same file name.
|
||||
// The 3ds is linux-like, so to emulate this behaviour we need to rename the file
|
||||
// first to an unique name then mark it for deletion. This way we can open new files
|
||||
// with the same name. Once all handles of the old file are closed, the old file will be
|
||||
// finally deleted.
|
||||
static std::atomic<uint64_t> counter{0};
|
||||
|
||||
const std::wstring wfilename = Common::UTF8ToUTF16W(filename);
|
||||
const DWORD pid = GetCurrentProcessId();
|
||||
const uint64_t id = counter++;
|
||||
|
||||
std::wstring deleted_path =
|
||||
wfilename + L".deleted." + std::to_wstring(pid) + L"." + std::to_wstring(id);
|
||||
|
||||
// Rename first
|
||||
if (MoveFileExW(wfilename.c_str(), deleted_path.c_str(), MOVEFILE_REPLACE_EXISTING)) {
|
||||
// Then mark file for deletion
|
||||
DeleteFileW(deleted_path.c_str());
|
||||
return true;
|
||||
}
|
||||
|
||||
LOG_ERROR(Common_Filesystem, "Rename to deleted path failed on {}: {}", filename,
|
||||
GetLastErrorMsg());
|
||||
|
||||
return false;
|
||||
#elif defined(ANDROID) && !defined(HAVE_LIBRETRO_VFS)
|
||||
if (!AndroidStorage::DeleteDocument(filename)) {
|
||||
LOG_ERROR(Common_Filesystem, "unlink failed on {}", filename);
|
||||
@ -344,9 +367,10 @@ bool DeleteDir(const std::string& filename) {
|
||||
bool Rename(const std::string& srcFullPath, const std::string& destFullPath) {
|
||||
LOG_TRACE(Common_Filesystem, "{} --> {}", srcFullPath, destFullPath);
|
||||
#ifdef _WIN32
|
||||
if (_wrename(Common::UTF8ToUTF16W(srcFullPath).c_str(),
|
||||
Common::UTF8ToUTF16W(destFullPath).c_str()) == 0)
|
||||
if (MoveFileExW(Common::UTF8ToUTF16W(srcFullPath).c_str(),
|
||||
Common::UTF8ToUTF16W(destFullPath).c_str(), MOVEFILE_REPLACE_EXISTING)) {
|
||||
return true;
|
||||
}
|
||||
#elif defined(ANDROID) && !defined(HAVE_LIBRETRO_VFS)
|
||||
// srcFullPath and destFullPath are relative to the user directory
|
||||
if (AndroidStorage::GetBuildFlavor() == AndroidStorage::AndroidBuildFlavors::GOOGLEPLAY) {
|
||||
@ -1167,11 +1191,46 @@ bool IOFile::Open() {
|
||||
Close();
|
||||
|
||||
#ifdef _WIN32
|
||||
if (flags == 0) {
|
||||
flags = _SH_DENYNO;
|
||||
// Open with FILE_SHARE_READ, FILE_SHARE_WRITE and FILE_SHARE_DELETE
|
||||
// flags. This mimics linux behaviour as much as possible, which
|
||||
// the 3DS also does.
|
||||
|
||||
const std::wstring wfilename = Common::UTF8ToUTF16W(filename);
|
||||
|
||||
DWORD access = 0;
|
||||
DWORD creation = OPEN_EXISTING;
|
||||
|
||||
if (openmode.find("r") != std::string::npos)
|
||||
access |= GENERIC_READ;
|
||||
if (openmode.find("w") != std::string::npos) {
|
||||
access |= GENERIC_WRITE;
|
||||
creation = CREATE_ALWAYS;
|
||||
}
|
||||
m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(),
|
||||
Common::UTF8ToUTF16W(openmode).c_str(), flags);
|
||||
if (openmode.find("a") != std::string::npos) {
|
||||
access |= FILE_APPEND_DATA;
|
||||
creation = OPEN_ALWAYS;
|
||||
}
|
||||
if (openmode.find("+") != std::string::npos) {
|
||||
access |= GENERIC_READ | GENERIC_WRITE;
|
||||
}
|
||||
|
||||
HANDLE h = CreateFileW(wfilename.c_str(), access,
|
||||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr,
|
||||
creation, FILE_ATTRIBUTE_NORMAL, nullptr);
|
||||
|
||||
if (h == INVALID_HANDLE_VALUE) {
|
||||
m_good = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
int fd = _open_osfhandle(reinterpret_cast<intptr_t>(h), 0);
|
||||
if (fd == -1) {
|
||||
CloseHandle(h);
|
||||
m_good = false;
|
||||
return m_good;
|
||||
}
|
||||
|
||||
m_file = _fdopen(fd, openmode.c_str());
|
||||
m_good = m_file != nullptr;
|
||||
|
||||
#elif defined(ANDROID) && !defined(HAVE_LIBRETRO_VFS)
|
||||
|
||||
@ -469,7 +469,6 @@ System::ResultStatus System::Load(Frontend::EmuWindow& emu_window, const std::st
|
||||
m_emu_window = &emu_window;
|
||||
m_secondary_window = secondary_window;
|
||||
m_filepath = filepath;
|
||||
self_delete_pending = false;
|
||||
|
||||
// Reset counters and set time origin to current frame
|
||||
[[maybe_unused]] const PerfStats::Results result = GetAndResetPerfStats();
|
||||
@ -721,10 +720,6 @@ void System::Shutdown(bool is_deserializing) {
|
||||
|
||||
memory.reset();
|
||||
|
||||
if (self_delete_pending)
|
||||
FileUtil::Delete(m_filepath);
|
||||
self_delete_pending = false;
|
||||
|
||||
LOG_DEBUG(Core, "Shutdown OK");
|
||||
}
|
||||
|
||||
|
||||
@ -366,15 +366,6 @@ public:
|
||||
|
||||
bool LoadStateBuffer(std::vector<u8> buffer);
|
||||
|
||||
/// Self delete ncch
|
||||
bool SetSelfDelete(const std::string& file) {
|
||||
if (m_filepath == file) {
|
||||
self_delete_pending = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/// Applies any changes to settings to this core instance.
|
||||
void ApplySettings();
|
||||
|
||||
@ -480,7 +471,6 @@ private:
|
||||
std::string m_chainloadpath;
|
||||
std::optional<u8> m_mem_mode;
|
||||
u64 title_id;
|
||||
bool self_delete_pending;
|
||||
|
||||
std::mutex signal_mutex;
|
||||
Signal current_signal;
|
||||
|
||||
@ -916,15 +916,7 @@ bool CIAFile::Close() {
|
||||
if (abort) {
|
||||
break;
|
||||
}
|
||||
|
||||
// If the file to delete is the current launched rom, signal the system to delete
|
||||
// the current rom instead of deleting it now, once all the handles to the file
|
||||
// are closed.
|
||||
std::string to_delete =
|
||||
GetTitleContentPath(media_type, old_tmd.GetTitleID(), old_index);
|
||||
if (!system.IsPoweredOn() || !system.SetSelfDelete(to_delete)) {
|
||||
FileUtil::Delete(to_delete);
|
||||
}
|
||||
FileUtil::Delete(GetTitleContentPath(media_type, old_tmd.GetTitleID(), old_index));
|
||||
}
|
||||
|
||||
FileUtil::Delete(old_tmd_path);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user