diff --git a/Source/Core/Common/FileUtil.cpp b/Source/Core/Common/FileUtil.cpp index dfb82712d7..3944005d6c 100644 --- a/Source/Core/Common/FileUtil.cpp +++ b/Source/Core/Common/FileUtil.cpp @@ -6,13 +6,17 @@ #include #include #include +#include #include #include #include +#include #include #include +#include #include #include +#include #include #include @@ -29,11 +33,10 @@ #include "Common/StringUtil.h" #ifdef _WIN32 -#include +#include #include #include // for GetSaveFileName #include // getcwd -#include #include #include // guid stuff #include @@ -57,13 +60,8 @@ #include "jni/AndroidCommon/AndroidCommon.h" #endif -#ifndef S_ISDIR -#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) -#endif +namespace fs = std::filesystem; -// This namespace has various generic functions related to files and paths. -// The code still needs a ton of cleanup. -// REMEMBER: strdup considered harmful! namespace File { #ifdef ANDROID @@ -82,16 +80,6 @@ static DolSecTranslocateIsTranslocatedURL s_is_translocated_url; static DolSecTranslocateCreateOriginalPathForURL s_create_orig_path; #endif -#ifdef _WIN32 -FileInfo::FileInfo(const std::string& path) -{ - m_exists = _tstat64(UTF8ToTStr(path).c_str(), &m_stat) == 0; -} - -FileInfo::FileInfo(const char* path) : FileInfo(std::string(path)) -{ -} -#else FileInfo::FileInfo(const std::string& path) : FileInfo(path.c_str()) { } @@ -100,27 +88,24 @@ FileInfo::FileInfo(const char* path) { #ifdef ANDROID if (IsPathAndroidContent(path)) - AndroidContentInit(path); + { + const jlong result = GetAndroidContentSizeAndIsDirectory(path); + m_status.type((result == -2) ? fs::file_type::directory : fs::file_type::regular); + m_size = (result >= 0) ? result : 0; + m_exists = result != -1; + } else #endif - m_exists = stat(path, &m_stat) == 0; + { + const auto fs_path = StringToPath(path); + std::error_code error; + m_status = fs::status(fs_path, error); + m_size = fs::file_size(fs_path, error); + if (error) + m_size = 0; + m_exists = fs::exists(m_status); + } } -#endif - -FileInfo::FileInfo(int fd) -{ - m_exists = fstat(fd, &m_stat) == 0; -} - -#ifdef ANDROID -void FileInfo::AndroidContentInit(const std::string& path) -{ - const jlong result = GetAndroidContentSizeAndIsDirectory(path); - m_exists = result != -1; - m_stat.st_mode = result == -2 ? S_IFDIR : S_IFREG; - m_stat.st_size = result >= 0 ? result : 0; -} -#endif bool FileInfo::Exists() const { @@ -129,17 +114,19 @@ bool FileInfo::Exists() const bool FileInfo::IsDirectory() const { - return m_exists ? S_ISDIR(m_stat.st_mode) : false; + return fs::is_directory(m_status); } bool FileInfo::IsFile() const { - return m_exists ? !S_ISDIR(m_stat.st_mode) : false; + return Exists() ? !fs::is_directory(m_status) : false; } u64 FileInfo::GetSize() const { - return IsFile() ? m_stat.st_size : 0; + if (!IsFile()) + return 0; + return m_size; } // Returns true if the path exists @@ -151,11 +138,7 @@ bool Exists(const std::string& path) // Returns true if the path exists and is a directory bool IsDirectory(const std::string& path) { -#ifdef _WIN32 - return PathIsDirectory(UTF8ToWString(path).c_str()); -#else return FileInfo(path).IsDirectory(); -#endif } // Returns true if the path exists and is a file @@ -168,50 +151,44 @@ bool IsFile(const std::string& path) // Doesn't supports deleting a directory bool Delete(const std::string& filename, IfAbsentBehavior behavior) { - DEBUG_LOG_FMT(COMMON, "Delete: file {}", filename); + DEBUG_LOG_FMT(COMMON, "{}: file {}", __func__, filename); #ifdef ANDROID if (filename.starts_with("content://")) { const bool success = DeleteAndroidContent(filename); if (!success) - WARN_LOG_FMT(COMMON, "Delete failed on {}", filename); + WARN_LOG_FMT(COMMON, "{} failed on {}", __func__, filename); return success; } #endif - const FileInfo file_info(filename); + auto native_path = StringToPath(filename); + std::error_code error; + auto status = fs::status(native_path, error); // Return true because we care about the file not being there, not the actual delete. - if (!file_info.Exists()) + if (!fs::exists(status)) { if (behavior == IfAbsentBehavior::ConsoleWarning) { - WARN_LOG_FMT(COMMON, "Delete: {} does not exist", filename); + WARN_LOG_FMT(COMMON, "{}: {} does not exist", __func__, filename); } return true; } - // We can't delete a directory - if (file_info.IsDirectory()) + // fs::remove can only delete an empty directory. Legacy dolphin behavior is just to bail. + if (fs::is_directory(status)) { - WARN_LOG_FMT(COMMON, "Delete failed: {} is a directory", filename); + WARN_LOG_FMT(COMMON, "{} failed: {} is a directory", __func__, filename); return false; } -#ifdef _WIN32 - if (!DeleteFile(UTF8ToTStr(filename).c_str())) + if (!fs::remove(native_path, error)) { - WARN_LOG_FMT(COMMON, "Delete: DeleteFile failed on {}: {}", filename, GetLastErrorString()); + WARN_LOG_FMT(COMMON, "{}: failed on {}: {}", __func__, filename, error.message()); return false; } -#else - if (unlink(filename.c_str()) == -1) - { - WARN_LOG_FMT(COMMON, "Delete: unlink failed on {}: {}", filename, LastStrerrorString()); - return false; - } -#endif return true; } @@ -219,131 +196,82 @@ bool Delete(const std::string& filename, IfAbsentBehavior behavior) // Returns true if successful, or path already exists. bool CreateDir(const std::string& path) { - DEBUG_LOG_FMT(COMMON, "CreateDir: directory {}", path); -#ifdef _WIN32 - if (::CreateDirectory(UTF8ToTStr(path).c_str(), nullptr)) - return true; - const DWORD error = GetLastError(); - if (error == ERROR_ALREADY_EXISTS) - { - WARN_LOG_FMT(COMMON, "CreateDir: CreateDirectory failed on {}: already exists", path); - return true; - } - ERROR_LOG_FMT(COMMON, "CreateDir: CreateDirectory failed on {}: {}", path, error); - return false; -#else - if (mkdir(path.c_str(), 0755) == 0) - return true; + DEBUG_LOG_FMT(COMMON, "{}: directory {}", __func__, path); - const int err = errno; - - if (err == EEXIST) - { - WARN_LOG_FMT(COMMON, "CreateDir: mkdir failed on {}: already exists", path); - return true; - } - - ERROR_LOG_FMT(COMMON, "CreateDir: mkdir failed on {}: {}", path, strerror(err)); - return false; -#endif + std::error_code error; + auto native_path = StringToPath(path); + bool success = fs::create_directory(native_path, error); + // If the path was not created, check if it was a pre-existing directory + if (!success && fs::is_directory(native_path)) + success = true; + if (!success) + ERROR_LOG_FMT(COMMON, "{}: failed on {}: {}", __func__, path, error.message()); + return success; } // Creates the full path of fullPath returns true on success bool CreateFullPath(const std::string& fullPath) { - int panicCounter = 100; - DEBUG_LOG_FMT(COMMON, "CreateFullPath: path {}", fullPath); + DEBUG_LOG_FMT(COMMON, "{}: path {}", __func__, fullPath); - if (Exists(fullPath)) - { - DEBUG_LOG_FMT(COMMON, "CreateFullPath: path exists {}", fullPath); - return true; - } - - size_t position = 0; - while (true) - { - // Find next sub path - position = fullPath.find(DIR_SEP_CHR, position); - - // we're done, yay! - if (position == fullPath.npos) - return true; - - // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") - std::string const subPath(fullPath.substr(0, position + 1)); - if (!IsDirectory(subPath)) - File::CreateDir(subPath); - - // A safety check - panicCounter--; - if (panicCounter <= 0) - { - ERROR_LOG_FMT(COMMON, "CreateFullPath: directory structure is too deep"); - return false; - } - position++; - } + std::error_code error; + auto native_path = StringToPath(fullPath); + bool success = fs::create_directories(native_path, error); + // If the path was not created, check if it was a pre-existing directory + if (!success && fs::is_directory(native_path)) + success = true; + if (!success) + ERROR_LOG_FMT(COMMON, "{}: failed on {}: {}", __func__, fullPath, error.message()); + return success; } // Deletes a directory filename, returns true on success bool DeleteDir(const std::string& filename, IfAbsentBehavior behavior) { - DEBUG_LOG_FMT(COMMON, "DeleteDir: directory {}", filename); + DEBUG_LOG_FMT(COMMON, "{}: directory {}", __func__, filename); + + auto native_path = StringToPath(filename); + std::error_code error; + auto status = fs::status(native_path, error); // Return true because we care about the directory not being there, not the actual delete. - if (!File::Exists(filename)) + if (!fs::exists(status)) { if (behavior == IfAbsentBehavior::ConsoleWarning) { - WARN_LOG_FMT(COMMON, "DeleteDir: {} does not exist", filename); + WARN_LOG_FMT(COMMON, "{}: {} does not exist", __func__, filename); } return true; } // check if a directory - if (!IsDirectory(filename)) + if (!fs::is_directory(status)) { - ERROR_LOG_FMT(COMMON, "DeleteDir: Not a directory {}", filename); + ERROR_LOG_FMT(COMMON, "{}: Not a directory {}", __func__, filename); return false; } -#ifdef _WIN32 - if (::RemoveDirectory(UTF8ToTStr(filename).c_str())) - return true; - ERROR_LOG_FMT(COMMON, "DeleteDir: RemoveDirectory failed on {}: {}", filename, - GetLastErrorString()); -#else - if (rmdir(filename.c_str()) == 0) - return true; - ERROR_LOG_FMT(COMMON, "DeleteDir: rmdir failed on {}: {}", filename, LastStrerrorString()); -#endif + if (!fs::remove(native_path, error)) + { + WARN_LOG_FMT(COMMON, "{}: failed on {}: {}", __func__, filename, error.message()); + return false; + } - return false; + return true; } // renames file srcFilename to destFilename, returns true on success bool Rename(const std::string& srcFilename, const std::string& destFilename) { - DEBUG_LOG_FMT(COMMON, "Rename: {} --> {}", srcFilename, destFilename); -#ifdef _WIN32 + DEBUG_LOG_FMT(COMMON, "{}: {} --> {}", __func__, srcFilename, destFilename); std::error_code error; - std::filesystem::rename(UTF8ToWString(srcFilename), UTF8ToWString(destFilename), error); + std::filesystem::rename(StringToPath(srcFilename), StringToPath(destFilename), error); if (error) { - ERROR_LOG_FMT(COMMON, "Rename failed: {} --> {}: {}", srcFilename, destFilename, + ERROR_LOG_FMT(COMMON, "{} failed: {} --> {}: {}", __func__, srcFilename, destFilename, error.message()); } - const bool success = !error; -#else - const bool success = rename(srcFilename.c_str(), destFilename.c_str()) == 0; - if (!success) - { - ERROR_LOG_FMT(COMMON, "Rename failed {} --> {}: {}", srcFilename, destFilename, - LastStrerrorString()); - } -#endif - return success; + return !error; } #ifndef _WIN32 @@ -363,10 +291,14 @@ bool RenameSync(const std::string& srcFilename, const std::string& destFilename) if (!Rename(srcFilename, destFilename)) return false; #ifdef _WIN32 - int fd = _topen(UTF8ToTStr(srcFilename).c_str(), _O_RDONLY); - if (fd != -1) + int fd = -1; + // XXX is this really needed? + errno_t err = _wsopen_s(&fd, UTF8ToWString(srcFilename).c_str(), _O_RDONLY, _SH_DENYNO, + _S_IREAD | _S_IWRITE); + if (!err && fd >= 0) { - _commit(fd); + if (_commit(fd) != 0) + ERROR_LOG_FMT(COMMON, "{} sync failed on {}: {}", __func__, srcFilename, err); close(fd); } #else @@ -384,30 +316,18 @@ bool RenameSync(const std::string& srcFilename, const std::string& destFilename) // copies file source_path to destination_path, returns true on success bool Copy(const std::string& source_path, const std::string& destination_path) { - DEBUG_LOG_FMT(COMMON, "Copy: {} --> {}", source_path, destination_path); -#ifdef _WIN32 - if (CopyFile(UTF8ToTStr(source_path).c_str(), UTF8ToTStr(destination_path).c_str(), FALSE)) - return true; + DEBUG_LOG_FMT(COMMON, "{}: {} --> {}", __func__, source_path, destination_path); - ERROR_LOG_FMT(COMMON, "Copy: failed {} --> {}: {}", source_path, destination_path, - GetLastErrorString()); - return false; -#else - std::ifstream source{source_path, std::ios::binary}; - std::ofstream destination{destination_path, std::ios::binary}; - - // Only attempt to write with << if there is actually something in the file - if (source.peek() != std::ifstream::traits_type::eof()) + auto src_path = StringToPath(source_path); + auto dst_path = StringToPath(destination_path); + std::error_code error; + bool copied = fs::copy_file(src_path, dst_path, fs::copy_options::overwrite_existing, error); + if (!copied) { - destination << source.rdbuf(); - return source.good() && destination.good(); + ERROR_LOG_FMT(COMMON, "{}: failed {} --> {}: {}", __func__, source_path, destination_path, + error.message()); } - else - { - // We can't use source.good() here because eofbit will be set, so check for the other bits. - return !source.fail() && !source.bad() && destination.good(); - } -#endif + return copied; } // Returns the size of a file (or returns 0 if the path isn't a file that exists) @@ -416,12 +336,6 @@ u64 GetSize(const std::string& path) return FileInfo(path).GetSize(); } -// Overloaded GetSize, accepts file descriptor -u64 GetSize(const int fd) -{ - return FileInfo(fd).GetSize(); -} - // Overloaded GetSize, accepts FILE* u64 GetSize(FILE* f) { @@ -457,89 +371,25 @@ bool CreateEmptyFile(const std::string& filename) return true; } -// Recursive or non-recursive list of files and directories under directory. -FSTEntry ScanDirectoryTree(std::string directory, bool recursive) +#ifdef ANDROID +static FSTEntry ScanDirectoryTreeAndroidContent(std::string directory, bool recursive) { -#ifdef _WIN32 - if (!directory.empty() && (directory.back() == '/' || directory.back() == '\\')) - directory.pop_back(); -#else - if (!directory.empty() && directory.back() == '/') - directory.pop_back(); -#endif - - DEBUG_LOG_FMT(COMMON, "ScanDirectoryTree: directory {}", directory); FSTEntry parent_entry; parent_entry.physicalName = directory; parent_entry.isDirectory = true; parent_entry.size = 0; -#ifdef _WIN32 - // Find the first file in the directory. - WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFile(UTF8ToTStr(directory + "\\*").c_str(), &ffd); - if (hFind == INVALID_HANDLE_VALUE) + for (const auto& child_name : GetAndroidContentChildNames(directory)) { - FindClose(hFind); - return parent_entry; - } - // Windows loop - do - { - const std::string virtual_name(TStrToUTF8(ffd.cFileName)); -#else - DIR* dirp = nullptr; - -#ifdef ANDROID - std::vector child_names; - if (IsPathAndroidContent(directory)) - { - child_names = GetAndroidContentChildNames(directory); - } - else -#endif - { - dirp = opendir(directory.c_str()); - if (!dirp) - return parent_entry; - } - -#ifdef ANDROID - auto it = child_names.cbegin(); -#endif - - // non Windows loop - while (true) - { - std::string virtual_name; - -#ifdef ANDROID - if (!dirp) - { - if (it == child_names.cend()) - break; - virtual_name = *it; - ++it; - } - else -#endif - { - dirent* result = readdir(dirp); - if (!result) - break; - virtual_name = result->d_name; - } -#endif - if (virtual_name == "." || virtual_name == "..") - continue; - auto physical_name = directory + DIR_SEP + virtual_name; - FSTEntry entry; + const auto physical_name = directory + DIR_SEP + child_name; const FileInfo file_info(physical_name); + FSTEntry entry; + entry.isDirectory = file_info.IsDirectory(); if (entry.isDirectory) { if (recursive) - entry = ScanDirectoryTree(physical_name, true); + entry = ScanDirectoryTreeAndroidContent(physical_name, true); else entry.size = 0; parent_entry.size += entry.size; @@ -548,177 +398,173 @@ FSTEntry ScanDirectoryTree(std::string directory, bool recursive) { entry.size = file_info.GetSize(); } - entry.virtualName = virtual_name; + entry.virtualName = child_name; entry.physicalName = physical_name; ++parent_entry.size; - // Push into the tree parent_entry.children.push_back(entry); -#ifdef _WIN32 - } while (FindNextFile(hFind, &ffd) != 0); - FindClose(hFind); -#else } - if (dirp) - closedir(dirp); + + return parent_entry; +} #endif +// Recursive or non-recursive list of files and directories under directory. +FSTEntry ScanDirectoryTree(std::string directory, bool recursive) +{ + DEBUG_LOG_FMT(COMMON, "{}: directory {}", __func__, directory); + +#ifdef ANDROID + if (IsPathAndroidContent(directory)) + return ScanDirectoryTreeAndroidContent(directory, recursive); +#endif + + auto path_to_physical_name = [](const fs::path& path) { +#ifdef _WIN32 + // TODO Ideally this would not be needed - dolphin really should not have code directly mucking + // about with directory separators (for host paths - emulated paths may require it) and instead + // use fs::path to interact with them. + auto wpath = path.wstring(); + std::replace(wpath.begin(), wpath.end(), L'\\', L'/'); + return WStringToUTF8(wpath); +#else + return PathToString(path); +#endif + }; + + auto dirent_to_fstent = [&](const fs::directory_entry& entry) { + return FSTEntry{ + .isDirectory = entry.is_directory(), + .size = entry.is_directory() ? 0 : entry.file_size(), + .physicalName = path_to_physical_name(entry.path()), + .virtualName = PathToString(entry.path().filename()), + }; + }; + + auto calc_dir_size = [](FSTEntry* dir) { + dir->size += dir->children.size(); + for (auto& child : dir->children) + if (child.isDirectory) + dir->size += child.size; + }; + + const auto directory_path = StringToPath(directory); + + FSTEntry parent_entry; + parent_entry.physicalName = path_to_physical_name(directory_path); + parent_entry.isDirectory = fs::is_directory(directory_path); + parent_entry.size = 0; + + std::error_code error; + if (recursive) + { + int prev_depth = 0; + std::stack dir_fsts; + dir_fsts.push(&parent_entry); + for (auto it = fs::recursive_directory_iterator(directory_path, error); + it != fs::recursive_directory_iterator(); it.increment(error)) + { + const int cur_depth = it.depth(); + if (cur_depth > prev_depth) + { + dir_fsts.push(&dir_fsts.top()->children.back()); + } + else if (cur_depth < prev_depth) + { + while (dir_fsts.size() - 1 != cur_depth) + { + calc_dir_size(dir_fsts.top()); + dir_fsts.pop(); + } + } + dir_fsts.top()->children.emplace_back(dirent_to_fstent(*it)); + prev_depth = cur_depth; + } + while (dir_fsts.size()) + { + calc_dir_size(dir_fsts.top()); + dir_fsts.pop(); + } + } + else + { + for (auto it = fs::directory_iterator(directory_path, error); it != fs::directory_iterator(); + it.increment(error)) + { + parent_entry.children.emplace_back(dirent_to_fstent(*it)); + } + calc_dir_size(&parent_entry); + } + + if (error) + { + // NOTE Possibly partial file list still returned + ERROR_LOG_FMT(COMMON, "{} error on {}: {}", __func__, directory, error.message()); + } + return parent_entry; } // Deletes the given directory and anything under it. Returns true on success. bool DeleteDirRecursively(const std::string& directory) { - DEBUG_LOG_FMT(COMMON, "DeleteDirRecursively: {}", directory); - bool success = true; - -#ifdef _WIN32 - // Find the first file in the directory. - WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFile(UTF8ToTStr(directory + "\\*").c_str(), &ffd); - - if (hFind == INVALID_HANDLE_VALUE) - { - FindClose(hFind); - return false; - } - - // Windows loop - do - { - const std::string virtualName(TStrToUTF8(ffd.cFileName)); -#else - DIR* dirp = opendir(directory.c_str()); - if (!dirp) - return false; - - // non Windows loop - while (dirent* result = readdir(dirp)) - { - const std::string virtualName = result->d_name; -#endif - - // check for "." and ".." - if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || - ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) - continue; - - std::string newPath = directory + DIR_SEP_CHR + virtualName; - if (IsDirectory(newPath)) - { - if (!DeleteDirRecursively(newPath)) - { - success = false; - break; - } - } - else - { - if (!File::Delete(newPath)) - { - success = false; - break; - } - } - -#ifdef _WIN32 - } while (FindNextFile(hFind, &ffd) != 0); - FindClose(hFind); -#else - } - closedir(dirp); -#endif - if (success) - File::DeleteDir(directory); + DEBUG_LOG_FMT(COMMON, "{}: {}", __func__, directory); + std::error_code error; + const std::uintmax_t num_removed = std::filesystem::remove_all(StringToPath(directory), error); + const bool success = num_removed != 0 && !error; + if (!success) + ERROR_LOG_FMT(COMMON, "{}: {} failed {}", __func__, directory, error.message()); return success; } // Create directory and copy contents (optionally overwrites existing files) bool CopyDir(const std::string& source_path, const std::string& dest_path, const bool destructive) { - if (source_path == dest_path) + auto src_path = StringToPath(source_path); + auto dst_path = StringToPath(dest_path); + if (fs::equivalent(src_path, dst_path)) return true; - if (!Exists(source_path)) - return false; - // Shouldn't be used to short circuit operations after an earlier failure - bool everything_copied = true; + DEBUG_LOG_FMT(COMMON, "{}: {} --> {}", __func__, source_path, dest_path); - if (!Exists(dest_path)) - everything_copied = File::CreateFullPath(dest_path) && everything_copied; - -#ifdef _WIN32 - WIN32_FIND_DATA ffd; - HANDLE hFind = FindFirstFile(UTF8ToTStr(source_path + "\\*").c_str(), &ffd); - - if (hFind == INVALID_HANDLE_VALUE) + auto options = fs::copy_options::recursive; + if (destructive) + options |= fs::copy_options::overwrite_existing; + std::error_code error; + bool copied = fs::copy_file(src_path, dst_path, options, error); + if (!copied) { - FindClose(hFind); - return false; + ERROR_LOG_FMT(COMMON, "{}: failed {} --> {}: {}", __func__, source_path, dest_path, + error.message()); } - - do - { - const std::string virtualName(TStrToUTF8(ffd.cFileName)); -#else - DIR* dirp = opendir(source_path.c_str()); - if (!dirp) - return false; - - while (dirent* result = readdir(dirp)) - { - const std::string virtualName(result->d_name); -#endif - // check for "." and ".." - if (virtualName == "." || virtualName == "..") - continue; - - const std::string source = source_path + DIR_SEP + virtualName; - const std::string dest = dest_path + DIR_SEP + virtualName; - if (IsDirectory(source)) - { - if (!Exists(dest)) - File::CreateFullPath(dest + DIR_SEP); - everything_copied = CopyDir(source, dest, destructive) && everything_copied; - } - else if (!destructive && !Exists(dest)) - { - everything_copied = Copy(source, dest) && everything_copied; - } - else if (destructive) - { - everything_copied = Rename(source, dest) && everything_copied; - } -#ifdef _WIN32 - } while (FindNextFile(hFind, &ffd) != 0); - FindClose(hFind); -#else - } - closedir(dirp); -#endif - return everything_copied; + return copied; } // Returns the current directory std::string GetCurrentDir() { - // Get the current working directory (getcwd uses malloc) - char* dir = __getcwd(nullptr, 0); - if (!dir) + std::error_code error; + auto directory = PathToString(fs::current_path(error)); + if (error) { - ERROR_LOG_FMT(COMMON, "GetCurrentDirectory failed: {}", LastStrerrorString()); - return ""; + ERROR_LOG_FMT(COMMON, "{} failed: {}", __func__, error.message()); + return {}; } - std::string strDir = dir; - free(dir); - return strDir; + return directory; } // Sets the current directory to the given directory bool SetCurrentDir(const std::string& directory) { - return __chdir(directory.c_str()) == 0; + std::error_code error; + fs::current_path(StringToPath(directory), error); + if (error) + { + ERROR_LOG_FMT(COMMON, "{} failed: {}", __func__, error.message()); + return false; + } + return true; } std::string CreateTempDir() @@ -754,18 +600,10 @@ std::string CreateTempDir() std::string GetTempFilenameForAtomicWrite(std::string path) { -#ifdef _WIN32 - std::unique_ptr absbuf{ - _tfullpath(nullptr, UTF8ToTStr(path).c_str(), 0), std::free}; - if (absbuf != nullptr) - { - path = TStrToUTF8(absbuf.get()); - } -#else - char absbuf[PATH_MAX]; - if (realpath(path.c_str(), absbuf) != nullptr) - path = absbuf; -#endif + std::error_code error; + auto absolute_path = fs::absolute(StringToPath(path), error); + if (!error) + path = PathToString(absolute_path); return std::move(path) + ".xxx"; } @@ -818,48 +656,32 @@ std::string GetBundleDirectory() std::string GetExePath() { - static const std::string dolphin_path = [] { - std::string result; #ifdef _WIN32 - auto dolphin_exe_path = GetModuleName(nullptr); - if (dolphin_exe_path) - { - std::unique_ptr dolphin_exe_expanded_path{ - _tfullpath(nullptr, dolphin_exe_path->c_str(), 0), std::free}; - if (dolphin_exe_expanded_path) - { - result = TStrToUTF8(dolphin_exe_expanded_path.get()); - } - else - { - result = TStrToUTF8(*dolphin_exe_path); - } - } + auto exe_path = GetModuleName(nullptr); + if (!exe_path) + return {}; + std::error_code error; + auto exe_path_absolute = fs::absolute(exe_path.value(), error); + if (error) + return {}; + return PathToString(exe_path_absolute); #elif defined(__APPLE__) - result = GetBundleDirectory(); + return GetBundleDirectory(); #else - char dolphin_exe_path[PATH_MAX]; - ssize_t len = ::readlink("/proc/self/exe", dolphin_exe_path, sizeof(dolphin_exe_path)); - if (len == -1 || len == sizeof(dolphin_exe_path)) - { - len = 0; - } - dolphin_exe_path[len] = '\0'; - result = dolphin_exe_path; + char dolphin_exe_path[PATH_MAX]; + ssize_t len = ::readlink("/proc/self/exe", dolphin_exe_path, sizeof(dolphin_exe_path)); + if (len == -1 || len == sizeof(dolphin_exe_path)) + { + len = 0; + } + dolphin_exe_path[len] = '\0'; + return dolphin_exe_path; #endif - return result; - }(); - return dolphin_path; } std::string GetExeDirectory() { - std::string exe_path = GetExePath(); -#ifdef _WIN32 - return exe_path.substr(0, exe_path.rfind('\\')); -#else - return exe_path.substr(0, exe_path.rfind('/')); -#endif + return PathToString(StringToPath(GetExePath()).parent_path()); } static std::string CreateSysDirectoryPath() diff --git a/Source/Core/Common/FileUtil.h b/Source/Core/Common/FileUtil.h index b87bde0295..37bced5a68 100644 --- a/Source/Core/Common/FileUtil.h +++ b/Source/Core/Common/FileUtil.h @@ -4,6 +4,7 @@ #pragma once #include +#include #include #include #include @@ -108,7 +109,6 @@ class FileInfo final public: explicit FileInfo(const std::string& path); explicit FileInfo(const char* path); - explicit FileInfo(int fd); // Returns true if the path exists bool Exists() const; @@ -120,15 +120,8 @@ public: u64 GetSize() const; private: -#ifdef ANDROID - void AndroidContentInit(const std::string& path); -#endif - -#ifdef _WIN32 - struct __stat64 m_stat; -#else - struct stat m_stat; -#endif + std::filesystem::file_status m_status; + std::uintmax_t m_size; bool m_exists; }; @@ -144,9 +137,6 @@ bool IsFile(const std::string& path); // Returns the size of a file (or returns 0 if the path isn't a file that exists) u64 GetSize(const std::string& path); -// Overloaded GetSize, accepts file descriptor -u64 GetSize(const int fd); - // Overloaded GetSize, accepts FILE* u64 GetSize(FILE* f);