diff --git a/Utilities/File.cpp b/Utilities/File.cpp index 210faafd41..b616ff9ff2 100644 --- a/Utilities/File.cpp +++ b/Utilities/File.cpp @@ -510,7 +510,7 @@ bool fs::remove_dir(const std::string& path) #endif } -bool fs::rename(const std::string& from, const std::string& to) +bool fs::rename(const std::string& from, const std::string& to, bool overwrite) { const auto device = get_virtual_device(from); @@ -525,14 +525,43 @@ bool fs::rename(const std::string& from, const std::string& to) } #ifdef _WIN32 - if (!MoveFileW(to_wchar(from).get(), to_wchar(to).get())) + const auto ws1 = to_wchar(from); + const auto ws2 = to_wchar(to); + + if (!MoveFileExW(ws1.get(), ws2.get(), overwrite ? MOVEFILE_REPLACE_EXISTING : 0)) { - g_tls_error = to_error(GetLastError()); + DWORD error1 = GetLastError(); + + if (overwrite && error1 == ERROR_ACCESS_DENIED && is_dir(from) && is_dir(to)) + { + if (RemoveDirectoryW(ws2.get())) + { + if (MoveFileW(ws1.get(), ws2.get())) + { + return true; + } + + error1 = GetLastError(); + CreateDirectoryW(ws2.get(), NULL); // TODO + } + else + { + error1 = GetLastError(); + } + } + + g_tls_error = to_error(error1); return false; } return true; #else + if (!overwrite && exists(to)) + { + g_tls_error = fs::error::exist; + return false; + } + if (::rename(from.c_str(), to.c_str()) != 0) { g_tls_error = to_error(errno); diff --git a/Utilities/File.h b/Utilities/File.h index 24d200c8e0..c7c6148de9 100644 --- a/Utilities/File.h +++ b/Utilities/File.h @@ -159,7 +159,7 @@ namespace fs bool create_path(const std::string& path); // Rename (move) file or directory - bool rename(const std::string& from, const std::string& to); + bool rename(const std::string& from, const std::string& to, bool overwrite); // Copy file contents bool copy_file(const std::string& from, const std::string& to, bool overwrite); diff --git a/rpcs3/Emu/Cell/Modules/cellGame.cpp b/rpcs3/Emu/Cell/Modules/cellGame.cpp index 655e42735c..1f98fa4a35 100644 --- a/rpcs3/Emu/Cell/Modules/cellGame.cpp +++ b/rpcs3/Emu/Cell/Modules/cellGame.cpp @@ -393,7 +393,7 @@ error_code cellGameContentPermit(vm::ptr contentInfoPa fmt::throw_exception("cellGameContentPermit(): epic fail: directory '%s' already exists", dir); } - if (fs::rename(prm->temp, vdir)) + if (fs::rename(prm->temp, vdir, false)) { cellGame.success("cellGameContentPermit(): directory '%s' has been created", dir); } diff --git a/rpcs3/Emu/Cell/lv2/sys_fs.cpp b/rpcs3/Emu/Cell/lv2/sys_fs.cpp index f64698e987..4fc07f18cf 100644 --- a/rpcs3/Emu/Cell/lv2/sys_fs.cpp +++ b/rpcs3/Emu/Cell/lv2/sys_fs.cpp @@ -580,9 +580,16 @@ error_code sys_fs_rename(vm::cptr from, vm::cptr to) { sys_fs.warning("sys_fs_rename(from=%s, to=%s)", from, to); - if (!fs::rename(vfs::get(from.get_ptr()), vfs::get(to.get_ptr()))) + if (!fs::rename(vfs::get(from.get_ptr()), vfs::get(to.get_ptr()), false)) { - return {CELL_ENOENT, from}; // ??? + switch (auto error = fs::g_tls_error) + { + case fs::error::noent: return {CELL_ENOENT, from}; + case fs::error::exist: return {CELL_EEXIST, to}; + default: sys_fs.error("sys_fs_rename(): unknown error %s", error); + } + + return {CELL_EIO, from}; // ??? } sys_fs.notice("sys_fs_rename(): %s renamed to %s", from, to); diff --git a/rpcs3/Emu/System.cpp b/rpcs3/Emu/System.cpp index 8b66a661ad..5367552ad6 100644 --- a/rpcs3/Emu/System.cpp +++ b/rpcs3/Emu/System.cpp @@ -376,7 +376,7 @@ void Emulator::Load(bool add_only) LOG_ERROR(LOADER, "Disc game %s found at invalid location /dev_hdd0/game/", m_title_id); // Move and retry from correct location - if (fs::rename(elf_dir + "/../../", hdd0_disc + elf_dir.substr(hdd0_game.size()) + "/../../")) + if (fs::rename(elf_dir + "/../../", hdd0_disc + elf_dir.substr(hdd0_game.size()) + "/../../", false)) { LOG_SUCCESS(LOADER, "Disc game %s moved to special location /dev_hdd0/disc/", m_title_id); return SetPath(hdd0_disc + m_path.substr(hdd0_game.size())), Load();