diff options
| author | 2021-05-25 19:32:56 -0400 | |
|---|---|---|
| committer | 2021-05-25 19:32:56 -0400 | |
| commit | 065867e2c24e9856c360fc2d6b9a86c92aedc43e (patch) | |
| tree | 7964e85ef4f01a3c2b8f44e850f37b384405b930 | |
| parent | Merge pull request #6349 from german77/suppress_config_warning (diff) | |
| download | yuzu-065867e2c24e9856c360fc2d6b9a86c92aedc43e.tar.gz yuzu-065867e2c24e9856c360fc2d6b9a86c92aedc43e.tar.xz yuzu-065867e2c24e9856c360fc2d6b9a86c92aedc43e.zip | |
common: fs: Rework the Common Filesystem interface to make use of std::filesystem (#6270)
* common: fs: fs_types: Create filesystem types
Contains various filesystem types used by the Common::FS library
* common: fs: fs_util: Add std::string to std::u8string conversion utility
* common: fs: path_util: Add utlity functions for paths
Contains various utility functions for getting or manipulating filesystem paths used by the Common::FS library
* common: fs: file: Rewrite the IOFile implementation
* common: fs: Reimplement Common::FS library using std::filesystem
* common: fs: fs_paths: Add fs_paths to replace common_paths
* common: fs: path_util: Add the rest of the path functions
* common: Remove the previous Common::FS implementation
* general: Remove unused fs includes
* string_util: Remove unused function and include
* nvidia_flags: Migrate to the new Common::FS library
* settings: Migrate to the new Common::FS library
* logging: backend: Migrate to the new Common::FS library
* core: Migrate to the new Common::FS library
* perf_stats: Migrate to the new Common::FS library
* reporter: Migrate to the new Common::FS library
* telemetry_session: Migrate to the new Common::FS library
* key_manager: Migrate to the new Common::FS library
* bis_factory: Migrate to the new Common::FS library
* registered_cache: Migrate to the new Common::FS library
* xts_archive: Migrate to the new Common::FS library
* service: acc: Migrate to the new Common::FS library
* applets/profile: Migrate to the new Common::FS library
* applets/web: Migrate to the new Common::FS library
* service: filesystem: Migrate to the new Common::FS library
* loader: Migrate to the new Common::FS library
* gl_shader_disk_cache: Migrate to the new Common::FS library
* nsight_aftermath_tracker: Migrate to the new Common::FS library
* vulkan_library: Migrate to the new Common::FS library
* configure_debug: Migrate to the new Common::FS library
* game_list_worker: Migrate to the new Common::FS library
* config: Migrate to the new Common::FS library
* configure_filesystem: Migrate to the new Common::FS library
* configure_per_game_addons: Migrate to the new Common::FS library
* configure_profile_manager: Migrate to the new Common::FS library
* configure_ui: Migrate to the new Common::FS library
* input_profiles: Migrate to the new Common::FS library
* yuzu_cmd: config: Migrate to the new Common::FS library
* yuzu_cmd: Migrate to the new Common::FS library
* vfs_real: Migrate to the new Common::FS library
* vfs: Migrate to the new Common::FS library
* vfs_libzip: Migrate to the new Common::FS library
* service: bcat: Migrate to the new Common::FS library
* yuzu: main: Migrate to the new Common::FS library
* vfs_real: Delete the contents of an existing file in CreateFile
Current usages of CreateFile expect to delete the contents of an existing file, retain this behavior for now.
* input_profiles: Don't iterate the input profile dir if it does not exist
Silences an error produced in the log if the directory does not exist.
* game_list_worker: Skip parsing file if the returned VfsFile is nullptr
Prevents crashes in GetLoader when the virtual file is nullptr
* common: fs: Validate paths for path length
* service: filesystem: Open the mod load directory as read only
74 files changed, 3789 insertions, 2173 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 88644eeb6..eafb96b0b 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt | |||
| @@ -109,7 +109,6 @@ add_library(common STATIC | |||
| 109 | cityhash.cpp | 109 | cityhash.cpp |
| 110 | cityhash.h | 110 | cityhash.h |
| 111 | common_funcs.h | 111 | common_funcs.h |
| 112 | common_paths.h | ||
| 113 | common_sizes.h | 112 | common_sizes.h |
| 114 | common_types.h | 113 | common_types.h |
| 115 | concepts.h | 114 | concepts.h |
| @@ -118,8 +117,16 @@ add_library(common STATIC | |||
| 118 | dynamic_library.h | 117 | dynamic_library.h |
| 119 | fiber.cpp | 118 | fiber.cpp |
| 120 | fiber.h | 119 | fiber.h |
| 121 | file_util.cpp | 120 | fs/file.cpp |
| 122 | file_util.h | 121 | fs/file.h |
| 122 | fs/fs.cpp | ||
| 123 | fs/fs.h | ||
| 124 | fs/fs_paths.h | ||
| 125 | fs/fs_types.h | ||
| 126 | fs/fs_util.cpp | ||
| 127 | fs/fs_util.h | ||
| 128 | fs/path_util.cpp | ||
| 129 | fs/path_util.h | ||
| 123 | hash.h | 130 | hash.h |
| 124 | hex_util.cpp | 131 | hex_util.cpp |
| 125 | hex_util.h | 132 | hex_util.h |
diff --git a/src/common/common_paths.h b/src/common/common_paths.h deleted file mode 100644 index 3c593d5f6..000000000 --- a/src/common/common_paths.h +++ /dev/null | |||
| @@ -1,52 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | // Directory separators, do we need this? | ||
| 8 | #define DIR_SEP "/" | ||
| 9 | #define DIR_SEP_CHR '/' | ||
| 10 | |||
| 11 | #ifndef MAX_PATH | ||
| 12 | #define MAX_PATH 260 | ||
| 13 | #endif | ||
| 14 | |||
| 15 | // The user data dir | ||
| 16 | #define ROOT_DIR "." | ||
| 17 | #define USERDATA_DIR "user" | ||
| 18 | #ifdef USER_DIR | ||
| 19 | #define EMU_DATA_DIR USER_DIR | ||
| 20 | #else | ||
| 21 | #define EMU_DATA_DIR "yuzu" | ||
| 22 | #endif | ||
| 23 | |||
| 24 | // Dirs in both User and Sys | ||
| 25 | #define EUR_DIR "EUR" | ||
| 26 | #define USA_DIR "USA" | ||
| 27 | #define JAP_DIR "JAP" | ||
| 28 | |||
| 29 | // Subdirs in the User dir returned by GetUserPath(UserPath::UserDir) | ||
| 30 | #define CONFIG_DIR "config" | ||
| 31 | #define CACHE_DIR "cache" | ||
| 32 | #define SDMC_DIR "sdmc" | ||
| 33 | #define NAND_DIR "nand" | ||
| 34 | #define SYSDATA_DIR "sysdata" | ||
| 35 | #define KEYS_DIR "keys" | ||
| 36 | #define LOAD_DIR "load" | ||
| 37 | #define DUMP_DIR "dump" | ||
| 38 | #define SCREENSHOTS_DIR "screenshots" | ||
| 39 | #define SHADER_DIR "shader" | ||
| 40 | #define LOG_DIR "log" | ||
| 41 | |||
| 42 | // Filenames | ||
| 43 | // Files in the directory returned by GetUserPath(UserPath::ConfigDir) | ||
| 44 | #define EMU_CONFIG "emu.ini" | ||
| 45 | #define DEBUGGER_CONFIG "debugger.ini" | ||
| 46 | #define LOGGER_CONFIG "logger.ini" | ||
| 47 | // Files in the directory returned by GetUserPath(UserPath::LogDir) | ||
| 48 | #define LOG_FILE "yuzu_log.txt" | ||
| 49 | |||
| 50 | // Sys files | ||
| 51 | #define SHARED_FONT "shared_font.bin" | ||
| 52 | #define AES_KEYS "aes_keys.txt" | ||
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp deleted file mode 100644 index 18fbfa25b..000000000 --- a/src/common/file_util.cpp +++ /dev/null | |||
| @@ -1,1032 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | #include <limits> | ||
| 7 | #include <memory> | ||
| 8 | #include <sstream> | ||
| 9 | #include <unordered_map> | ||
| 10 | #include "common/assert.h" | ||
| 11 | #include "common/common_funcs.h" | ||
| 12 | #include "common/common_paths.h" | ||
| 13 | #include "common/file_util.h" | ||
| 14 | #include "common/logging/log.h" | ||
| 15 | |||
| 16 | #ifdef _WIN32 | ||
| 17 | #include <windows.h> | ||
| 18 | // windows.h needs to be included before other windows headers | ||
| 19 | #include <direct.h> // getcwd | ||
| 20 | #include <io.h> | ||
| 21 | #include <shellapi.h> | ||
| 22 | #include <shlobj.h> // for SHGetFolderPath | ||
| 23 | #include <tchar.h> | ||
| 24 | #include "common/string_util.h" | ||
| 25 | |||
| 26 | #ifdef _MSC_VER | ||
| 27 | // 64 bit offsets for MSVC | ||
| 28 | #define fseeko _fseeki64 | ||
| 29 | #define ftello _ftelli64 | ||
| 30 | #define fileno _fileno | ||
| 31 | #endif | ||
| 32 | |||
| 33 | // 64 bit offsets for MSVC and MinGW. MinGW also needs this for using _wstat64 | ||
| 34 | #define stat _stat64 | ||
| 35 | #define fstat _fstat64 | ||
| 36 | |||
| 37 | #else | ||
| 38 | #ifdef __APPLE__ | ||
| 39 | #include <sys/param.h> | ||
| 40 | #endif | ||
| 41 | #include <cctype> | ||
| 42 | #include <cerrno> | ||
| 43 | #include <cstdlib> | ||
| 44 | #include <cstring> | ||
| 45 | #include <dirent.h> | ||
| 46 | #include <pwd.h> | ||
| 47 | #include <unistd.h> | ||
| 48 | #endif | ||
| 49 | |||
| 50 | #if defined(__APPLE__) | ||
| 51 | // CFURL contains __attribute__ directives that gcc does not know how to parse, so we need to just | ||
| 52 | // ignore them if we're not using clang. The macro is only used to prevent linking against | ||
| 53 | // functions that don't exist on older versions of macOS, and the worst case scenario is a linker | ||
| 54 | // error, so this is perfectly safe, just inconvenient. | ||
| 55 | #ifndef __clang__ | ||
| 56 | #define availability(...) | ||
| 57 | #endif | ||
| 58 | #include <CoreFoundation/CFBundle.h> | ||
| 59 | #include <CoreFoundation/CFString.h> | ||
| 60 | #include <CoreFoundation/CFURL.h> | ||
| 61 | #ifdef availability | ||
| 62 | #undef availability | ||
| 63 | #endif | ||
| 64 | |||
| 65 | #endif | ||
| 66 | |||
| 67 | #include <algorithm> | ||
| 68 | #include <sys/stat.h> | ||
| 69 | |||
| 70 | #ifndef S_ISDIR | ||
| 71 | #define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) | ||
| 72 | #endif | ||
| 73 | |||
| 74 | // This namespace has various generic functions related to files and paths. | ||
| 75 | // The code still needs a ton of cleanup. | ||
| 76 | // REMEMBER: strdup considered harmful! | ||
| 77 | namespace Common::FS { | ||
| 78 | |||
| 79 | // Remove any ending forward slashes from directory paths | ||
| 80 | // Modifies argument. | ||
| 81 | static void StripTailDirSlashes(std::string& fname) { | ||
| 82 | if (fname.length() <= 1) { | ||
| 83 | return; | ||
| 84 | } | ||
| 85 | |||
| 86 | std::size_t i = fname.length(); | ||
| 87 | while (i > 0 && fname[i - 1] == DIR_SEP_CHR) { | ||
| 88 | --i; | ||
| 89 | } | ||
| 90 | fname.resize(i); | ||
| 91 | } | ||
| 92 | |||
| 93 | bool Exists(const std::string& filename) { | ||
| 94 | struct stat file_info; | ||
| 95 | |||
| 96 | std::string copy(filename); | ||
| 97 | StripTailDirSlashes(copy); | ||
| 98 | |||
| 99 | #ifdef _WIN32 | ||
| 100 | // Windows needs a slash to identify a driver root | ||
| 101 | if (copy.size() != 0 && copy.back() == ':') | ||
| 102 | copy += DIR_SEP_CHR; | ||
| 103 | |||
| 104 | int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info); | ||
| 105 | #else | ||
| 106 | int result = stat(copy.c_str(), &file_info); | ||
| 107 | #endif | ||
| 108 | |||
| 109 | return (result == 0); | ||
| 110 | } | ||
| 111 | |||
| 112 | bool IsDirectory(const std::string& filename) { | ||
| 113 | struct stat file_info; | ||
| 114 | |||
| 115 | std::string copy(filename); | ||
| 116 | StripTailDirSlashes(copy); | ||
| 117 | |||
| 118 | #ifdef _WIN32 | ||
| 119 | // Windows needs a slash to identify a driver root | ||
| 120 | if (copy.size() != 0 && copy.back() == ':') | ||
| 121 | copy += DIR_SEP_CHR; | ||
| 122 | |||
| 123 | int result = _wstat64(Common::UTF8ToUTF16W(copy).c_str(), &file_info); | ||
| 124 | #else | ||
| 125 | int result = stat(copy.c_str(), &file_info); | ||
| 126 | #endif | ||
| 127 | |||
| 128 | if (result < 0) { | ||
| 129 | LOG_DEBUG(Common_Filesystem, "stat failed on {}: {}", filename, GetLastErrorMsg()); | ||
| 130 | return false; | ||
| 131 | } | ||
| 132 | |||
| 133 | return S_ISDIR(file_info.st_mode); | ||
| 134 | } | ||
| 135 | |||
| 136 | bool Delete(const std::string& filename) { | ||
| 137 | LOG_TRACE(Common_Filesystem, "file {}", filename); | ||
| 138 | |||
| 139 | // Return true because we care about the file no | ||
| 140 | // being there, not the actual delete. | ||
| 141 | if (!Exists(filename)) { | ||
| 142 | LOG_DEBUG(Common_Filesystem, "{} does not exist", filename); | ||
| 143 | return true; | ||
| 144 | } | ||
| 145 | |||
| 146 | // We can't delete a directory | ||
| 147 | if (IsDirectory(filename)) { | ||
| 148 | LOG_ERROR(Common_Filesystem, "Failed: {} is a directory", filename); | ||
| 149 | return false; | ||
| 150 | } | ||
| 151 | |||
| 152 | #ifdef _WIN32 | ||
| 153 | if (!DeleteFileW(Common::UTF8ToUTF16W(filename).c_str())) { | ||
| 154 | LOG_ERROR(Common_Filesystem, "DeleteFile failed on {}: {}", filename, GetLastErrorMsg()); | ||
| 155 | return false; | ||
| 156 | } | ||
| 157 | #else | ||
| 158 | if (unlink(filename.c_str()) == -1) { | ||
| 159 | LOG_ERROR(Common_Filesystem, "unlink failed on {}: {}", filename, GetLastErrorMsg()); | ||
| 160 | return false; | ||
| 161 | } | ||
| 162 | #endif | ||
| 163 | |||
| 164 | return true; | ||
| 165 | } | ||
| 166 | |||
| 167 | bool CreateDir(const std::string& path) { | ||
| 168 | LOG_TRACE(Common_Filesystem, "directory {}", path); | ||
| 169 | #ifdef _WIN32 | ||
| 170 | if (::CreateDirectoryW(Common::UTF8ToUTF16W(path).c_str(), nullptr)) | ||
| 171 | return true; | ||
| 172 | DWORD error = GetLastError(); | ||
| 173 | if (error == ERROR_ALREADY_EXISTS) { | ||
| 174 | LOG_DEBUG(Common_Filesystem, "CreateDirectory failed on {}: already exists", path); | ||
| 175 | return true; | ||
| 176 | } | ||
| 177 | LOG_ERROR(Common_Filesystem, "CreateDirectory failed on {}: {}", path, error); | ||
| 178 | return false; | ||
| 179 | #else | ||
| 180 | if (mkdir(path.c_str(), 0755) == 0) | ||
| 181 | return true; | ||
| 182 | |||
| 183 | int err = errno; | ||
| 184 | |||
| 185 | if (err == EEXIST) { | ||
| 186 | LOG_DEBUG(Common_Filesystem, "mkdir failed on {}: already exists", path); | ||
| 187 | return true; | ||
| 188 | } | ||
| 189 | |||
| 190 | LOG_ERROR(Common_Filesystem, "mkdir failed on {}: {}", path, strerror(err)); | ||
| 191 | return false; | ||
| 192 | #endif | ||
| 193 | } | ||
| 194 | |||
| 195 | bool CreateFullPath(const std::string& fullPath) { | ||
| 196 | int panicCounter = 100; | ||
| 197 | LOG_TRACE(Common_Filesystem, "path {}", fullPath); | ||
| 198 | |||
| 199 | if (Exists(fullPath)) { | ||
| 200 | LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath); | ||
| 201 | return true; | ||
| 202 | } | ||
| 203 | |||
| 204 | std::size_t position = 0; | ||
| 205 | while (true) { | ||
| 206 | // Find next sub path | ||
| 207 | position = fullPath.find(DIR_SEP_CHR, position); | ||
| 208 | |||
| 209 | // we're done, yay! | ||
| 210 | if (position == fullPath.npos) | ||
| 211 | return true; | ||
| 212 | |||
| 213 | // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") | ||
| 214 | std::string const subPath(fullPath.substr(0, position + 1)); | ||
| 215 | if (!IsDirectory(subPath) && !CreateDir(subPath)) { | ||
| 216 | LOG_ERROR(Common, "CreateFullPath: directory creation failed"); | ||
| 217 | return false; | ||
| 218 | } | ||
| 219 | |||
| 220 | // A safety check | ||
| 221 | panicCounter--; | ||
| 222 | if (panicCounter <= 0) { | ||
| 223 | LOG_ERROR(Common, "CreateFullPath: directory structure is too deep"); | ||
| 224 | return false; | ||
| 225 | } | ||
| 226 | position++; | ||
| 227 | } | ||
| 228 | } | ||
| 229 | |||
| 230 | bool DeleteDir(const std::string& filename) { | ||
| 231 | LOG_TRACE(Common_Filesystem, "directory {}", filename); | ||
| 232 | |||
| 233 | // check if a directory | ||
| 234 | if (!IsDirectory(filename)) { | ||
| 235 | LOG_ERROR(Common_Filesystem, "Not a directory {}", filename); | ||
| 236 | return false; | ||
| 237 | } | ||
| 238 | |||
| 239 | #ifdef _WIN32 | ||
| 240 | if (::RemoveDirectoryW(Common::UTF8ToUTF16W(filename).c_str())) | ||
| 241 | return true; | ||
| 242 | #else | ||
| 243 | if (rmdir(filename.c_str()) == 0) | ||
| 244 | return true; | ||
| 245 | #endif | ||
| 246 | LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); | ||
| 247 | |||
| 248 | return false; | ||
| 249 | } | ||
| 250 | |||
| 251 | bool Rename(const std::string& srcFilename, const std::string& destFilename) { | ||
| 252 | LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); | ||
| 253 | #ifdef _WIN32 | ||
| 254 | if (_wrename(Common::UTF8ToUTF16W(srcFilename).c_str(), | ||
| 255 | Common::UTF8ToUTF16W(destFilename).c_str()) == 0) | ||
| 256 | return true; | ||
| 257 | #else | ||
| 258 | if (rename(srcFilename.c_str(), destFilename.c_str()) == 0) | ||
| 259 | return true; | ||
| 260 | #endif | ||
| 261 | LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, | ||
| 262 | GetLastErrorMsg()); | ||
| 263 | return false; | ||
| 264 | } | ||
| 265 | |||
| 266 | bool Copy(const std::string& srcFilename, const std::string& destFilename) { | ||
| 267 | LOG_TRACE(Common_Filesystem, "{} --> {}", srcFilename, destFilename); | ||
| 268 | #ifdef _WIN32 | ||
| 269 | if (CopyFileW(Common::UTF8ToUTF16W(srcFilename).c_str(), | ||
| 270 | Common::UTF8ToUTF16W(destFilename).c_str(), FALSE)) | ||
| 271 | return true; | ||
| 272 | |||
| 273 | LOG_ERROR(Common_Filesystem, "failed {} --> {}: {}", srcFilename, destFilename, | ||
| 274 | GetLastErrorMsg()); | ||
| 275 | return false; | ||
| 276 | #else | ||
| 277 | using CFilePointer = std::unique_ptr<FILE, decltype(&std::fclose)>; | ||
| 278 | |||
| 279 | // Open input file | ||
| 280 | CFilePointer input{fopen(srcFilename.c_str(), "rb"), std::fclose}; | ||
| 281 | if (!input) { | ||
| 282 | LOG_ERROR(Common_Filesystem, "opening input failed {} --> {}: {}", srcFilename, | ||
| 283 | destFilename, GetLastErrorMsg()); | ||
| 284 | return false; | ||
| 285 | } | ||
| 286 | |||
| 287 | // open output file | ||
| 288 | CFilePointer output{fopen(destFilename.c_str(), "wb"), std::fclose}; | ||
| 289 | if (!output) { | ||
| 290 | LOG_ERROR(Common_Filesystem, "opening output failed {} --> {}: {}", srcFilename, | ||
| 291 | destFilename, GetLastErrorMsg()); | ||
| 292 | return false; | ||
| 293 | } | ||
| 294 | |||
| 295 | // copy loop | ||
| 296 | std::array<char, 1024> buffer; | ||
| 297 | while (!feof(input.get())) { | ||
| 298 | // read input | ||
| 299 | std::size_t rnum = fread(buffer.data(), sizeof(char), buffer.size(), input.get()); | ||
| 300 | if (rnum != buffer.size()) { | ||
| 301 | if (ferror(input.get()) != 0) { | ||
| 302 | LOG_ERROR(Common_Filesystem, "failed reading from source, {} --> {}: {}", | ||
| 303 | srcFilename, destFilename, GetLastErrorMsg()); | ||
| 304 | return false; | ||
| 305 | } | ||
| 306 | } | ||
| 307 | |||
| 308 | // write output | ||
| 309 | std::size_t wnum = fwrite(buffer.data(), sizeof(char), rnum, output.get()); | ||
| 310 | if (wnum != rnum) { | ||
| 311 | LOG_ERROR(Common_Filesystem, "failed writing to output, {} --> {}: {}", srcFilename, | ||
| 312 | destFilename, GetLastErrorMsg()); | ||
| 313 | return false; | ||
| 314 | } | ||
| 315 | } | ||
| 316 | |||
| 317 | return true; | ||
| 318 | #endif | ||
| 319 | } | ||
| 320 | |||
| 321 | u64 GetSize(const std::string& filename) { | ||
| 322 | if (!Exists(filename)) { | ||
| 323 | LOG_ERROR(Common_Filesystem, "failed {}: No such file", filename); | ||
| 324 | return 0; | ||
| 325 | } | ||
| 326 | |||
| 327 | if (IsDirectory(filename)) { | ||
| 328 | LOG_ERROR(Common_Filesystem, "failed {}: is a directory", filename); | ||
| 329 | return 0; | ||
| 330 | } | ||
| 331 | |||
| 332 | struct stat buf; | ||
| 333 | #ifdef _WIN32 | ||
| 334 | if (_wstat64(Common::UTF8ToUTF16W(filename).c_str(), &buf) == 0) | ||
| 335 | #else | ||
| 336 | if (stat(filename.c_str(), &buf) == 0) | ||
| 337 | #endif | ||
| 338 | { | ||
| 339 | LOG_TRACE(Common_Filesystem, "{}: {}", filename, buf.st_size); | ||
| 340 | return buf.st_size; | ||
| 341 | } | ||
| 342 | |||
| 343 | LOG_ERROR(Common_Filesystem, "Stat failed {}: {}", filename, GetLastErrorMsg()); | ||
| 344 | return 0; | ||
| 345 | } | ||
| 346 | |||
| 347 | u64 GetSize(const int fd) { | ||
| 348 | struct stat buf; | ||
| 349 | if (fstat(fd, &buf) != 0) { | ||
| 350 | LOG_ERROR(Common_Filesystem, "GetSize: stat failed {}: {}", fd, GetLastErrorMsg()); | ||
| 351 | return 0; | ||
| 352 | } | ||
| 353 | return buf.st_size; | ||
| 354 | } | ||
| 355 | |||
| 356 | u64 GetSize(FILE* f) { | ||
| 357 | // can't use off_t here because it can be 32-bit | ||
| 358 | u64 pos = ftello(f); | ||
| 359 | if (fseeko(f, 0, SEEK_END) != 0) { | ||
| 360 | LOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), GetLastErrorMsg()); | ||
| 361 | return 0; | ||
| 362 | } | ||
| 363 | u64 size = ftello(f); | ||
| 364 | if ((size != pos) && (fseeko(f, pos, SEEK_SET) != 0)) { | ||
| 365 | LOG_ERROR(Common_Filesystem, "GetSize: seek failed {}: {}", fmt::ptr(f), GetLastErrorMsg()); | ||
| 366 | return 0; | ||
| 367 | } | ||
| 368 | return size; | ||
| 369 | } | ||
| 370 | |||
| 371 | bool CreateEmptyFile(const std::string& filename) { | ||
| 372 | LOG_TRACE(Common_Filesystem, "{}", filename); | ||
| 373 | |||
| 374 | if (!IOFile(filename, "wb").IsOpen()) { | ||
| 375 | LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); | ||
| 376 | return false; | ||
| 377 | } | ||
| 378 | |||
| 379 | return true; | ||
| 380 | } | ||
| 381 | |||
| 382 | bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory, | ||
| 383 | DirectoryEntryCallable callback) { | ||
| 384 | LOG_TRACE(Common_Filesystem, "directory {}", directory); | ||
| 385 | |||
| 386 | // How many files + directories we found | ||
| 387 | u64 found_entries = 0; | ||
| 388 | |||
| 389 | // Save the status of callback function | ||
| 390 | bool callback_error = false; | ||
| 391 | |||
| 392 | #ifdef _WIN32 | ||
| 393 | // Find the first file in the directory. | ||
| 394 | WIN32_FIND_DATAW ffd; | ||
| 395 | |||
| 396 | HANDLE handle_find = FindFirstFileW(Common::UTF8ToUTF16W(directory + "\\*").c_str(), &ffd); | ||
| 397 | if (handle_find == INVALID_HANDLE_VALUE) { | ||
| 398 | FindClose(handle_find); | ||
| 399 | return false; | ||
| 400 | } | ||
| 401 | // windows loop | ||
| 402 | do { | ||
| 403 | const std::string virtual_name(Common::UTF16ToUTF8(ffd.cFileName)); | ||
| 404 | #else | ||
| 405 | DIR* dirp = opendir(directory.c_str()); | ||
| 406 | if (!dirp) | ||
| 407 | return false; | ||
| 408 | |||
| 409 | // non windows loop | ||
| 410 | while (struct dirent* result = readdir(dirp)) { | ||
| 411 | const std::string virtual_name(result->d_name); | ||
| 412 | #endif | ||
| 413 | |||
| 414 | if (virtual_name == "." || virtual_name == "..") | ||
| 415 | continue; | ||
| 416 | |||
| 417 | u64 ret_entries = 0; | ||
| 418 | if (!callback(&ret_entries, directory, virtual_name)) { | ||
| 419 | callback_error = true; | ||
| 420 | break; | ||
| 421 | } | ||
| 422 | found_entries += ret_entries; | ||
| 423 | |||
| 424 | #ifdef _WIN32 | ||
| 425 | } while (FindNextFileW(handle_find, &ffd) != 0); | ||
| 426 | FindClose(handle_find); | ||
| 427 | #else | ||
| 428 | } | ||
| 429 | closedir(dirp); | ||
| 430 | #endif | ||
| 431 | |||
| 432 | if (callback_error) | ||
| 433 | return false; | ||
| 434 | |||
| 435 | // num_entries_out is allowed to be specified nullptr, in which case we shouldn't try to set it | ||
| 436 | if (num_entries_out != nullptr) | ||
| 437 | *num_entries_out = found_entries; | ||
| 438 | return true; | ||
| 439 | } | ||
| 440 | |||
| 441 | u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry, | ||
| 442 | unsigned int recursion) { | ||
| 443 | const auto callback = [recursion, &parent_entry](u64* num_entries_out, | ||
| 444 | const std::string& directory, | ||
| 445 | const std::string& virtual_name) -> bool { | ||
| 446 | FSTEntry entry; | ||
| 447 | entry.virtualName = virtual_name; | ||
| 448 | entry.physicalName = directory + DIR_SEP + virtual_name; | ||
| 449 | |||
| 450 | if (IsDirectory(entry.physicalName)) { | ||
| 451 | entry.isDirectory = true; | ||
| 452 | // is a directory, lets go inside if we didn't recurse to often | ||
| 453 | if (recursion > 0) { | ||
| 454 | entry.size = ScanDirectoryTree(entry.physicalName, entry, recursion - 1); | ||
| 455 | *num_entries_out += entry.size; | ||
| 456 | } else { | ||
| 457 | entry.size = 0; | ||
| 458 | } | ||
| 459 | } else { // is a file | ||
| 460 | entry.isDirectory = false; | ||
| 461 | entry.size = GetSize(entry.physicalName); | ||
| 462 | } | ||
| 463 | (*num_entries_out)++; | ||
| 464 | |||
| 465 | // Push into the tree | ||
| 466 | parent_entry.children.push_back(std::move(entry)); | ||
| 467 | return true; | ||
| 468 | }; | ||
| 469 | |||
| 470 | u64 num_entries; | ||
| 471 | return ForeachDirectoryEntry(&num_entries, directory, callback) ? num_entries : 0; | ||
| 472 | } | ||
| 473 | |||
| 474 | bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) { | ||
| 475 | const auto callback = [recursion](u64*, const std::string& directory, | ||
| 476 | const std::string& virtual_name) { | ||
| 477 | const std::string new_path = directory + DIR_SEP_CHR + virtual_name; | ||
| 478 | |||
| 479 | if (IsDirectory(new_path)) { | ||
| 480 | if (recursion == 0) { | ||
| 481 | return false; | ||
| 482 | } | ||
| 483 | return DeleteDirRecursively(new_path, recursion - 1); | ||
| 484 | } | ||
| 485 | return Delete(new_path); | ||
| 486 | }; | ||
| 487 | |||
| 488 | if (!ForeachDirectoryEntry(nullptr, directory, callback)) | ||
| 489 | return false; | ||
| 490 | |||
| 491 | // Delete the outermost directory | ||
| 492 | DeleteDir(directory); | ||
| 493 | return true; | ||
| 494 | } | ||
| 495 | |||
| 496 | void CopyDir([[maybe_unused]] const std::string& source_path, | ||
| 497 | [[maybe_unused]] const std::string& dest_path) { | ||
| 498 | #ifndef _WIN32 | ||
| 499 | if (source_path == dest_path) { | ||
| 500 | return; | ||
| 501 | } | ||
| 502 | if (!Exists(source_path)) { | ||
| 503 | return; | ||
| 504 | } | ||
| 505 | if (!Exists(dest_path)) { | ||
| 506 | CreateFullPath(dest_path); | ||
| 507 | } | ||
| 508 | |||
| 509 | DIR* dirp = opendir(source_path.c_str()); | ||
| 510 | if (!dirp) { | ||
| 511 | return; | ||
| 512 | } | ||
| 513 | |||
| 514 | while (struct dirent* result = readdir(dirp)) { | ||
| 515 | const std::string virtualName(result->d_name); | ||
| 516 | // check for "." and ".." | ||
| 517 | if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || | ||
| 518 | ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) { | ||
| 519 | continue; | ||
| 520 | } | ||
| 521 | |||
| 522 | std::string source, dest; | ||
| 523 | source = source_path + virtualName; | ||
| 524 | dest = dest_path + virtualName; | ||
| 525 | if (IsDirectory(source)) { | ||
| 526 | source += '/'; | ||
| 527 | dest += '/'; | ||
| 528 | if (!Exists(dest)) { | ||
| 529 | CreateFullPath(dest); | ||
| 530 | } | ||
| 531 | CopyDir(source, dest); | ||
| 532 | } else if (!Exists(dest)) { | ||
| 533 | Copy(source, dest); | ||
| 534 | } | ||
| 535 | } | ||
| 536 | closedir(dirp); | ||
| 537 | #endif | ||
| 538 | } | ||
| 539 | |||
| 540 | std::optional<std::string> GetCurrentDir() { | ||
| 541 | // Get the current working directory (getcwd uses malloc) | ||
| 542 | #ifdef _WIN32 | ||
| 543 | wchar_t* dir = _wgetcwd(nullptr, 0); | ||
| 544 | if (!dir) { | ||
| 545 | #else | ||
| 546 | char* dir = getcwd(nullptr, 0); | ||
| 547 | if (!dir) { | ||
| 548 | #endif | ||
| 549 | LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); | ||
| 550 | return std::nullopt; | ||
| 551 | } | ||
| 552 | #ifdef _WIN32 | ||
| 553 | std::string strDir = Common::UTF16ToUTF8(dir); | ||
| 554 | #else | ||
| 555 | std::string strDir = dir; | ||
| 556 | #endif | ||
| 557 | free(dir); | ||
| 558 | return strDir; | ||
| 559 | } | ||
| 560 | |||
| 561 | bool SetCurrentDir(const std::string& directory) { | ||
| 562 | #ifdef _WIN32 | ||
| 563 | return _wchdir(Common::UTF8ToUTF16W(directory).c_str()) == 0; | ||
| 564 | #else | ||
| 565 | return chdir(directory.c_str()) == 0; | ||
| 566 | #endif | ||
| 567 | } | ||
| 568 | |||
| 569 | #if defined(__APPLE__) | ||
| 570 | std::string GetBundleDirectory() { | ||
| 571 | CFURLRef BundleRef; | ||
| 572 | char AppBundlePath[MAXPATHLEN]; | ||
| 573 | // Get the main bundle for the app | ||
| 574 | BundleRef = CFBundleCopyBundleURL(CFBundleGetMainBundle()); | ||
| 575 | CFStringRef BundlePath = CFURLCopyFileSystemPath(BundleRef, kCFURLPOSIXPathStyle); | ||
| 576 | CFStringGetFileSystemRepresentation(BundlePath, AppBundlePath, sizeof(AppBundlePath)); | ||
| 577 | CFRelease(BundleRef); | ||
| 578 | CFRelease(BundlePath); | ||
| 579 | |||
| 580 | return AppBundlePath; | ||
| 581 | } | ||
| 582 | #endif | ||
| 583 | |||
| 584 | #ifdef _WIN32 | ||
| 585 | const std::string& GetExeDirectory() { | ||
| 586 | static std::string exe_path; | ||
| 587 | if (exe_path.empty()) { | ||
| 588 | wchar_t wchar_exe_path[2048]; | ||
| 589 | GetModuleFileNameW(nullptr, wchar_exe_path, 2048); | ||
| 590 | exe_path = Common::UTF16ToUTF8(wchar_exe_path); | ||
| 591 | exe_path = exe_path.substr(0, exe_path.find_last_of('\\')); | ||
| 592 | } | ||
| 593 | return exe_path; | ||
| 594 | } | ||
| 595 | |||
| 596 | std::string AppDataRoamingDirectory() { | ||
| 597 | PWSTR pw_local_path = nullptr; | ||
| 598 | // Only supported by Windows Vista or later | ||
| 599 | SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &pw_local_path); | ||
| 600 | std::string local_path = Common::UTF16ToUTF8(pw_local_path); | ||
| 601 | CoTaskMemFree(pw_local_path); | ||
| 602 | return local_path; | ||
| 603 | } | ||
| 604 | #else | ||
| 605 | /** | ||
| 606 | * @return The user’s home directory on POSIX systems | ||
| 607 | */ | ||
| 608 | static const std::string& GetHomeDirectory() { | ||
| 609 | static std::string home_path; | ||
| 610 | if (home_path.empty()) { | ||
| 611 | const char* envvar = getenv("HOME"); | ||
| 612 | if (envvar) { | ||
| 613 | home_path = envvar; | ||
| 614 | } else { | ||
| 615 | auto pw = getpwuid(getuid()); | ||
| 616 | ASSERT_MSG(pw, | ||
| 617 | "$HOME isn’t defined, and the current user can’t be found in /etc/passwd."); | ||
| 618 | home_path = pw->pw_dir; | ||
| 619 | } | ||
| 620 | } | ||
| 621 | return home_path; | ||
| 622 | } | ||
| 623 | |||
| 624 | /** | ||
| 625 | * Follows the XDG Base Directory Specification to get a directory path | ||
| 626 | * @param envvar The XDG environment variable to get the value from | ||
| 627 | * @return The directory path | ||
| 628 | * @sa http://standards.freedesktop.org/basedir-spec/basedir-spec-latest.html | ||
| 629 | */ | ||
| 630 | static const std::string GetUserDirectory(const std::string& envvar) { | ||
| 631 | const char* directory = getenv(envvar.c_str()); | ||
| 632 | |||
| 633 | std::string user_dir; | ||
| 634 | if (directory) { | ||
| 635 | user_dir = directory; | ||
| 636 | } else { | ||
| 637 | std::string subdirectory; | ||
| 638 | if (envvar == "XDG_DATA_HOME") | ||
| 639 | subdirectory = DIR_SEP ".local" DIR_SEP "share"; | ||
| 640 | else if (envvar == "XDG_CONFIG_HOME") | ||
| 641 | subdirectory = DIR_SEP ".config"; | ||
| 642 | else if (envvar == "XDG_CACHE_HOME") | ||
| 643 | subdirectory = DIR_SEP ".cache"; | ||
| 644 | else | ||
| 645 | ASSERT_MSG(false, "Unknown XDG variable {}.", envvar); | ||
| 646 | user_dir = GetHomeDirectory() + subdirectory; | ||
| 647 | } | ||
| 648 | |||
| 649 | ASSERT_MSG(!user_dir.empty(), "User directory {} mustn’t be empty.", envvar); | ||
| 650 | ASSERT_MSG(user_dir[0] == '/', "User directory {} must be absolute.", envvar); | ||
| 651 | |||
| 652 | return user_dir; | ||
| 653 | } | ||
| 654 | #endif | ||
| 655 | |||
| 656 | std::string GetSysDirectory() { | ||
| 657 | std::string sysDir; | ||
| 658 | |||
| 659 | #if defined(__APPLE__) | ||
| 660 | sysDir = GetBundleDirectory(); | ||
| 661 | sysDir += DIR_SEP; | ||
| 662 | sysDir += SYSDATA_DIR; | ||
| 663 | #else | ||
| 664 | sysDir = SYSDATA_DIR; | ||
| 665 | #endif | ||
| 666 | sysDir += DIR_SEP; | ||
| 667 | |||
| 668 | LOG_DEBUG(Common_Filesystem, "Setting to {}:", sysDir); | ||
| 669 | return sysDir; | ||
| 670 | } | ||
| 671 | |||
| 672 | const std::string& GetUserPath(UserPath path, const std::string& new_path) { | ||
| 673 | static std::unordered_map<UserPath, std::string> paths; | ||
| 674 | auto& user_path = paths[UserPath::UserDir]; | ||
| 675 | |||
| 676 | // Set up all paths and files on the first run | ||
| 677 | if (user_path.empty()) { | ||
| 678 | #ifdef _WIN32 | ||
| 679 | user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; | ||
| 680 | if (!IsDirectory(user_path)) { | ||
| 681 | user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP; | ||
| 682 | } else { | ||
| 683 | LOG_INFO(Common_Filesystem, "Using the local user directory"); | ||
| 684 | } | ||
| 685 | |||
| 686 | paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); | ||
| 687 | paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); | ||
| 688 | #else | ||
| 689 | if (Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) { | ||
| 690 | user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; | ||
| 691 | paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); | ||
| 692 | paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); | ||
| 693 | } else { | ||
| 694 | std::string data_dir = GetUserDirectory("XDG_DATA_HOME"); | ||
| 695 | std::string config_dir = GetUserDirectory("XDG_CONFIG_HOME"); | ||
| 696 | std::string cache_dir = GetUserDirectory("XDG_CACHE_HOME"); | ||
| 697 | |||
| 698 | user_path = data_dir + DIR_SEP EMU_DATA_DIR DIR_SEP; | ||
| 699 | paths.emplace(UserPath::ConfigDir, config_dir + DIR_SEP EMU_DATA_DIR DIR_SEP); | ||
| 700 | paths.emplace(UserPath::CacheDir, cache_dir + DIR_SEP EMU_DATA_DIR DIR_SEP); | ||
| 701 | } | ||
| 702 | #endif | ||
| 703 | paths.emplace(UserPath::SDMCDir, user_path + SDMC_DIR DIR_SEP); | ||
| 704 | paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); | ||
| 705 | paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP); | ||
| 706 | paths.emplace(UserPath::DumpDir, user_path + DUMP_DIR DIR_SEP); | ||
| 707 | paths.emplace(UserPath::ScreenshotsDir, user_path + SCREENSHOTS_DIR DIR_SEP); | ||
| 708 | paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP); | ||
| 709 | paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); | ||
| 710 | paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP); | ||
| 711 | // TODO: Put the logs in a better location for each OS | ||
| 712 | paths.emplace(UserPath::LogDir, user_path + LOG_DIR DIR_SEP); | ||
| 713 | } | ||
| 714 | |||
| 715 | if (!new_path.empty()) { | ||
| 716 | if (!IsDirectory(new_path)) { | ||
| 717 | LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path); | ||
| 718 | return paths[path]; | ||
| 719 | } else { | ||
| 720 | paths[path] = new_path; | ||
| 721 | } | ||
| 722 | |||
| 723 | switch (path) { | ||
| 724 | case UserPath::RootDir: | ||
| 725 | user_path = paths[UserPath::RootDir] + DIR_SEP; | ||
| 726 | break; | ||
| 727 | case UserPath::UserDir: | ||
| 728 | user_path = paths[UserPath::RootDir] + DIR_SEP; | ||
| 729 | paths[UserPath::ConfigDir] = user_path + CONFIG_DIR DIR_SEP; | ||
| 730 | paths[UserPath::CacheDir] = user_path + CACHE_DIR DIR_SEP; | ||
| 731 | paths[UserPath::SDMCDir] = user_path + SDMC_DIR DIR_SEP; | ||
| 732 | paths[UserPath::NANDDir] = user_path + NAND_DIR DIR_SEP; | ||
| 733 | break; | ||
| 734 | default: | ||
| 735 | break; | ||
| 736 | } | ||
| 737 | } | ||
| 738 | |||
| 739 | return paths[path]; | ||
| 740 | } | ||
| 741 | |||
| 742 | std::string GetHactoolConfigurationPath() { | ||
| 743 | #ifdef _WIN32 | ||
| 744 | PWSTR pw_local_path = nullptr; | ||
| 745 | if (SHGetKnownFolderPath(FOLDERID_Profile, 0, nullptr, &pw_local_path) != S_OK) | ||
| 746 | return ""; | ||
| 747 | std::string local_path = Common::UTF16ToUTF8(pw_local_path); | ||
| 748 | CoTaskMemFree(pw_local_path); | ||
| 749 | return local_path + "\\.switch"; | ||
| 750 | #else | ||
| 751 | return GetHomeDirectory() + "/.switch"; | ||
| 752 | #endif | ||
| 753 | } | ||
| 754 | |||
| 755 | std::string GetNANDRegistrationDir(bool system) { | ||
| 756 | if (system) | ||
| 757 | return GetUserPath(UserPath::NANDDir) + "system/Contents/registered/"; | ||
| 758 | return GetUserPath(UserPath::NANDDir) + "user/Contents/registered/"; | ||
| 759 | } | ||
| 760 | |||
| 761 | std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str) { | ||
| 762 | return IOFile(filename, text_file ? "w" : "wb").WriteString(str); | ||
| 763 | } | ||
| 764 | |||
| 765 | std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str) { | ||
| 766 | IOFile file(filename, text_file ? "r" : "rb"); | ||
| 767 | |||
| 768 | if (!file.IsOpen()) | ||
| 769 | return 0; | ||
| 770 | |||
| 771 | str.resize(static_cast<u32>(file.GetSize())); | ||
| 772 | return file.ReadArray(&str[0], str.size()); | ||
| 773 | } | ||
| 774 | |||
| 775 | void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, | ||
| 776 | std::array<char, 4>& extension) { | ||
| 777 | static constexpr std::string_view forbidden_characters = ".\"/\\[]:;=, "; | ||
| 778 | |||
| 779 | // On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces. | ||
| 780 | short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}}; | ||
| 781 | extension = {{' ', ' ', ' ', '\0'}}; | ||
| 782 | |||
| 783 | auto point = filename.rfind('.'); | ||
| 784 | if (point == filename.size() - 1) { | ||
| 785 | point = filename.rfind('.', point); | ||
| 786 | } | ||
| 787 | |||
| 788 | // Get short name. | ||
| 789 | int j = 0; | ||
| 790 | for (char letter : filename.substr(0, point)) { | ||
| 791 | if (forbidden_characters.find(letter, 0) != std::string::npos) { | ||
| 792 | continue; | ||
| 793 | } | ||
| 794 | if (j == 8) { | ||
| 795 | // TODO(Link Mauve): also do that for filenames containing a space. | ||
| 796 | // TODO(Link Mauve): handle multiple files having the same short name. | ||
| 797 | short_name[6] = '~'; | ||
| 798 | short_name[7] = '1'; | ||
| 799 | break; | ||
| 800 | } | ||
| 801 | short_name[j++] = static_cast<char>(std::toupper(letter)); | ||
| 802 | } | ||
| 803 | |||
| 804 | // Get extension. | ||
| 805 | if (point != std::string::npos) { | ||
| 806 | j = 0; | ||
| 807 | for (char letter : filename.substr(point + 1, 3)) { | ||
| 808 | extension[j++] = static_cast<char>(std::toupper(letter)); | ||
| 809 | } | ||
| 810 | } | ||
| 811 | } | ||
| 812 | |||
| 813 | std::vector<std::string> SplitPathComponents(std::string_view filename) { | ||
| 814 | std::string copy(filename); | ||
| 815 | std::replace(copy.begin(), copy.end(), '\\', '/'); | ||
| 816 | std::vector<std::string> out; | ||
| 817 | |||
| 818 | std::stringstream stream(copy); | ||
| 819 | std::string item; | ||
| 820 | while (std::getline(stream, item, '/')) { | ||
| 821 | out.push_back(std::move(item)); | ||
| 822 | } | ||
| 823 | |||
| 824 | return out; | ||
| 825 | } | ||
| 826 | |||
| 827 | std::string_view GetParentPath(std::string_view path) { | ||
| 828 | const auto name_bck_index = path.rfind('\\'); | ||
| 829 | const auto name_fwd_index = path.rfind('/'); | ||
| 830 | std::size_t name_index; | ||
| 831 | |||
| 832 | if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) { | ||
| 833 | name_index = std::min(name_bck_index, name_fwd_index); | ||
| 834 | } else { | ||
| 835 | name_index = std::max(name_bck_index, name_fwd_index); | ||
| 836 | } | ||
| 837 | |||
| 838 | return path.substr(0, name_index); | ||
| 839 | } | ||
| 840 | |||
| 841 | std::string_view GetPathWithoutTop(std::string_view path) { | ||
| 842 | if (path.empty()) { | ||
| 843 | return path; | ||
| 844 | } | ||
| 845 | |||
| 846 | while (path[0] == '\\' || path[0] == '/') { | ||
| 847 | path.remove_prefix(1); | ||
| 848 | if (path.empty()) { | ||
| 849 | return path; | ||
| 850 | } | ||
| 851 | } | ||
| 852 | |||
| 853 | const auto name_bck_index = path.find('\\'); | ||
| 854 | const auto name_fwd_index = path.find('/'); | ||
| 855 | return path.substr(std::min(name_bck_index, name_fwd_index) + 1); | ||
| 856 | } | ||
| 857 | |||
| 858 | std::string_view GetFilename(std::string_view path) { | ||
| 859 | const auto name_index = path.find_last_of("\\/"); | ||
| 860 | |||
| 861 | if (name_index == std::string_view::npos) { | ||
| 862 | return {}; | ||
| 863 | } | ||
| 864 | |||
| 865 | return path.substr(name_index + 1); | ||
| 866 | } | ||
| 867 | |||
| 868 | std::string_view GetExtensionFromFilename(std::string_view name) { | ||
| 869 | const std::size_t index = name.rfind('.'); | ||
| 870 | |||
| 871 | if (index == std::string_view::npos) { | ||
| 872 | return {}; | ||
| 873 | } | ||
| 874 | |||
| 875 | return name.substr(index + 1); | ||
| 876 | } | ||
| 877 | |||
| 878 | std::string_view RemoveTrailingSlash(std::string_view path) { | ||
| 879 | if (path.empty()) { | ||
| 880 | return path; | ||
| 881 | } | ||
| 882 | |||
| 883 | if (path.back() == '\\' || path.back() == '/') { | ||
| 884 | path.remove_suffix(1); | ||
| 885 | return path; | ||
| 886 | } | ||
| 887 | |||
| 888 | return path; | ||
| 889 | } | ||
| 890 | |||
| 891 | std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { | ||
| 892 | std::string path(path_); | ||
| 893 | char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\'; | ||
| 894 | char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/'; | ||
| 895 | |||
| 896 | if (directory_separator == DirectorySeparator::PlatformDefault) { | ||
| 897 | #ifdef _WIN32 | ||
| 898 | type1 = '/'; | ||
| 899 | type2 = '\\'; | ||
| 900 | #endif | ||
| 901 | } | ||
| 902 | |||
| 903 | std::replace(path.begin(), path.end(), type1, type2); | ||
| 904 | |||
| 905 | auto start = path.begin(); | ||
| 906 | #ifdef _WIN32 | ||
| 907 | // allow network paths which start with a double backslash (e.g. \\server\share) | ||
| 908 | if (start != path.end()) | ||
| 909 | ++start; | ||
| 910 | #endif | ||
| 911 | path.erase(std::unique(start, path.end(), | ||
| 912 | [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }), | ||
| 913 | path.end()); | ||
| 914 | return std::string(RemoveTrailingSlash(path)); | ||
| 915 | } | ||
| 916 | |||
| 917 | IOFile::IOFile() = default; | ||
| 918 | |||
| 919 | IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { | ||
| 920 | void(Open(filename, openmode, flags)); | ||
| 921 | } | ||
| 922 | |||
| 923 | IOFile::~IOFile() { | ||
| 924 | Close(); | ||
| 925 | } | ||
| 926 | |||
| 927 | IOFile::IOFile(IOFile&& other) noexcept { | ||
| 928 | Swap(other); | ||
| 929 | } | ||
| 930 | |||
| 931 | IOFile& IOFile::operator=(IOFile&& other) noexcept { | ||
| 932 | Swap(other); | ||
| 933 | return *this; | ||
| 934 | } | ||
| 935 | |||
| 936 | void IOFile::Swap(IOFile& other) noexcept { | ||
| 937 | std::swap(m_file, other.m_file); | ||
| 938 | } | ||
| 939 | |||
| 940 | bool IOFile::Open(const std::string& filename, const char openmode[], int flags) { | ||
| 941 | Close(); | ||
| 942 | bool m_good; | ||
| 943 | #ifdef _WIN32 | ||
| 944 | if (flags != 0) { | ||
| 945 | m_file = _wfsopen(Common::UTF8ToUTF16W(filename).c_str(), | ||
| 946 | Common::UTF8ToUTF16W(openmode).c_str(), flags); | ||
| 947 | m_good = m_file != nullptr; | ||
| 948 | } else { | ||
| 949 | m_good = _wfopen_s(&m_file, Common::UTF8ToUTF16W(filename).c_str(), | ||
| 950 | Common::UTF8ToUTF16W(openmode).c_str()) == 0; | ||
| 951 | } | ||
| 952 | #else | ||
| 953 | m_file = std::fopen(filename.c_str(), openmode); | ||
| 954 | m_good = m_file != nullptr; | ||
| 955 | #endif | ||
| 956 | |||
| 957 | return m_good; | ||
| 958 | } | ||
| 959 | |||
| 960 | bool IOFile::Close() { | ||
| 961 | if (!IsOpen() || 0 != std::fclose(m_file)) { | ||
| 962 | return false; | ||
| 963 | } | ||
| 964 | |||
| 965 | m_file = nullptr; | ||
| 966 | return true; | ||
| 967 | } | ||
| 968 | |||
| 969 | u64 IOFile::GetSize() const { | ||
| 970 | if (IsOpen()) { | ||
| 971 | return FS::GetSize(m_file); | ||
| 972 | } | ||
| 973 | return 0; | ||
| 974 | } | ||
| 975 | |||
| 976 | bool IOFile::Seek(s64 off, int origin) const { | ||
| 977 | return IsOpen() && 0 == fseeko(m_file, off, origin); | ||
| 978 | } | ||
| 979 | |||
| 980 | u64 IOFile::Tell() const { | ||
| 981 | if (IsOpen()) { | ||
| 982 | return ftello(m_file); | ||
| 983 | } | ||
| 984 | return std::numeric_limits<u64>::max(); | ||
| 985 | } | ||
| 986 | |||
| 987 | bool IOFile::Flush() { | ||
| 988 | return IsOpen() && 0 == std::fflush(m_file); | ||
| 989 | } | ||
| 990 | |||
| 991 | std::size_t IOFile::ReadImpl(void* data, std::size_t length, std::size_t data_size) const { | ||
| 992 | if (!IsOpen()) { | ||
| 993 | return std::numeric_limits<std::size_t>::max(); | ||
| 994 | } | ||
| 995 | |||
| 996 | if (length == 0) { | ||
| 997 | return 0; | ||
| 998 | } | ||
| 999 | |||
| 1000 | DEBUG_ASSERT(data != nullptr); | ||
| 1001 | |||
| 1002 | return std::fread(data, data_size, length, m_file); | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | std::size_t IOFile::WriteImpl(const void* data, std::size_t length, std::size_t data_size) { | ||
| 1006 | if (!IsOpen()) { | ||
| 1007 | return std::numeric_limits<std::size_t>::max(); | ||
| 1008 | } | ||
| 1009 | |||
| 1010 | if (length == 0) { | ||
| 1011 | return 0; | ||
| 1012 | } | ||
| 1013 | |||
| 1014 | DEBUG_ASSERT(data != nullptr); | ||
| 1015 | |||
| 1016 | return std::fwrite(data, data_size, length, m_file); | ||
| 1017 | } | ||
| 1018 | |||
| 1019 | bool IOFile::Resize(u64 size) { | ||
| 1020 | return IsOpen() && 0 == | ||
| 1021 | #ifdef _WIN32 | ||
| 1022 | // ector: _chsize sucks, not 64-bit safe | ||
| 1023 | // F|RES: changed to _chsize_s. i think it is 64-bit safe | ||
| 1024 | _chsize_s(_fileno(m_file), size) | ||
| 1025 | #else | ||
| 1026 | // TODO: handle 64bit and growing | ||
| 1027 | ftruncate(fileno(m_file), size) | ||
| 1028 | #endif | ||
| 1029 | ; | ||
| 1030 | } | ||
| 1031 | |||
| 1032 | } // namespace Common::FS | ||
diff --git a/src/common/file_util.h b/src/common/file_util.h deleted file mode 100644 index 840cde2a6..000000000 --- a/src/common/file_util.h +++ /dev/null | |||
| @@ -1,298 +0,0 @@ | |||
| 1 | // Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <cstdio> | ||
| 9 | #include <fstream> | ||
| 10 | #include <functional> | ||
| 11 | #include <limits> | ||
| 12 | #include <optional> | ||
| 13 | #include <string> | ||
| 14 | #include <string_view> | ||
| 15 | #include <type_traits> | ||
| 16 | #include <vector> | ||
| 17 | #include "common/common_types.h" | ||
| 18 | #ifdef _MSC_VER | ||
| 19 | #include "common/string_util.h" | ||
| 20 | #endif | ||
| 21 | |||
| 22 | namespace Common::FS { | ||
| 23 | |||
| 24 | // User paths for GetUserPath | ||
| 25 | enum class UserPath { | ||
| 26 | CacheDir, | ||
| 27 | ConfigDir, | ||
| 28 | KeysDir, | ||
| 29 | LogDir, | ||
| 30 | NANDDir, | ||
| 31 | RootDir, | ||
| 32 | SDMCDir, | ||
| 33 | LoadDir, | ||
| 34 | DumpDir, | ||
| 35 | ScreenshotsDir, | ||
| 36 | ShaderDir, | ||
| 37 | SysDataDir, | ||
| 38 | UserDir, | ||
| 39 | }; | ||
| 40 | |||
| 41 | // FileSystem tree node/ | ||
| 42 | struct FSTEntry { | ||
| 43 | bool isDirectory; | ||
| 44 | u64 size; // file length or number of entries from children | ||
| 45 | std::string physicalName; // name on disk | ||
| 46 | std::string virtualName; // name in FST names table | ||
| 47 | std::vector<FSTEntry> children; | ||
| 48 | }; | ||
| 49 | |||
| 50 | // Returns true if file filename exists | ||
| 51 | [[nodiscard]] bool Exists(const std::string& filename); | ||
| 52 | |||
| 53 | // Returns true if filename is a directory | ||
| 54 | [[nodiscard]] bool IsDirectory(const std::string& filename); | ||
| 55 | |||
| 56 | // Returns the size of filename (64bit) | ||
| 57 | [[nodiscard]] u64 GetSize(const std::string& filename); | ||
| 58 | |||
| 59 | // Overloaded GetSize, accepts file descriptor | ||
| 60 | [[nodiscard]] u64 GetSize(int fd); | ||
| 61 | |||
| 62 | // Overloaded GetSize, accepts FILE* | ||
| 63 | [[nodiscard]] u64 GetSize(FILE* f); | ||
| 64 | |||
| 65 | // Returns true if successful, or path already exists. | ||
| 66 | bool CreateDir(const std::string& filename); | ||
| 67 | |||
| 68 | // Creates the full path of fullPath returns true on success | ||
| 69 | bool CreateFullPath(const std::string& fullPath); | ||
| 70 | |||
| 71 | // Deletes a given filename, return true on success | ||
| 72 | // Doesn't supports deleting a directory | ||
| 73 | bool Delete(const std::string& filename); | ||
| 74 | |||
| 75 | // Deletes a directory filename, returns true on success | ||
| 76 | bool DeleteDir(const std::string& filename); | ||
| 77 | |||
| 78 | // renames file srcFilename to destFilename, returns true on success | ||
| 79 | bool Rename(const std::string& srcFilename, const std::string& destFilename); | ||
| 80 | |||
| 81 | // copies file srcFilename to destFilename, returns true on success | ||
| 82 | bool Copy(const std::string& srcFilename, const std::string& destFilename); | ||
| 83 | |||
| 84 | // creates an empty file filename, returns true on success | ||
| 85 | bool CreateEmptyFile(const std::string& filename); | ||
| 86 | |||
| 87 | /** | ||
| 88 | * @param num_entries_out to be assigned by the callable with the number of iterated directory | ||
| 89 | * entries, never null | ||
| 90 | * @param directory the path to the enclosing directory | ||
| 91 | * @param virtual_name the entry name, without any preceding directory info | ||
| 92 | * @return whether handling the entry succeeded | ||
| 93 | */ | ||
| 94 | using DirectoryEntryCallable = std::function<bool( | ||
| 95 | u64* num_entries_out, const std::string& directory, const std::string& virtual_name)>; | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Scans a directory, calling the callback for each file/directory contained within. | ||
| 99 | * If the callback returns failure, scanning halts and this function returns failure as well | ||
| 100 | * @param num_entries_out assigned by the function with the number of iterated directory entries, | ||
| 101 | * can be null | ||
| 102 | * @param directory the directory to scan | ||
| 103 | * @param callback The callback which will be called for each entry | ||
| 104 | * @return whether scanning the directory succeeded | ||
| 105 | */ | ||
| 106 | bool ForeachDirectoryEntry(u64* num_entries_out, const std::string& directory, | ||
| 107 | DirectoryEntryCallable callback); | ||
| 108 | |||
| 109 | /** | ||
| 110 | * Scans the directory tree, storing the results. | ||
| 111 | * @param directory the parent directory to start scanning from | ||
| 112 | * @param parent_entry FSTEntry where the filesystem tree results will be stored. | ||
| 113 | * @param recursion Number of children directories to read before giving up. | ||
| 114 | * @return the total number of files/directories found | ||
| 115 | */ | ||
| 116 | u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry, | ||
| 117 | unsigned int recursion = 0); | ||
| 118 | |||
| 119 | // deletes the given directory and anything under it. Returns true on success. | ||
| 120 | bool DeleteDirRecursively(const std::string& directory, unsigned int recursion = 256); | ||
| 121 | |||
| 122 | // Returns the current directory | ||
| 123 | [[nodiscard]] std::optional<std::string> GetCurrentDir(); | ||
| 124 | |||
| 125 | // Create directory and copy contents (does not overwrite existing files) | ||
| 126 | void CopyDir(const std::string& source_path, const std::string& dest_path); | ||
| 127 | |||
| 128 | // Set the current directory to given directory | ||
| 129 | bool SetCurrentDir(const std::string& directory); | ||
| 130 | |||
| 131 | // Returns a pointer to a string with a yuzu data dir in the user's home | ||
| 132 | // directory. To be used in "multi-user" mode (that is, installed). | ||
| 133 | const std::string& GetUserPath(UserPath path, const std::string& new_path = ""); | ||
| 134 | |||
| 135 | [[nodiscard]] std::string GetHactoolConfigurationPath(); | ||
| 136 | |||
| 137 | [[nodiscard]] std::string GetNANDRegistrationDir(bool system = false); | ||
| 138 | |||
| 139 | // Returns the path to where the sys file are | ||
| 140 | [[nodiscard]] std::string GetSysDirectory(); | ||
| 141 | |||
| 142 | #ifdef __APPLE__ | ||
| 143 | [[nodiscard]] std::string GetBundleDirectory(); | ||
| 144 | #endif | ||
| 145 | |||
| 146 | #ifdef _WIN32 | ||
| 147 | [[nodiscard]] const std::string& GetExeDirectory(); | ||
| 148 | [[nodiscard]] std::string AppDataRoamingDirectory(); | ||
| 149 | #endif | ||
| 150 | |||
| 151 | std::size_t WriteStringToFile(bool text_file, const std::string& filename, std::string_view str); | ||
| 152 | |||
| 153 | std::size_t ReadFileToString(bool text_file, const std::string& filename, std::string& str); | ||
| 154 | |||
| 155 | /** | ||
| 156 | * Splits the filename into 8.3 format | ||
| 157 | * Loosely implemented following https://en.wikipedia.org/wiki/8.3_filename | ||
| 158 | * @param filename The normal filename to use | ||
| 159 | * @param short_name A 9-char array in which the short name will be written | ||
| 160 | * @param extension A 4-char array in which the extension will be written | ||
| 161 | */ | ||
| 162 | void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, | ||
| 163 | std::array<char, 4>& extension); | ||
| 164 | |||
| 165 | // Splits the path on '/' or '\' and put the components into a vector | ||
| 166 | // i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } | ||
| 167 | [[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename); | ||
| 168 | |||
| 169 | // Gets all of the text up to the last '/' or '\' in the path. | ||
| 170 | [[nodiscard]] std::string_view GetParentPath(std::string_view path); | ||
| 171 | |||
| 172 | // Gets all of the text after the first '/' or '\' in the path. | ||
| 173 | [[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path); | ||
| 174 | |||
| 175 | // Gets the filename of the path | ||
| 176 | [[nodiscard]] std::string_view GetFilename(std::string_view path); | ||
| 177 | |||
| 178 | // Gets the extension of the filename | ||
| 179 | [[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name); | ||
| 180 | |||
| 181 | // Removes the final '/' or '\' if one exists | ||
| 182 | [[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path); | ||
| 183 | |||
| 184 | // Creates a new vector containing indices [first, last) from the original. | ||
| 185 | template <typename T> | ||
| 186 | [[nodiscard]] std::vector<T> SliceVector(const std::vector<T>& vector, std::size_t first, | ||
| 187 | std::size_t last) { | ||
| 188 | if (first >= last) { | ||
| 189 | return {}; | ||
| 190 | } | ||
| 191 | last = std::min<std::size_t>(last, vector.size()); | ||
| 192 | return std::vector<T>(vector.begin() + first, vector.begin() + first + last); | ||
| 193 | } | ||
| 194 | |||
| 195 | enum class DirectorySeparator { | ||
| 196 | ForwardSlash, | ||
| 197 | BackwardSlash, | ||
| 198 | PlatformDefault, | ||
| 199 | }; | ||
| 200 | |||
| 201 | // Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\' | ||
| 202 | // depending if directory_separator is BackwardSlash or PlatformDefault and running on windows | ||
| 203 | [[nodiscard]] std::string SanitizePath( | ||
| 204 | std::string_view path, | ||
| 205 | DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash); | ||
| 206 | |||
| 207 | // To deal with Windows being dumb at Unicode | ||
| 208 | template <typename T> | ||
| 209 | void OpenFStream(T& fstream, const std::string& filename, std::ios_base::openmode openmode) { | ||
| 210 | #ifdef _MSC_VER | ||
| 211 | fstream.open(Common::UTF8ToUTF16W(filename), openmode); | ||
| 212 | #else | ||
| 213 | fstream.open(filename, openmode); | ||
| 214 | #endif | ||
| 215 | } | ||
| 216 | |||
| 217 | // simple wrapper for cstdlib file functions to | ||
| 218 | // hopefully will make error checking easier | ||
| 219 | // and make forgetting an fclose() harder | ||
| 220 | class IOFile : public NonCopyable { | ||
| 221 | public: | ||
| 222 | IOFile(); | ||
| 223 | // flags is used for windows specific file open mode flags, which | ||
| 224 | // allows yuzu to open the logs in shared write mode, so that the file | ||
| 225 | // isn't considered "locked" while yuzu is open and people can open the log file and view it | ||
| 226 | IOFile(const std::string& filename, const char openmode[], int flags = 0); | ||
| 227 | |||
| 228 | ~IOFile(); | ||
| 229 | |||
| 230 | IOFile(IOFile&& other) noexcept; | ||
| 231 | IOFile& operator=(IOFile&& other) noexcept; | ||
| 232 | |||
| 233 | void Swap(IOFile& other) noexcept; | ||
| 234 | |||
| 235 | bool Open(const std::string& filename, const char openmode[], int flags = 0); | ||
| 236 | bool Close(); | ||
| 237 | |||
| 238 | template <typename T> | ||
| 239 | std::size_t ReadArray(T* data, std::size_t length) const { | ||
| 240 | static_assert(std::is_trivially_copyable_v<T>, | ||
| 241 | "Given array does not consist of trivially copyable objects"); | ||
| 242 | |||
| 243 | return ReadImpl(data, length, sizeof(T)); | ||
| 244 | } | ||
| 245 | |||
| 246 | template <typename T> | ||
| 247 | std::size_t WriteArray(const T* data, std::size_t length) { | ||
| 248 | static_assert(std::is_trivially_copyable_v<T>, | ||
| 249 | "Given array does not consist of trivially copyable objects"); | ||
| 250 | |||
| 251 | return WriteImpl(data, length, sizeof(T)); | ||
| 252 | } | ||
| 253 | |||
| 254 | template <typename T> | ||
| 255 | std::size_t ReadBytes(T* data, std::size_t length) const { | ||
| 256 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); | ||
| 257 | return ReadArray(reinterpret_cast<char*>(data), length); | ||
| 258 | } | ||
| 259 | |||
| 260 | template <typename T> | ||
| 261 | std::size_t WriteBytes(const T* data, std::size_t length) { | ||
| 262 | static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable"); | ||
| 263 | return WriteArray(reinterpret_cast<const char*>(data), length); | ||
| 264 | } | ||
| 265 | |||
| 266 | template <typename T> | ||
| 267 | std::size_t WriteObject(const T& object) { | ||
| 268 | static_assert(!std::is_pointer_v<T>, "WriteObject arguments must not be a pointer"); | ||
| 269 | return WriteArray(&object, 1); | ||
| 270 | } | ||
| 271 | |||
| 272 | std::size_t WriteString(std::string_view str) { | ||
| 273 | return WriteArray(str.data(), str.length()); | ||
| 274 | } | ||
| 275 | |||
| 276 | [[nodiscard]] bool IsOpen() const { | ||
| 277 | return nullptr != m_file; | ||
| 278 | } | ||
| 279 | |||
| 280 | bool Seek(s64 off, int origin) const; | ||
| 281 | [[nodiscard]] u64 Tell() const; | ||
| 282 | [[nodiscard]] u64 GetSize() const; | ||
| 283 | bool Resize(u64 size); | ||
| 284 | bool Flush(); | ||
| 285 | |||
| 286 | // clear error state | ||
| 287 | void Clear() { | ||
| 288 | std::clearerr(m_file); | ||
| 289 | } | ||
| 290 | |||
| 291 | private: | ||
| 292 | std::size_t ReadImpl(void* data, std::size_t length, std::size_t data_size) const; | ||
| 293 | std::size_t WriteImpl(const void* data, std::size_t length, std::size_t data_size); | ||
| 294 | |||
| 295 | std::FILE* m_file = nullptr; | ||
| 296 | }; | ||
| 297 | |||
| 298 | } // namespace Common::FS | ||
diff --git a/src/common/fs/file.cpp b/src/common/fs/file.cpp new file mode 100644 index 000000000..9f3de1cb0 --- /dev/null +++ b/src/common/fs/file.cpp | |||
| @@ -0,0 +1,392 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/fs/file.h" | ||
| 6 | #include "common/fs/fs.h" | ||
| 7 | #include "common/fs/path_util.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | |||
| 10 | #ifdef _WIN32 | ||
| 11 | #include <io.h> | ||
| 12 | #include <share.h> | ||
| 13 | #else | ||
| 14 | #include <unistd.h> | ||
| 15 | #endif | ||
| 16 | |||
| 17 | #ifdef _MSC_VER | ||
| 18 | #define fileno _fileno | ||
| 19 | #define fseeko _fseeki64 | ||
| 20 | #define ftello _ftelli64 | ||
| 21 | #endif | ||
| 22 | |||
| 23 | namespace Common::FS { | ||
| 24 | |||
| 25 | namespace fs = std::filesystem; | ||
| 26 | |||
| 27 | namespace { | ||
| 28 | |||
| 29 | #ifdef _WIN32 | ||
| 30 | |||
| 31 | /** | ||
| 32 | * Converts the file access mode and file type enums to a file access mode wide string. | ||
| 33 | * | ||
| 34 | * @param mode File access mode | ||
| 35 | * @param type File type | ||
| 36 | * | ||
| 37 | * @returns A pointer to a wide string representing the file access mode. | ||
| 38 | */ | ||
| 39 | [[nodiscard]] constexpr const wchar_t* AccessModeToWStr(FileAccessMode mode, FileType type) { | ||
| 40 | switch (type) { | ||
| 41 | case FileType::BinaryFile: | ||
| 42 | switch (mode) { | ||
| 43 | case FileAccessMode::Read: | ||
| 44 | return L"rb"; | ||
| 45 | case FileAccessMode::Write: | ||
| 46 | return L"wb"; | ||
| 47 | case FileAccessMode::Append: | ||
| 48 | return L"ab"; | ||
| 49 | case FileAccessMode::ReadWrite: | ||
| 50 | return L"r+b"; | ||
| 51 | case FileAccessMode::ReadAppend: | ||
| 52 | return L"a+b"; | ||
| 53 | } | ||
| 54 | break; | ||
| 55 | case FileType::TextFile: | ||
| 56 | switch (mode) { | ||
| 57 | case FileAccessMode::Read: | ||
| 58 | return L"r"; | ||
| 59 | case FileAccessMode::Write: | ||
| 60 | return L"w"; | ||
| 61 | case FileAccessMode::Append: | ||
| 62 | return L"a"; | ||
| 63 | case FileAccessMode::ReadWrite: | ||
| 64 | return L"r+"; | ||
| 65 | case FileAccessMode::ReadAppend: | ||
| 66 | return L"a+"; | ||
| 67 | } | ||
| 68 | break; | ||
| 69 | } | ||
| 70 | |||
| 71 | return L""; | ||
| 72 | } | ||
| 73 | |||
| 74 | /** | ||
| 75 | * Converts the file-share access flag enum to a Windows defined file-share access flag. | ||
| 76 | * | ||
| 77 | * @param flag File-share access flag | ||
| 78 | * | ||
| 79 | * @returns Windows defined file-share access flag. | ||
| 80 | */ | ||
| 81 | [[nodiscard]] constexpr int ToWindowsFileShareFlag(FileShareFlag flag) { | ||
| 82 | switch (flag) { | ||
| 83 | case FileShareFlag::ShareNone: | ||
| 84 | default: | ||
| 85 | return _SH_DENYRW; | ||
| 86 | case FileShareFlag::ShareReadOnly: | ||
| 87 | return _SH_DENYWR; | ||
| 88 | case FileShareFlag::ShareWriteOnly: | ||
| 89 | return _SH_DENYRD; | ||
| 90 | case FileShareFlag::ShareReadWrite: | ||
| 91 | return _SH_DENYNO; | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | #else | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Converts the file access mode and file type enums to a file access mode string. | ||
| 99 | * | ||
| 100 | * @param mode File access mode | ||
| 101 | * @param type File type | ||
| 102 | * | ||
| 103 | * @returns A pointer to a string representing the file access mode. | ||
| 104 | */ | ||
| 105 | [[nodiscard]] constexpr const char* AccessModeToStr(FileAccessMode mode, FileType type) { | ||
| 106 | switch (type) { | ||
| 107 | case FileType::BinaryFile: | ||
| 108 | switch (mode) { | ||
| 109 | case FileAccessMode::Read: | ||
| 110 | return "rb"; | ||
| 111 | case FileAccessMode::Write: | ||
| 112 | return "wb"; | ||
| 113 | case FileAccessMode::Append: | ||
| 114 | return "ab"; | ||
| 115 | case FileAccessMode::ReadWrite: | ||
| 116 | return "r+b"; | ||
| 117 | case FileAccessMode::ReadAppend: | ||
| 118 | return "a+b"; | ||
| 119 | } | ||
| 120 | break; | ||
| 121 | case FileType::TextFile: | ||
| 122 | switch (mode) { | ||
| 123 | case FileAccessMode::Read: | ||
| 124 | return "r"; | ||
| 125 | case FileAccessMode::Write: | ||
| 126 | return "w"; | ||
| 127 | case FileAccessMode::Append: | ||
| 128 | return "a"; | ||
| 129 | case FileAccessMode::ReadWrite: | ||
| 130 | return "r+"; | ||
| 131 | case FileAccessMode::ReadAppend: | ||
| 132 | return "a+"; | ||
| 133 | } | ||
| 134 | break; | ||
| 135 | } | ||
| 136 | |||
| 137 | return ""; | ||
| 138 | } | ||
| 139 | |||
| 140 | #endif | ||
| 141 | |||
| 142 | /** | ||
| 143 | * Converts the seek origin enum to a seek origin integer. | ||
| 144 | * | ||
| 145 | * @param origin Seek origin | ||
| 146 | * | ||
| 147 | * @returns Seek origin integer. | ||
| 148 | */ | ||
| 149 | [[nodiscard]] constexpr int ToSeekOrigin(SeekOrigin origin) { | ||
| 150 | switch (origin) { | ||
| 151 | case SeekOrigin::SetOrigin: | ||
| 152 | default: | ||
| 153 | return SEEK_SET; | ||
| 154 | case SeekOrigin::CurrentPosition: | ||
| 155 | return SEEK_CUR; | ||
| 156 | case SeekOrigin::End: | ||
| 157 | return SEEK_END; | ||
| 158 | } | ||
| 159 | } | ||
| 160 | |||
| 161 | } // Anonymous namespace | ||
| 162 | |||
| 163 | std::string ReadStringFromFile(const std::filesystem::path& path, FileType type) { | ||
| 164 | if (!IsFile(path)) { | ||
| 165 | return ""; | ||
| 166 | } | ||
| 167 | |||
| 168 | IOFile io_file{path, FileAccessMode::Read, type}; | ||
| 169 | |||
| 170 | return io_file.ReadString(io_file.GetSize()); | ||
| 171 | } | ||
| 172 | |||
| 173 | size_t WriteStringToFile(const std::filesystem::path& path, FileType type, | ||
| 174 | std::string_view string) { | ||
| 175 | if (!IsFile(path)) { | ||
| 176 | return 0; | ||
| 177 | } | ||
| 178 | |||
| 179 | IOFile io_file{path, FileAccessMode::Write, type}; | ||
| 180 | |||
| 181 | return io_file.WriteString(string); | ||
| 182 | } | ||
| 183 | |||
| 184 | size_t AppendStringToFile(const std::filesystem::path& path, FileType type, | ||
| 185 | std::string_view string) { | ||
| 186 | if (!Exists(path)) { | ||
| 187 | return WriteStringToFile(path, type, string); | ||
| 188 | } | ||
| 189 | |||
| 190 | if (!IsFile(path)) { | ||
| 191 | return 0; | ||
| 192 | } | ||
| 193 | |||
| 194 | IOFile io_file{path, FileAccessMode::Append, type}; | ||
| 195 | |||
| 196 | return io_file.WriteString(string); | ||
| 197 | } | ||
| 198 | |||
| 199 | IOFile::IOFile() = default; | ||
| 200 | |||
| 201 | IOFile::IOFile(const std::string& path, FileAccessMode mode, FileType type, FileShareFlag flag) { | ||
| 202 | Open(path, mode, type, flag); | ||
| 203 | } | ||
| 204 | |||
| 205 | IOFile::IOFile(std::string_view path, FileAccessMode mode, FileType type, FileShareFlag flag) { | ||
| 206 | Open(path, mode, type, flag); | ||
| 207 | } | ||
| 208 | |||
| 209 | IOFile::IOFile(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) { | ||
| 210 | Open(path, mode, type, flag); | ||
| 211 | } | ||
| 212 | |||
| 213 | IOFile::~IOFile() { | ||
| 214 | Close(); | ||
| 215 | } | ||
| 216 | |||
| 217 | IOFile::IOFile(IOFile&& other) noexcept { | ||
| 218 | std::swap(file_path, other.file_path); | ||
| 219 | std::swap(file_access_mode, other.file_access_mode); | ||
| 220 | std::swap(file_type, other.file_type); | ||
| 221 | std::swap(file, other.file); | ||
| 222 | } | ||
| 223 | |||
| 224 | IOFile& IOFile::operator=(IOFile&& other) noexcept { | ||
| 225 | std::swap(file_path, other.file_path); | ||
| 226 | std::swap(file_access_mode, other.file_access_mode); | ||
| 227 | std::swap(file_type, other.file_type); | ||
| 228 | std::swap(file, other.file); | ||
| 229 | return *this; | ||
| 230 | } | ||
| 231 | |||
| 232 | fs::path IOFile::GetPath() const { | ||
| 233 | return file_path; | ||
| 234 | } | ||
| 235 | |||
| 236 | FileAccessMode IOFile::GetAccessMode() const { | ||
| 237 | return file_access_mode; | ||
| 238 | } | ||
| 239 | |||
| 240 | FileType IOFile::GetType() const { | ||
| 241 | return file_type; | ||
| 242 | } | ||
| 243 | |||
| 244 | void IOFile::Open(const fs::path& path, FileAccessMode mode, FileType type, FileShareFlag flag) { | ||
| 245 | Close(); | ||
| 246 | |||
| 247 | file_path = path; | ||
| 248 | file_access_mode = mode; | ||
| 249 | file_type = type; | ||
| 250 | |||
| 251 | errno = 0; | ||
| 252 | |||
| 253 | #ifdef _WIN32 | ||
| 254 | if (flag != FileShareFlag::ShareNone) { | ||
| 255 | file = _wfsopen(path.c_str(), AccessModeToWStr(mode, type), ToWindowsFileShareFlag(flag)); | ||
| 256 | } else { | ||
| 257 | _wfopen_s(&file, path.c_str(), AccessModeToWStr(mode, type)); | ||
| 258 | } | ||
| 259 | #else | ||
| 260 | file = std::fopen(path.c_str(), AccessModeToStr(mode, type)); | ||
| 261 | #endif | ||
| 262 | |||
| 263 | if (!IsOpen()) { | ||
| 264 | const auto ec = std::error_code{errno, std::generic_category()}; | ||
| 265 | LOG_ERROR(Common_Filesystem, "Failed to open the file at path={}, ec_message={}", | ||
| 266 | PathToUTF8String(file_path), ec.message()); | ||
| 267 | } | ||
| 268 | } | ||
| 269 | |||
| 270 | void IOFile::Close() { | ||
| 271 | if (!IsOpen()) { | ||
| 272 | return; | ||
| 273 | } | ||
| 274 | |||
| 275 | errno = 0; | ||
| 276 | |||
| 277 | const auto close_result = std::fclose(file) == 0; | ||
| 278 | |||
| 279 | if (!close_result) { | ||
| 280 | const auto ec = std::error_code{errno, std::generic_category()}; | ||
| 281 | LOG_ERROR(Common_Filesystem, "Failed to close the file at path={}, ec_message={}", | ||
| 282 | PathToUTF8String(file_path), ec.message()); | ||
| 283 | } | ||
| 284 | |||
| 285 | file = nullptr; | ||
| 286 | } | ||
| 287 | |||
| 288 | bool IOFile::IsOpen() const { | ||
| 289 | return file != nullptr; | ||
| 290 | } | ||
| 291 | |||
| 292 | std::string IOFile::ReadString(size_t length) const { | ||
| 293 | std::vector<char> string_buffer(length); | ||
| 294 | |||
| 295 | const auto chars_read = ReadSpan<char>(string_buffer); | ||
| 296 | const auto string_size = chars_read != length ? chars_read : length; | ||
| 297 | |||
| 298 | return std::string{string_buffer.data(), string_size}; | ||
| 299 | } | ||
| 300 | |||
| 301 | size_t IOFile::WriteString(std::span<const char> string) const { | ||
| 302 | return WriteSpan(string); | ||
| 303 | } | ||
| 304 | |||
| 305 | bool IOFile::Flush() const { | ||
| 306 | if (!IsOpen()) { | ||
| 307 | return false; | ||
| 308 | } | ||
| 309 | |||
| 310 | errno = 0; | ||
| 311 | |||
| 312 | const auto flush_result = std::fflush(file) == 0; | ||
| 313 | |||
| 314 | if (!flush_result) { | ||
| 315 | const auto ec = std::error_code{errno, std::generic_category()}; | ||
| 316 | LOG_ERROR(Common_Filesystem, "Failed to flush the file at path={}, ec_message={}", | ||
| 317 | PathToUTF8String(file_path), ec.message()); | ||
| 318 | } | ||
| 319 | |||
| 320 | return flush_result; | ||
| 321 | } | ||
| 322 | |||
| 323 | bool IOFile::SetSize(u64 size) const { | ||
| 324 | if (!IsOpen()) { | ||
| 325 | return false; | ||
| 326 | } | ||
| 327 | |||
| 328 | errno = 0; | ||
| 329 | |||
| 330 | #ifdef _WIN32 | ||
| 331 | const auto set_size_result = _chsize_s(fileno(file), static_cast<s64>(size)) == 0; | ||
| 332 | #else | ||
| 333 | const auto set_size_result = ftruncate(fileno(file), static_cast<s64>(size)) == 0; | ||
| 334 | #endif | ||
| 335 | |||
| 336 | if (!set_size_result) { | ||
| 337 | const auto ec = std::error_code{errno, std::generic_category()}; | ||
| 338 | LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={}, size={}, ec_message={}", | ||
| 339 | PathToUTF8String(file_path), size, ec.message()); | ||
| 340 | } | ||
| 341 | |||
| 342 | return set_size_result; | ||
| 343 | } | ||
| 344 | |||
| 345 | u64 IOFile::GetSize() const { | ||
| 346 | if (!IsOpen()) { | ||
| 347 | return 0; | ||
| 348 | } | ||
| 349 | |||
| 350 | std::error_code ec; | ||
| 351 | |||
| 352 | const auto file_size = fs::file_size(file_path, ec); | ||
| 353 | |||
| 354 | if (ec) { | ||
| 355 | LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}", | ||
| 356 | PathToUTF8String(file_path), ec.message()); | ||
| 357 | return 0; | ||
| 358 | } | ||
| 359 | |||
| 360 | return file_size; | ||
| 361 | } | ||
| 362 | |||
| 363 | bool IOFile::Seek(s64 offset, SeekOrigin origin) const { | ||
| 364 | if (!IsOpen()) { | ||
| 365 | return false; | ||
| 366 | } | ||
| 367 | |||
| 368 | errno = 0; | ||
| 369 | |||
| 370 | const auto seek_result = fseeko(file, offset, ToSeekOrigin(origin)) == 0; | ||
| 371 | |||
| 372 | if (!seek_result) { | ||
| 373 | const auto ec = std::error_code{errno, std::generic_category()}; | ||
| 374 | LOG_ERROR(Common_Filesystem, | ||
| 375 | "Failed to seek the file at path={}, offset={}, origin={}, ec_message={}", | ||
| 376 | PathToUTF8String(file_path), offset, origin, ec.message()); | ||
| 377 | } | ||
| 378 | |||
| 379 | return seek_result; | ||
| 380 | } | ||
| 381 | |||
| 382 | s64 IOFile::Tell() const { | ||
| 383 | if (!IsOpen()) { | ||
| 384 | return 0; | ||
| 385 | } | ||
| 386 | |||
| 387 | errno = 0; | ||
| 388 | |||
| 389 | return ftello(file); | ||
| 390 | } | ||
| 391 | |||
| 392 | } // namespace Common::FS | ||
diff --git a/src/common/fs/file.h b/src/common/fs/file.h new file mode 100644 index 000000000..209f9664b --- /dev/null +++ b/src/common/fs/file.h | |||
| @@ -0,0 +1,450 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstdio> | ||
| 8 | #include <filesystem> | ||
| 9 | #include <fstream> | ||
| 10 | #include <span> | ||
| 11 | #include <type_traits> | ||
| 12 | #include <vector> | ||
| 13 | |||
| 14 | #include "common/concepts.h" | ||
| 15 | #include "common/fs/fs_types.h" | ||
| 16 | #include "common/fs/fs_util.h" | ||
| 17 | |||
| 18 | namespace Common::FS { | ||
| 19 | |||
| 20 | enum class SeekOrigin { | ||
| 21 | SetOrigin, // Seeks from the start of the file. | ||
| 22 | CurrentPosition, // Seeks from the current file pointer position. | ||
| 23 | End, // Seeks from the end of the file. | ||
| 24 | }; | ||
| 25 | |||
| 26 | /** | ||
| 27 | * Opens a file stream at path with the specified open mode. | ||
| 28 | * | ||
| 29 | * @param file_stream Reference to file stream | ||
| 30 | * @param path Filesystem path | ||
| 31 | * @param open_mode File stream open mode | ||
| 32 | */ | ||
| 33 | template <typename FileStream> | ||
| 34 | void OpenFileStream(FileStream& file_stream, const std::filesystem::path& path, | ||
| 35 | std::ios_base::openmode open_mode) { | ||
| 36 | file_stream.open(path, open_mode); | ||
| 37 | } | ||
| 38 | |||
| 39 | #ifdef _WIN32 | ||
| 40 | template <typename FileStream, typename Path> | ||
| 41 | void OpenFileStream(FileStream& file_stream, const Path& path, std::ios_base::openmode open_mode) { | ||
| 42 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 43 | file_stream.open(ToU8String(path), open_mode); | ||
| 44 | } else { | ||
| 45 | file_stream.open(std::filesystem::path{path}, open_mode); | ||
| 46 | } | ||
| 47 | } | ||
| 48 | #endif | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Reads an entire file at path and returns a string of the contents read from the file. | ||
| 52 | * If the filesystem object at path is not a file, this function returns an empty string. | ||
| 53 | * | ||
| 54 | * @param path Filesystem path | ||
| 55 | * @param type File type | ||
| 56 | * | ||
| 57 | * @returns A string of the contents read from the file. | ||
| 58 | */ | ||
| 59 | [[nodiscard]] std::string ReadStringFromFile(const std::filesystem::path& path, FileType type); | ||
| 60 | |||
| 61 | #ifdef _WIN32 | ||
| 62 | template <typename Path> | ||
| 63 | [[nodiscard]] std::string ReadStringFromFile(const Path& path, FileType type) { | ||
| 64 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 65 | return ReadStringFromFile(ToU8String(path), type); | ||
| 66 | } else { | ||
| 67 | return ReadStringFromFile(std::filesystem::path{path}, type); | ||
| 68 | } | ||
| 69 | } | ||
| 70 | #endif | ||
| 71 | |||
| 72 | /** | ||
| 73 | * Writes a string to a file at path and returns the number of characters successfully written. | ||
| 74 | * If an file already exists at path, its contents will be erased. | ||
| 75 | * If the filesystem object at path is not a file, this function returns 0. | ||
| 76 | * | ||
| 77 | * @param path Filesystem path | ||
| 78 | * @param type File type | ||
| 79 | * | ||
| 80 | * @returns Number of characters successfully written. | ||
| 81 | */ | ||
| 82 | [[nodiscard]] size_t WriteStringToFile(const std::filesystem::path& path, FileType type, | ||
| 83 | std::string_view string); | ||
| 84 | |||
| 85 | #ifdef _WIN32 | ||
| 86 | template <typename Path> | ||
| 87 | [[nodiscard]] size_t WriteStringToFile(const Path& path, FileType type, std::string_view string) { | ||
| 88 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 89 | return WriteStringToFile(ToU8String(path), type, string); | ||
| 90 | } else { | ||
| 91 | return WriteStringToFile(std::filesystem::path{path}, type, string); | ||
| 92 | } | ||
| 93 | } | ||
| 94 | #endif | ||
| 95 | |||
| 96 | /** | ||
| 97 | * Appends a string to a file at path and returns the number of characters successfully written. | ||
| 98 | * If a file does not exist at path, WriteStringToFile is called instead. | ||
| 99 | * If the filesystem object at path is not a file, this function returns 0. | ||
| 100 | * | ||
| 101 | * @param path Filesystem path | ||
| 102 | * @param type File type | ||
| 103 | * | ||
| 104 | * @returns Number of characters successfully written. | ||
| 105 | */ | ||
| 106 | [[nodiscard]] size_t AppendStringToFile(const std::filesystem::path& path, FileType type, | ||
| 107 | std::string_view string); | ||
| 108 | |||
| 109 | #ifdef _WIN32 | ||
| 110 | template <typename Path> | ||
| 111 | [[nodiscard]] size_t AppendStringToFile(const Path& path, FileType type, std::string_view string) { | ||
| 112 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 113 | return AppendStringToFile(ToU8String(path), type, string); | ||
| 114 | } else { | ||
| 115 | return AppendStringToFile(std::filesystem::path{path}, type, string); | ||
| 116 | } | ||
| 117 | } | ||
| 118 | #endif | ||
| 119 | |||
| 120 | class IOFile final : NonCopyable { | ||
| 121 | public: | ||
| 122 | IOFile(); | ||
| 123 | |||
| 124 | explicit IOFile(const std::string& path, FileAccessMode mode, | ||
| 125 | FileType type = FileType::BinaryFile, | ||
| 126 | FileShareFlag flag = FileShareFlag::ShareReadOnly); | ||
| 127 | |||
| 128 | explicit IOFile(std::string_view path, FileAccessMode mode, | ||
| 129 | FileType type = FileType::BinaryFile, | ||
| 130 | FileShareFlag flag = FileShareFlag::ShareReadOnly); | ||
| 131 | |||
| 132 | /** | ||
| 133 | * An IOFile is a lightweight wrapper on C Library file operations. | ||
| 134 | * Automatically closes an open file on the destruction of an IOFile object. | ||
| 135 | * | ||
| 136 | * @param path Filesystem path | ||
| 137 | * @param mode File access mode | ||
| 138 | * @param type File type, default is BinaryFile. Use TextFile to open the file as a text file | ||
| 139 | * @param flag (Windows only) File-share access flag, default is ShareReadOnly | ||
| 140 | */ | ||
| 141 | explicit IOFile(const std::filesystem::path& path, FileAccessMode mode, | ||
| 142 | FileType type = FileType::BinaryFile, | ||
| 143 | FileShareFlag flag = FileShareFlag::ShareReadOnly); | ||
| 144 | |||
| 145 | virtual ~IOFile(); | ||
| 146 | |||
| 147 | IOFile(IOFile&& other) noexcept; | ||
| 148 | IOFile& operator=(IOFile&& other) noexcept; | ||
| 149 | |||
| 150 | /** | ||
| 151 | * Gets the path of the file. | ||
| 152 | * | ||
| 153 | * @returns The path of the file. | ||
| 154 | */ | ||
| 155 | [[nodiscard]] std::filesystem::path GetPath() const; | ||
| 156 | |||
| 157 | /** | ||
| 158 | * Gets the access mode of the file. | ||
| 159 | * | ||
| 160 | * @returns The access mode of the file. | ||
| 161 | */ | ||
| 162 | [[nodiscard]] FileAccessMode GetAccessMode() const; | ||
| 163 | |||
| 164 | /** | ||
| 165 | * Gets the type of the file. | ||
| 166 | * | ||
| 167 | * @returns The type of the file. | ||
| 168 | */ | ||
| 169 | [[nodiscard]] FileType GetType() const; | ||
| 170 | |||
| 171 | /** | ||
| 172 | * Opens a file at path with the specified file access mode. | ||
| 173 | * This function behaves differently depending on the FileAccessMode. | ||
| 174 | * These behaviors are documented in each enum value of FileAccessMode. | ||
| 175 | * | ||
| 176 | * @param path Filesystem path | ||
| 177 | * @param mode File access mode | ||
| 178 | * @param type File type, default is BinaryFile. Use TextFile to open the file as a text file | ||
| 179 | * @param flag (Windows only) File-share access flag, default is ShareReadOnly | ||
| 180 | */ | ||
| 181 | void Open(const std::filesystem::path& path, FileAccessMode mode, | ||
| 182 | FileType type = FileType::BinaryFile, | ||
| 183 | FileShareFlag flag = FileShareFlag::ShareReadOnly); | ||
| 184 | |||
| 185 | #ifdef _WIN32 | ||
| 186 | template <typename Path> | ||
| 187 | [[nodiscard]] void Open(const Path& path, FileAccessMode mode, | ||
| 188 | FileType type = FileType::BinaryFile, | ||
| 189 | FileShareFlag flag = FileShareFlag::ShareReadOnly) { | ||
| 190 | using ValueType = typename Path::value_type; | ||
| 191 | if constexpr (IsChar<ValueType>) { | ||
| 192 | Open(ToU8String(path), mode, type, flag); | ||
| 193 | } else { | ||
| 194 | Open(std::filesystem::path{path}, mode, type, flag); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | #endif | ||
| 198 | |||
| 199 | /// Closes the file if it is opened. | ||
| 200 | void Close(); | ||
| 201 | |||
| 202 | /** | ||
| 203 | * Checks whether the file is open. | ||
| 204 | * Use this to check whether the calls to Open() or Close() succeeded. | ||
| 205 | * | ||
| 206 | * @returns True if the file is open, false otherwise. | ||
| 207 | */ | ||
| 208 | [[nodiscard]] bool IsOpen() const; | ||
| 209 | |||
| 210 | /** | ||
| 211 | * Helper function which deduces the value type of a contiguous STL container used in ReadSpan. | ||
| 212 | * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls | ||
| 213 | * ReadObject and T must be a trivially copyable object. | ||
| 214 | * | ||
| 215 | * See ReadSpan for more details if T is a contiguous container. | ||
| 216 | * See ReadObject for more details if T is a trivially copyable object. | ||
| 217 | * | ||
| 218 | * @tparam T Contiguous container or trivially copyable object | ||
| 219 | * | ||
| 220 | * @param data Container of T::value_type data or reference to object | ||
| 221 | * | ||
| 222 | * @returns Count of T::value_type data or objects successfully read. | ||
| 223 | */ | ||
| 224 | template <typename T> | ||
| 225 | [[nodiscard]] size_t Read(T& data) const { | ||
| 226 | if constexpr (IsSTLContainer<T>) { | ||
| 227 | using ContiguousType = typename T::value_type; | ||
| 228 | static_assert(std::is_trivially_copyable_v<ContiguousType>, | ||
| 229 | "Data type must be trivially copyable."); | ||
| 230 | return ReadSpan<ContiguousType>(data); | ||
| 231 | } else { | ||
| 232 | return ReadObject(data) ? 1 : 0; | ||
| 233 | } | ||
| 234 | } | ||
| 235 | |||
| 236 | /** | ||
| 237 | * Helper function which deduces the value type of a contiguous STL container used in WriteSpan. | ||
| 238 | * If T is not a contiguous STL container as defined by the concept IsSTLContainer, this calls | ||
| 239 | * WriteObject and T must be a trivially copyable object. | ||
| 240 | * | ||
| 241 | * See WriteSpan for more details if T is a contiguous container. | ||
| 242 | * See WriteObject for more details if T is a trivially copyable object. | ||
| 243 | * | ||
| 244 | * @tparam T Contiguous container or trivially copyable object | ||
| 245 | * | ||
| 246 | * @param data Container of T::value_type data or const reference to object | ||
| 247 | * | ||
| 248 | * @returns Count of T::value_type data or objects successfully written. | ||
| 249 | */ | ||
| 250 | template <typename T> | ||
| 251 | [[nodiscard]] size_t Write(const T& data) const { | ||
| 252 | if constexpr (IsSTLContainer<T>) { | ||
| 253 | using ContiguousType = typename T::value_type; | ||
| 254 | static_assert(std::is_trivially_copyable_v<ContiguousType>, | ||
| 255 | "Data type must be trivially copyable."); | ||
| 256 | return WriteSpan<ContiguousType>(data); | ||
| 257 | } else { | ||
| 258 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | ||
| 259 | return WriteObject(data) ? 1 : 0; | ||
| 260 | } | ||
| 261 | } | ||
| 262 | |||
| 263 | /** | ||
| 264 | * Reads a span of T data from a file sequentially. | ||
| 265 | * This function reads from the current position of the file pointer and | ||
| 266 | * advances it by the (count of T * sizeof(T)) bytes successfully read. | ||
| 267 | * | ||
| 268 | * Failures occur when: | ||
| 269 | * - The file is not open | ||
| 270 | * - The opened file lacks read permissions | ||
| 271 | * - Attempting to read beyond the end-of-file | ||
| 272 | * | ||
| 273 | * @tparam T Data type | ||
| 274 | * | ||
| 275 | * @param data Span of T data | ||
| 276 | * | ||
| 277 | * @returns Count of T data successfully read. | ||
| 278 | */ | ||
| 279 | template <typename T> | ||
| 280 | [[nodiscard]] size_t ReadSpan(std::span<T> data) const { | ||
| 281 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | ||
| 282 | |||
| 283 | if (!IsOpen()) { | ||
| 284 | return 0; | ||
| 285 | } | ||
| 286 | |||
| 287 | return std::fread(data.data(), sizeof(T), data.size(), file); | ||
| 288 | } | ||
| 289 | |||
| 290 | /** | ||
| 291 | * Writes a span of T data to a file sequentially. | ||
| 292 | * This function writes from the current position of the file pointer and | ||
| 293 | * advances it by the (count of T * sizeof(T)) bytes successfully written. | ||
| 294 | * | ||
| 295 | * Failures occur when: | ||
| 296 | * - The file is not open | ||
| 297 | * - The opened file lacks write permissions | ||
| 298 | * | ||
| 299 | * @tparam T Data type | ||
| 300 | * | ||
| 301 | * @param data Span of T data | ||
| 302 | * | ||
| 303 | * @returns Count of T data successfully written. | ||
| 304 | */ | ||
| 305 | template <typename T> | ||
| 306 | [[nodiscard]] size_t WriteSpan(std::span<const T> data) const { | ||
| 307 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | ||
| 308 | |||
| 309 | if (!IsOpen()) { | ||
| 310 | return 0; | ||
| 311 | } | ||
| 312 | |||
| 313 | return std::fwrite(data.data(), sizeof(T), data.size(), file); | ||
| 314 | } | ||
| 315 | |||
| 316 | /** | ||
| 317 | * Reads a T object from a file sequentially. | ||
| 318 | * This function reads from the current position of the file pointer and | ||
| 319 | * advances it by the sizeof(T) bytes successfully read. | ||
| 320 | * | ||
| 321 | * Failures occur when: | ||
| 322 | * - The file is not open | ||
| 323 | * - The opened file lacks read permissions | ||
| 324 | * - Attempting to read beyond the end-of-file | ||
| 325 | * | ||
| 326 | * @tparam T Data type | ||
| 327 | * | ||
| 328 | * @param object Reference to object | ||
| 329 | * | ||
| 330 | * @returns True if the object is successfully read from the file, false otherwise. | ||
| 331 | */ | ||
| 332 | template <typename T> | ||
| 333 | [[nodiscard]] bool ReadObject(T& object) const { | ||
| 334 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | ||
| 335 | static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object."); | ||
| 336 | |||
| 337 | if (!IsOpen()) { | ||
| 338 | return false; | ||
| 339 | } | ||
| 340 | |||
| 341 | return std::fread(&object, sizeof(T), 1, file) == 1; | ||
| 342 | } | ||
| 343 | |||
| 344 | /** | ||
| 345 | * Writes a T object to a file sequentially. | ||
| 346 | * This function writes from the current position of the file pointer and | ||
| 347 | * advances it by the sizeof(T) bytes successfully written. | ||
| 348 | * | ||
| 349 | * Failures occur when: | ||
| 350 | * - The file is not open | ||
| 351 | * - The opened file lacks write permissions | ||
| 352 | * | ||
| 353 | * @tparam T Data type | ||
| 354 | * | ||
| 355 | * @param object Const reference to object | ||
| 356 | * | ||
| 357 | * @returns True if the object is successfully written to the file, false otherwise. | ||
| 358 | */ | ||
| 359 | template <typename T> | ||
| 360 | [[nodiscard]] bool WriteObject(const T& object) const { | ||
| 361 | static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); | ||
| 362 | static_assert(!std::is_pointer_v<T>, "T must not be a pointer to an object."); | ||
| 363 | |||
| 364 | if (!IsOpen()) { | ||
| 365 | return false; | ||
| 366 | } | ||
| 367 | |||
| 368 | return std::fwrite(&object, sizeof(T), 1, file) == 1; | ||
| 369 | } | ||
| 370 | |||
| 371 | /** | ||
| 372 | * Specialized function to read a string of a given length from a file sequentially. | ||
| 373 | * This function writes from the current position of the file pointer and | ||
| 374 | * advances it by the number of characters successfully read. | ||
| 375 | * The size of the returned string may not match length if not all bytes are successfully read. | ||
| 376 | * | ||
| 377 | * @param length Length of the string | ||
| 378 | * | ||
| 379 | * @returns A string read from the file. | ||
| 380 | */ | ||
| 381 | [[nodiscard]] std::string ReadString(size_t length) const; | ||
| 382 | |||
| 383 | /** | ||
| 384 | * Specialized function to write a string to a file sequentially. | ||
| 385 | * This function writes from the current position of the file pointer and | ||
| 386 | * advances it by the number of characters successfully written. | ||
| 387 | * | ||
| 388 | * @param string Span of const char backed std::string or std::string_view | ||
| 389 | * | ||
| 390 | * @returns Number of characters successfully written. | ||
| 391 | */ | ||
| 392 | [[nodiscard]] size_t WriteString(std::span<const char> string) const; | ||
| 393 | |||
| 394 | /** | ||
| 395 | * Flushes any unwritten buffered data into the file. | ||
| 396 | * | ||
| 397 | * @returns True if the flush was successful, false otherwise. | ||
| 398 | */ | ||
| 399 | [[nodiscard]] bool Flush() const; | ||
| 400 | |||
| 401 | /** | ||
| 402 | * Resizes the file to a given size. | ||
| 403 | * If the file is resized to a smaller size, the remainder of the file is discarded. | ||
| 404 | * If the file is resized to a larger size, the new area appears as if zero-filled. | ||
| 405 | * | ||
| 406 | * Failures occur when: | ||
| 407 | * - The file is not open | ||
| 408 | * | ||
| 409 | * @param size File size in bytes | ||
| 410 | * | ||
| 411 | * @returns True if the file resize succeeded, false otherwise. | ||
| 412 | */ | ||
| 413 | [[nodiscard]] bool SetSize(u64 size) const; | ||
| 414 | |||
| 415 | /** | ||
| 416 | * Gets the size of the file. | ||
| 417 | * | ||
| 418 | * Failures occur when: | ||
| 419 | * - The file is not open | ||
| 420 | * | ||
| 421 | * @returns The file size in bytes of the file. Returns 0 on failure. | ||
| 422 | */ | ||
| 423 | [[nodiscard]] u64 GetSize() const; | ||
| 424 | |||
| 425 | /** | ||
| 426 | * Moves the current position of the file pointer with the specified offset and seek origin. | ||
| 427 | * | ||
| 428 | * @param offset Offset from seek origin | ||
| 429 | * @param origin Seek origin | ||
| 430 | * | ||
| 431 | * @returns True if the file pointer has moved to the specified offset, false otherwise. | ||
| 432 | */ | ||
| 433 | [[nodiscard]] bool Seek(s64 offset, SeekOrigin origin = SeekOrigin::SetOrigin) const; | ||
| 434 | |||
| 435 | /** | ||
| 436 | * Gets the current position of the file pointer. | ||
| 437 | * | ||
| 438 | * @returns The current position of the file pointer. | ||
| 439 | */ | ||
| 440 | [[nodiscard]] s64 Tell() const; | ||
| 441 | |||
| 442 | private: | ||
| 443 | std::filesystem::path file_path; | ||
| 444 | FileAccessMode file_access_mode; | ||
| 445 | FileType file_type; | ||
| 446 | |||
| 447 | std::FILE* file = nullptr; | ||
| 448 | }; | ||
| 449 | |||
| 450 | } // namespace Common::FS | ||
diff --git a/src/common/fs/fs.cpp b/src/common/fs/fs.cpp new file mode 100644 index 000000000..d492480d9 --- /dev/null +++ b/src/common/fs/fs.cpp | |||
| @@ -0,0 +1,610 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/fs/file.h" | ||
| 6 | #include "common/fs/fs.h" | ||
| 7 | #include "common/fs/path_util.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | |||
| 10 | namespace Common::FS { | ||
| 11 | |||
| 12 | namespace fs = std::filesystem; | ||
| 13 | |||
| 14 | // File Operations | ||
| 15 | |||
| 16 | bool NewFile(const fs::path& path, u64 size) { | ||
| 17 | if (!ValidatePath(path)) { | ||
| 18 | LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); | ||
| 19 | return false; | ||
| 20 | } | ||
| 21 | |||
| 22 | if (!Exists(path.parent_path())) { | ||
| 23 | LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist", | ||
| 24 | PathToUTF8String(path)); | ||
| 25 | return false; | ||
| 26 | } | ||
| 27 | |||
| 28 | if (Exists(path)) { | ||
| 29 | LOG_ERROR(Common_Filesystem, "Filesystem object at path={} exists", PathToUTF8String(path)); | ||
| 30 | return false; | ||
| 31 | } | ||
| 32 | |||
| 33 | IOFile io_file{path, FileAccessMode::Write}; | ||
| 34 | |||
| 35 | if (!io_file.IsOpen()) { | ||
| 36 | LOG_ERROR(Common_Filesystem, "Failed to create a file at path={}", PathToUTF8String(path)); | ||
| 37 | return false; | ||
| 38 | } | ||
| 39 | |||
| 40 | if (!io_file.SetSize(size)) { | ||
| 41 | LOG_ERROR(Common_Filesystem, "Failed to resize the file at path={} to size={}", | ||
| 42 | PathToUTF8String(path), size); | ||
| 43 | return false; | ||
| 44 | } | ||
| 45 | |||
| 46 | io_file.Close(); | ||
| 47 | |||
| 48 | LOG_DEBUG(Common_Filesystem, "Successfully created a file at path={} with size={}", | ||
| 49 | PathToUTF8String(path), size); | ||
| 50 | |||
| 51 | return true; | ||
| 52 | } | ||
| 53 | |||
| 54 | bool RemoveFile(const fs::path& path) { | ||
| 55 | if (!ValidatePath(path)) { | ||
| 56 | LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); | ||
| 57 | return false; | ||
| 58 | } | ||
| 59 | |||
| 60 | if (!Exists(path)) { | ||
| 61 | LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist", | ||
| 62 | PathToUTF8String(path)); | ||
| 63 | return true; | ||
| 64 | } | ||
| 65 | |||
| 66 | if (!IsFile(path)) { | ||
| 67 | LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a file", | ||
| 68 | PathToUTF8String(path)); | ||
| 69 | return false; | ||
| 70 | } | ||
| 71 | |||
| 72 | std::error_code ec; | ||
| 73 | |||
| 74 | fs::remove(path, ec); | ||
| 75 | |||
| 76 | if (ec) { | ||
| 77 | LOG_ERROR(Common_Filesystem, "Failed to remove the file at path={}, ec_message={}", | ||
| 78 | PathToUTF8String(path), ec.message()); | ||
| 79 | return false; | ||
| 80 | } | ||
| 81 | |||
| 82 | LOG_DEBUG(Common_Filesystem, "Successfully removed the file at path={}", | ||
| 83 | PathToUTF8String(path)); | ||
| 84 | |||
| 85 | return true; | ||
| 86 | } | ||
| 87 | |||
| 88 | bool RenameFile(const fs::path& old_path, const fs::path& new_path) { | ||
| 89 | if (!ValidatePath(old_path) || !ValidatePath(new_path)) { | ||
| 90 | LOG_ERROR(Common_Filesystem, | ||
| 91 | "One or both input path(s) is not valid, old_path={}, new_path={}", | ||
| 92 | PathToUTF8String(old_path), PathToUTF8String(new_path)); | ||
| 93 | return false; | ||
| 94 | } | ||
| 95 | |||
| 96 | if (!Exists(old_path)) { | ||
| 97 | LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} does not exist", | ||
| 98 | PathToUTF8String(old_path)); | ||
| 99 | return false; | ||
| 100 | } | ||
| 101 | |||
| 102 | if (!IsFile(old_path)) { | ||
| 103 | LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} is not a file", | ||
| 104 | PathToUTF8String(old_path)); | ||
| 105 | return false; | ||
| 106 | } | ||
| 107 | |||
| 108 | if (Exists(new_path)) { | ||
| 109 | LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} exists", | ||
| 110 | PathToUTF8String(new_path)); | ||
| 111 | return false; | ||
| 112 | } | ||
| 113 | |||
| 114 | std::error_code ec; | ||
| 115 | |||
| 116 | fs::rename(old_path, new_path, ec); | ||
| 117 | |||
| 118 | if (ec) { | ||
| 119 | LOG_ERROR(Common_Filesystem, | ||
| 120 | "Failed to rename the file from old_path={} to new_path={}, ec_message={}", | ||
| 121 | PathToUTF8String(old_path), PathToUTF8String(new_path), ec.message()); | ||
| 122 | return false; | ||
| 123 | } | ||
| 124 | |||
| 125 | LOG_DEBUG(Common_Filesystem, "Successfully renamed the file from old_path={} to new_path={}", | ||
| 126 | PathToUTF8String(old_path), PathToUTF8String(new_path)); | ||
| 127 | |||
| 128 | return true; | ||
| 129 | } | ||
| 130 | |||
| 131 | std::shared_ptr<IOFile> FileOpen(const fs::path& path, FileAccessMode mode, FileType type, | ||
| 132 | FileShareFlag flag) { | ||
| 133 | if (!ValidatePath(path)) { | ||
| 134 | LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); | ||
| 135 | return nullptr; | ||
| 136 | } | ||
| 137 | |||
| 138 | if (!IsFile(path)) { | ||
| 139 | LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a file", | ||
| 140 | PathToUTF8String(path)); | ||
| 141 | return nullptr; | ||
| 142 | } | ||
| 143 | |||
| 144 | auto io_file = std::make_shared<IOFile>(path, mode, type, flag); | ||
| 145 | |||
| 146 | if (!io_file->IsOpen()) { | ||
| 147 | io_file.reset(); | ||
| 148 | |||
| 149 | LOG_ERROR(Common_Filesystem, | ||
| 150 | "Failed to open the file at path={} with mode={}, type={}, flag={}", | ||
| 151 | PathToUTF8String(path), mode, type, flag); | ||
| 152 | |||
| 153 | return nullptr; | ||
| 154 | } | ||
| 155 | |||
| 156 | LOG_DEBUG(Common_Filesystem, | ||
| 157 | "Successfully opened the file at path={} with mode={}, type={}, flag={}", | ||
| 158 | PathToUTF8String(path), mode, type, flag); | ||
| 159 | |||
| 160 | return io_file; | ||
| 161 | } | ||
| 162 | |||
| 163 | // Directory Operations | ||
| 164 | |||
| 165 | bool CreateDir(const fs::path& path) { | ||
| 166 | if (!ValidatePath(path)) { | ||
| 167 | LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); | ||
| 168 | return false; | ||
| 169 | } | ||
| 170 | |||
| 171 | if (!Exists(path.parent_path())) { | ||
| 172 | LOG_ERROR(Common_Filesystem, "Parent directory of path={} does not exist", | ||
| 173 | PathToUTF8String(path)); | ||
| 174 | return false; | ||
| 175 | } | ||
| 176 | |||
| 177 | if (IsDir(path)) { | ||
| 178 | LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory", | ||
| 179 | PathToUTF8String(path)); | ||
| 180 | return true; | ||
| 181 | } | ||
| 182 | |||
| 183 | std::error_code ec; | ||
| 184 | |||
| 185 | fs::create_directory(path, ec); | ||
| 186 | |||
| 187 | if (ec) { | ||
| 188 | LOG_ERROR(Common_Filesystem, "Failed to create the directory at path={}, ec_message={}", | ||
| 189 | PathToUTF8String(path), ec.message()); | ||
| 190 | return false; | ||
| 191 | } | ||
| 192 | |||
| 193 | LOG_DEBUG(Common_Filesystem, "Successfully created the directory at path={}", | ||
| 194 | PathToUTF8String(path)); | ||
| 195 | |||
| 196 | return true; | ||
| 197 | } | ||
| 198 | |||
| 199 | bool CreateDirs(const fs::path& path) { | ||
| 200 | if (!ValidatePath(path)) { | ||
| 201 | LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); | ||
| 202 | return false; | ||
| 203 | } | ||
| 204 | |||
| 205 | if (IsDir(path)) { | ||
| 206 | LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} exists and is a directory", | ||
| 207 | PathToUTF8String(path)); | ||
| 208 | return true; | ||
| 209 | } | ||
| 210 | |||
| 211 | std::error_code ec; | ||
| 212 | |||
| 213 | fs::create_directories(path, ec); | ||
| 214 | |||
| 215 | if (ec) { | ||
| 216 | LOG_ERROR(Common_Filesystem, "Failed to create the directories at path={}, ec_message={}", | ||
| 217 | PathToUTF8String(path), ec.message()); | ||
| 218 | return false; | ||
| 219 | } | ||
| 220 | |||
| 221 | LOG_DEBUG(Common_Filesystem, "Successfully created the directories at path={}", | ||
| 222 | PathToUTF8String(path)); | ||
| 223 | |||
| 224 | return true; | ||
| 225 | } | ||
| 226 | |||
| 227 | bool CreateParentDir(const fs::path& path) { | ||
| 228 | return CreateDir(path.parent_path()); | ||
| 229 | } | ||
| 230 | |||
| 231 | bool CreateParentDirs(const fs::path& path) { | ||
| 232 | return CreateDirs(path.parent_path()); | ||
| 233 | } | ||
| 234 | |||
| 235 | bool RemoveDir(const fs::path& path) { | ||
| 236 | if (!ValidatePath(path)) { | ||
| 237 | LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); | ||
| 238 | return false; | ||
| 239 | } | ||
| 240 | |||
| 241 | if (!Exists(path)) { | ||
| 242 | LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist", | ||
| 243 | PathToUTF8String(path)); | ||
| 244 | return true; | ||
| 245 | } | ||
| 246 | |||
| 247 | if (!IsDir(path)) { | ||
| 248 | LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", | ||
| 249 | PathToUTF8String(path)); | ||
| 250 | return false; | ||
| 251 | } | ||
| 252 | |||
| 253 | std::error_code ec; | ||
| 254 | |||
| 255 | fs::remove(path, ec); | ||
| 256 | |||
| 257 | if (ec) { | ||
| 258 | LOG_ERROR(Common_Filesystem, "Failed to remove the directory at path={}, ec_message={}", | ||
| 259 | PathToUTF8String(path), ec.message()); | ||
| 260 | return false; | ||
| 261 | } | ||
| 262 | |||
| 263 | LOG_DEBUG(Common_Filesystem, "Successfully removed the directory at path={}", | ||
| 264 | PathToUTF8String(path)); | ||
| 265 | |||
| 266 | return true; | ||
| 267 | } | ||
| 268 | |||
| 269 | bool RemoveDirRecursively(const fs::path& path) { | ||
| 270 | if (!ValidatePath(path)) { | ||
| 271 | LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); | ||
| 272 | return false; | ||
| 273 | } | ||
| 274 | |||
| 275 | if (!Exists(path)) { | ||
| 276 | LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist", | ||
| 277 | PathToUTF8String(path)); | ||
| 278 | return true; | ||
| 279 | } | ||
| 280 | |||
| 281 | if (!IsDir(path)) { | ||
| 282 | LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", | ||
| 283 | PathToUTF8String(path)); | ||
| 284 | return false; | ||
| 285 | } | ||
| 286 | |||
| 287 | std::error_code ec; | ||
| 288 | |||
| 289 | fs::remove_all(path, ec); | ||
| 290 | |||
| 291 | if (ec) { | ||
| 292 | LOG_ERROR(Common_Filesystem, | ||
| 293 | "Failed to remove the directory and its contents at path={}, ec_message={}", | ||
| 294 | PathToUTF8String(path), ec.message()); | ||
| 295 | return false; | ||
| 296 | } | ||
| 297 | |||
| 298 | LOG_DEBUG(Common_Filesystem, "Successfully removed the directory and its contents at path={}", | ||
| 299 | PathToUTF8String(path)); | ||
| 300 | |||
| 301 | return true; | ||
| 302 | } | ||
| 303 | |||
| 304 | bool RemoveDirContentsRecursively(const fs::path& path) { | ||
| 305 | if (!ValidatePath(path)) { | ||
| 306 | LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); | ||
| 307 | return false; | ||
| 308 | } | ||
| 309 | |||
| 310 | if (!Exists(path)) { | ||
| 311 | LOG_DEBUG(Common_Filesystem, "Filesystem object at path={} does not exist", | ||
| 312 | PathToUTF8String(path)); | ||
| 313 | return true; | ||
| 314 | } | ||
| 315 | |||
| 316 | if (!IsDir(path)) { | ||
| 317 | LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", | ||
| 318 | PathToUTF8String(path)); | ||
| 319 | return false; | ||
| 320 | } | ||
| 321 | |||
| 322 | std::error_code ec; | ||
| 323 | |||
| 324 | for (const auto& entry : fs::recursive_directory_iterator(path, ec)) { | ||
| 325 | if (ec) { | ||
| 326 | LOG_ERROR(Common_Filesystem, | ||
| 327 | "Failed to completely enumerate the directory at path={}, ec_message={}", | ||
| 328 | PathToUTF8String(path), ec.message()); | ||
| 329 | break; | ||
| 330 | } | ||
| 331 | |||
| 332 | fs::remove(entry.path(), ec); | ||
| 333 | |||
| 334 | if (ec) { | ||
| 335 | LOG_ERROR(Common_Filesystem, | ||
| 336 | "Failed to remove the filesystem object at path={}, ec_message={}", | ||
| 337 | PathToUTF8String(entry.path()), ec.message()); | ||
| 338 | break; | ||
| 339 | } | ||
| 340 | } | ||
| 341 | |||
| 342 | if (ec) { | ||
| 343 | LOG_ERROR(Common_Filesystem, | ||
| 344 | "Failed to remove all the contents of the directory at path={}, ec_message={}", | ||
| 345 | PathToUTF8String(path), ec.message()); | ||
| 346 | return false; | ||
| 347 | } | ||
| 348 | |||
| 349 | LOG_DEBUG(Common_Filesystem, | ||
| 350 | "Successfully removed all the contents of the directory at path={}", | ||
| 351 | PathToUTF8String(path)); | ||
| 352 | |||
| 353 | return true; | ||
| 354 | } | ||
| 355 | |||
| 356 | bool RenameDir(const fs::path& old_path, const fs::path& new_path) { | ||
| 357 | if (!ValidatePath(old_path) || !ValidatePath(new_path)) { | ||
| 358 | LOG_ERROR(Common_Filesystem, | ||
| 359 | "One or both input path(s) is not valid, old_path={}, new_path={}", | ||
| 360 | PathToUTF8String(old_path), PathToUTF8String(new_path)); | ||
| 361 | return false; | ||
| 362 | } | ||
| 363 | |||
| 364 | if (!Exists(old_path)) { | ||
| 365 | LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} does not exist", | ||
| 366 | PathToUTF8String(old_path)); | ||
| 367 | return false; | ||
| 368 | } | ||
| 369 | |||
| 370 | if (!IsDir(old_path)) { | ||
| 371 | LOG_ERROR(Common_Filesystem, "Filesystem object at old_path={} is not a directory", | ||
| 372 | PathToUTF8String(old_path)); | ||
| 373 | return false; | ||
| 374 | } | ||
| 375 | |||
| 376 | if (Exists(new_path)) { | ||
| 377 | LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} exists", | ||
| 378 | PathToUTF8String(new_path)); | ||
| 379 | return false; | ||
| 380 | } | ||
| 381 | |||
| 382 | std::error_code ec; | ||
| 383 | |||
| 384 | fs::rename(old_path, new_path, ec); | ||
| 385 | |||
| 386 | if (ec) { | ||
| 387 | LOG_ERROR(Common_Filesystem, | ||
| 388 | "Failed to rename the file from old_path={} to new_path={}, ec_message={}", | ||
| 389 | PathToUTF8String(old_path), PathToUTF8String(new_path), ec.message()); | ||
| 390 | return false; | ||
| 391 | } | ||
| 392 | |||
| 393 | LOG_DEBUG(Common_Filesystem, "Successfully renamed the file from old_path={} to new_path={}", | ||
| 394 | PathToUTF8String(old_path), PathToUTF8String(new_path)); | ||
| 395 | |||
| 396 | return true; | ||
| 397 | } | ||
| 398 | |||
| 399 | void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable& callback, | ||
| 400 | DirEntryFilter filter) { | ||
| 401 | if (!ValidatePath(path)) { | ||
| 402 | LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); | ||
| 403 | return; | ||
| 404 | } | ||
| 405 | |||
| 406 | if (!Exists(path)) { | ||
| 407 | LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist", | ||
| 408 | PathToUTF8String(path)); | ||
| 409 | return; | ||
| 410 | } | ||
| 411 | |||
| 412 | if (!IsDir(path)) { | ||
| 413 | LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", | ||
| 414 | PathToUTF8String(path)); | ||
| 415 | return; | ||
| 416 | } | ||
| 417 | |||
| 418 | bool callback_error = false; | ||
| 419 | |||
| 420 | std::error_code ec; | ||
| 421 | |||
| 422 | for (const auto& entry : fs::directory_iterator(path, ec)) { | ||
| 423 | if (ec) { | ||
| 424 | break; | ||
| 425 | } | ||
| 426 | |||
| 427 | if (True(filter & DirEntryFilter::File) && | ||
| 428 | entry.status().type() == fs::file_type::regular) { | ||
| 429 | if (!callback(entry.path())) { | ||
| 430 | callback_error = true; | ||
| 431 | break; | ||
| 432 | } | ||
| 433 | } | ||
| 434 | |||
| 435 | if (True(filter & DirEntryFilter::Directory) && | ||
| 436 | entry.status().type() == fs::file_type::directory) { | ||
| 437 | if (!callback(entry.path())) { | ||
| 438 | callback_error = true; | ||
| 439 | break; | ||
| 440 | } | ||
| 441 | } | ||
| 442 | } | ||
| 443 | |||
| 444 | if (callback_error || ec) { | ||
| 445 | LOG_ERROR(Common_Filesystem, | ||
| 446 | "Failed to visit all the directory entries of path={}, ec_message={}", | ||
| 447 | PathToUTF8String(path), ec.message()); | ||
| 448 | return; | ||
| 449 | } | ||
| 450 | |||
| 451 | LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}", | ||
| 452 | PathToUTF8String(path)); | ||
| 453 | } | ||
| 454 | |||
| 455 | void IterateDirEntriesRecursively(const std::filesystem::path& path, | ||
| 456 | const DirEntryCallable& callback, DirEntryFilter filter) { | ||
| 457 | if (!ValidatePath(path)) { | ||
| 458 | LOG_ERROR(Common_Filesystem, "Input path is not valid, path={}", PathToUTF8String(path)); | ||
| 459 | return; | ||
| 460 | } | ||
| 461 | |||
| 462 | if (!Exists(path)) { | ||
| 463 | LOG_ERROR(Common_Filesystem, "Filesystem object at path={} does not exist", | ||
| 464 | PathToUTF8String(path)); | ||
| 465 | return; | ||
| 466 | } | ||
| 467 | |||
| 468 | if (!IsDir(path)) { | ||
| 469 | LOG_ERROR(Common_Filesystem, "Filesystem object at path={} is not a directory", | ||
| 470 | PathToUTF8String(path)); | ||
| 471 | return; | ||
| 472 | } | ||
| 473 | |||
| 474 | bool callback_error = false; | ||
| 475 | |||
| 476 | std::error_code ec; | ||
| 477 | |||
| 478 | for (const auto& entry : fs::recursive_directory_iterator(path, ec)) { | ||
| 479 | if (ec) { | ||
| 480 | break; | ||
| 481 | } | ||
| 482 | |||
| 483 | if (True(filter & DirEntryFilter::File) && | ||
| 484 | entry.status().type() == fs::file_type::regular) { | ||
| 485 | if (!callback(entry.path())) { | ||
| 486 | callback_error = true; | ||
| 487 | break; | ||
| 488 | } | ||
| 489 | } | ||
| 490 | |||
| 491 | if (True(filter & DirEntryFilter::Directory) && | ||
| 492 | entry.status().type() == fs::file_type::directory) { | ||
| 493 | if (!callback(entry.path())) { | ||
| 494 | callback_error = true; | ||
| 495 | break; | ||
| 496 | } | ||
| 497 | } | ||
| 498 | } | ||
| 499 | |||
| 500 | if (callback_error || ec) { | ||
| 501 | LOG_ERROR(Common_Filesystem, | ||
| 502 | "Failed to visit all the directory entries of path={}, ec_message={}", | ||
| 503 | PathToUTF8String(path), ec.message()); | ||
| 504 | return; | ||
| 505 | } | ||
| 506 | |||
| 507 | LOG_DEBUG(Common_Filesystem, "Successfully visited all the directory entries of path={}", | ||
| 508 | PathToUTF8String(path)); | ||
| 509 | } | ||
| 510 | |||
| 511 | // Generic Filesystem Operations | ||
| 512 | |||
| 513 | bool Exists(const fs::path& path) { | ||
| 514 | return fs::exists(path); | ||
| 515 | } | ||
| 516 | |||
| 517 | bool IsFile(const fs::path& path) { | ||
| 518 | return fs::is_regular_file(path); | ||
| 519 | } | ||
| 520 | |||
| 521 | bool IsDir(const fs::path& path) { | ||
| 522 | return fs::is_directory(path); | ||
| 523 | } | ||
| 524 | |||
| 525 | fs::path GetCurrentDir() { | ||
| 526 | std::error_code ec; | ||
| 527 | |||
| 528 | const auto current_path = fs::current_path(ec); | ||
| 529 | |||
| 530 | if (ec) { | ||
| 531 | LOG_ERROR(Common_Filesystem, "Failed to get the current path, ec_message={}", ec.message()); | ||
| 532 | return {}; | ||
| 533 | } | ||
| 534 | |||
| 535 | return current_path; | ||
| 536 | } | ||
| 537 | |||
| 538 | bool SetCurrentDir(const fs::path& path) { | ||
| 539 | std::error_code ec; | ||
| 540 | |||
| 541 | fs::current_path(path, ec); | ||
| 542 | |||
| 543 | if (ec) { | ||
| 544 | LOG_ERROR(Common_Filesystem, "Failed to set the current path to path={}, ec_message={}", | ||
| 545 | PathToUTF8String(path), ec.message()); | ||
| 546 | return false; | ||
| 547 | } | ||
| 548 | |||
| 549 | return true; | ||
| 550 | } | ||
| 551 | |||
| 552 | fs::file_type GetEntryType(const fs::path& path) { | ||
| 553 | std::error_code ec; | ||
| 554 | |||
| 555 | const auto file_status = fs::status(path, ec); | ||
| 556 | |||
| 557 | if (ec) { | ||
| 558 | LOG_ERROR(Common_Filesystem, "Failed to retrieve the entry type of path={}, ec_message={}", | ||
| 559 | PathToUTF8String(path), ec.message()); | ||
| 560 | return fs::file_type::not_found; | ||
| 561 | } | ||
| 562 | |||
| 563 | return file_status.type(); | ||
| 564 | } | ||
| 565 | |||
| 566 | u64 GetSize(const fs::path& path) { | ||
| 567 | std::error_code ec; | ||
| 568 | |||
| 569 | const auto file_size = fs::file_size(path, ec); | ||
| 570 | |||
| 571 | if (ec) { | ||
| 572 | LOG_ERROR(Common_Filesystem, "Failed to retrieve the file size of path={}, ec_message={}", | ||
| 573 | PathToUTF8String(path), ec.message()); | ||
| 574 | return 0; | ||
| 575 | } | ||
| 576 | |||
| 577 | return file_size; | ||
| 578 | } | ||
| 579 | |||
| 580 | u64 GetFreeSpaceSize(const fs::path& path) { | ||
| 581 | std::error_code ec; | ||
| 582 | |||
| 583 | const auto space_info = fs::space(path, ec); | ||
| 584 | |||
| 585 | if (ec) { | ||
| 586 | LOG_ERROR(Common_Filesystem, | ||
| 587 | "Failed to retrieve the available free space of path={}, ec_message={}", | ||
| 588 | PathToUTF8String(path), ec.message()); | ||
| 589 | return 0; | ||
| 590 | } | ||
| 591 | |||
| 592 | return space_info.free; | ||
| 593 | } | ||
| 594 | |||
| 595 | u64 GetTotalSpaceSize(const fs::path& path) { | ||
| 596 | std::error_code ec; | ||
| 597 | |||
| 598 | const auto space_info = fs::space(path, ec); | ||
| 599 | |||
| 600 | if (ec) { | ||
| 601 | LOG_ERROR(Common_Filesystem, | ||
| 602 | "Failed to retrieve the total capacity of path={}, ec_message={}", | ||
| 603 | PathToUTF8String(path), ec.message()); | ||
| 604 | return 0; | ||
| 605 | } | ||
| 606 | |||
| 607 | return space_info.capacity; | ||
| 608 | } | ||
| 609 | |||
| 610 | } // namespace Common::FS | ||
diff --git a/src/common/fs/fs.h b/src/common/fs/fs.h new file mode 100644 index 000000000..f6f256349 --- /dev/null +++ b/src/common/fs/fs.h | |||
| @@ -0,0 +1,582 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <filesystem> | ||
| 8 | #include <memory> | ||
| 9 | |||
| 10 | #include "common/fs/fs_types.h" | ||
| 11 | #include "common/fs/fs_util.h" | ||
| 12 | |||
| 13 | namespace Common::FS { | ||
| 14 | |||
| 15 | class IOFile; | ||
| 16 | |||
| 17 | // File Operations | ||
| 18 | |||
| 19 | /** | ||
| 20 | * Creates a new file at path with the specified size. | ||
| 21 | * | ||
| 22 | * Failures occur when: | ||
| 23 | * - Input path is not valid | ||
| 24 | * - The input path's parent directory does not exist | ||
| 25 | * - Filesystem object at path exists | ||
| 26 | * - Filesystem at path is read only | ||
| 27 | * | ||
| 28 | * @param path Filesystem path | ||
| 29 | * @param size File size | ||
| 30 | * | ||
| 31 | * @returns True if the file creation succeeds, false otherwise. | ||
| 32 | */ | ||
| 33 | [[nodiscard]] bool NewFile(const std::filesystem::path& path, u64 size = 0); | ||
| 34 | |||
| 35 | #ifdef _WIN32 | ||
| 36 | template <typename Path> | ||
| 37 | [[nodiscard]] bool NewFile(const Path& path, u64 size = 0) { | ||
| 38 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 39 | return NewFile(ToU8String(path), size); | ||
| 40 | } else { | ||
| 41 | return NewFile(std::filesystem::path{path}, size); | ||
| 42 | } | ||
| 43 | } | ||
| 44 | #endif | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Removes a file at path. | ||
| 48 | * | ||
| 49 | * Failures occur when: | ||
| 50 | * - Input path is not valid | ||
| 51 | * - Filesystem object at path is not a file | ||
| 52 | * - Filesystem at path is read only | ||
| 53 | * | ||
| 54 | * @param path Filesystem path | ||
| 55 | * | ||
| 56 | * @returns True if file removal succeeds or file does not exist, false otherwise. | ||
| 57 | */ | ||
| 58 | [[nodiscard]] bool RemoveFile(const std::filesystem::path& path); | ||
| 59 | |||
| 60 | #ifdef _WIN32 | ||
| 61 | template <typename Path> | ||
| 62 | [[nodiscard]] bool RemoveFile(const Path& path) { | ||
| 63 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 64 | return RemoveFile(ToU8String(path)); | ||
| 65 | } else { | ||
| 66 | return RemoveFile(std::filesystem::path{path}); | ||
| 67 | } | ||
| 68 | } | ||
| 69 | #endif | ||
| 70 | |||
| 71 | /** | ||
| 72 | * Renames a file from old_path to new_path. | ||
| 73 | * | ||
| 74 | * Failures occur when: | ||
| 75 | * - One or both input path(s) is not valid | ||
| 76 | * - Filesystem object at old_path does not exist | ||
| 77 | * - Filesystem object at old_path is not a file | ||
| 78 | * - Filesystem object at new_path exists | ||
| 79 | * - Filesystem at either path is read only | ||
| 80 | * | ||
| 81 | * @param old_path Old filesystem path | ||
| 82 | * @param new_path New filesystem path | ||
| 83 | * | ||
| 84 | * @returns True if file rename succeeds, false otherwise. | ||
| 85 | */ | ||
| 86 | [[nodiscard]] bool RenameFile(const std::filesystem::path& old_path, | ||
| 87 | const std::filesystem::path& new_path); | ||
| 88 | |||
| 89 | #ifdef _WIN32 | ||
| 90 | template <typename Path1, typename Path2> | ||
| 91 | [[nodiscard]] bool RenameFile(const Path1& old_path, const Path2& new_path) { | ||
| 92 | using ValueType1 = typename Path1::value_type; | ||
| 93 | using ValueType2 = typename Path2::value_type; | ||
| 94 | if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) { | ||
| 95 | return RenameFile(ToU8String(old_path), ToU8String(new_path)); | ||
| 96 | } else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) { | ||
| 97 | return RenameFile(ToU8String(old_path), new_path); | ||
| 98 | } else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) { | ||
| 99 | return RenameFile(old_path, ToU8String(new_path)); | ||
| 100 | } else { | ||
| 101 | return RenameFile(std::filesystem::path{old_path}, std::filesystem::path{new_path}); | ||
| 102 | } | ||
| 103 | } | ||
| 104 | #endif | ||
| 105 | |||
| 106 | /** | ||
| 107 | * Opens a file at path with the specified file access mode. | ||
| 108 | * This function behaves differently depending on the FileAccessMode. | ||
| 109 | * These behaviors are documented in each enum value of FileAccessMode. | ||
| 110 | * | ||
| 111 | * Failures occur when: | ||
| 112 | * - Input path is not valid | ||
| 113 | * - Filesystem object at path is not a file | ||
| 114 | * - The file is not opened | ||
| 115 | * | ||
| 116 | * @param path Filesystem path | ||
| 117 | * @param mode File access mode | ||
| 118 | * @param type File type, default is BinaryFile. Use TextFile to open the file as a text file | ||
| 119 | * @param flag (Windows only) File-share access flag, default is ShareReadOnly | ||
| 120 | * | ||
| 121 | * @returns A shared pointer to the opened file. Returns nullptr on failure. | ||
| 122 | */ | ||
| 123 | [[nodiscard]] std::shared_ptr<IOFile> FileOpen(const std::filesystem::path& path, | ||
| 124 | FileAccessMode mode, | ||
| 125 | FileType type = FileType::BinaryFile, | ||
| 126 | FileShareFlag flag = FileShareFlag::ShareReadOnly); | ||
| 127 | |||
| 128 | #ifdef _WIN32 | ||
| 129 | template <typename Path> | ||
| 130 | [[nodiscard]] std::shared_ptr<IOFile> FileOpen(const Path& path, FileAccessMode mode, | ||
| 131 | FileType type = FileType::BinaryFile, | ||
| 132 | FileShareFlag flag = FileShareFlag::ShareReadOnly) { | ||
| 133 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 134 | return FileOpen(ToU8String(path), mode, type, flag); | ||
| 135 | } else { | ||
| 136 | return FileOpen(std::filesystem::path{path}, mode, type, flag); | ||
| 137 | } | ||
| 138 | } | ||
| 139 | #endif | ||
| 140 | |||
| 141 | // Directory Operations | ||
| 142 | |||
| 143 | /** | ||
| 144 | * Creates a directory at path. | ||
| 145 | * Note that this function will *always* assume that the input path is a directory. For example, | ||
| 146 | * if the input path is /path/to/directory/file.txt, it will create a directory called "file.txt". | ||
| 147 | * If you intend to create the parent directory of a file, use CreateParentDir instead. | ||
| 148 | * | ||
| 149 | * Failures occur when: | ||
| 150 | * - Input path is not valid | ||
| 151 | * - The input path's parent directory does not exist | ||
| 152 | * - Filesystem at path is read only | ||
| 153 | * | ||
| 154 | * @param path Filesystem path | ||
| 155 | * | ||
| 156 | * @returns True if directory creation succeeds or directory already exists, false otherwise. | ||
| 157 | */ | ||
| 158 | [[nodiscard]] bool CreateDir(const std::filesystem::path& path); | ||
| 159 | |||
| 160 | #ifdef _WIN32 | ||
| 161 | template <typename Path> | ||
| 162 | [[nodiscard]] bool CreateDir(const Path& path) { | ||
| 163 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 164 | return CreateDir(ToU8String(path)); | ||
| 165 | } else { | ||
| 166 | return CreateDir(std::filesystem::path{path}); | ||
| 167 | } | ||
| 168 | } | ||
| 169 | #endif | ||
| 170 | |||
| 171 | /** | ||
| 172 | * Recursively creates a directory at path. | ||
| 173 | * Note that this function will *always* assume that the input path is a directory. For example, | ||
| 174 | * if the input path is /path/to/directory/file.txt, it will create a directory called "file.txt". | ||
| 175 | * If you intend to create the parent directory of a file, use CreateParentDirs instead. | ||
| 176 | * Unlike CreateDir, this creates all of input path's parent directories if they do not exist. | ||
| 177 | * | ||
| 178 | * Failures occur when: | ||
| 179 | * - Input path is not valid | ||
| 180 | * - Filesystem at path is read only | ||
| 181 | * | ||
| 182 | * @param path Filesystem path | ||
| 183 | * | ||
| 184 | * @returns True if directory creation succeeds or directory already exists, false otherwise. | ||
| 185 | */ | ||
| 186 | [[nodiscard]] bool CreateDirs(const std::filesystem::path& path); | ||
| 187 | |||
| 188 | #ifdef _WIN32 | ||
| 189 | template <typename Path> | ||
| 190 | [[nodiscard]] bool CreateDirs(const Path& path) { | ||
| 191 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 192 | return CreateDirs(ToU8String(path)); | ||
| 193 | } else { | ||
| 194 | return CreateDirs(std::filesystem::path{path}); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | #endif | ||
| 198 | |||
| 199 | /** | ||
| 200 | * Creates the parent directory of a given path. | ||
| 201 | * This function calls CreateDir(path.parent_path()), see CreateDir for more details. | ||
| 202 | * | ||
| 203 | * @param path Filesystem path | ||
| 204 | * | ||
| 205 | * @returns True if directory creation succeeds or directory already exists, false otherwise. | ||
| 206 | */ | ||
| 207 | [[nodiscard]] bool CreateParentDir(const std::filesystem::path& path); | ||
| 208 | |||
| 209 | #ifdef _WIN32 | ||
| 210 | template <typename Path> | ||
| 211 | [[nodiscard]] bool CreateParentDir(const Path& path) { | ||
| 212 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 213 | return CreateParentDir(ToU8String(path)); | ||
| 214 | } else { | ||
| 215 | return CreateParentDir(std::filesystem::path{path}); | ||
| 216 | } | ||
| 217 | } | ||
| 218 | #endif | ||
| 219 | |||
| 220 | /** | ||
| 221 | * Recursively creates the parent directory of a given path. | ||
| 222 | * This function calls CreateDirs(path.parent_path()), see CreateDirs for more details. | ||
| 223 | * | ||
| 224 | * @param path Filesystem path | ||
| 225 | * | ||
| 226 | * @returns True if directory creation succeeds or directory already exists, false otherwise. | ||
| 227 | */ | ||
| 228 | [[nodiscard]] bool CreateParentDirs(const std::filesystem::path& path); | ||
| 229 | |||
| 230 | #ifdef _WIN32 | ||
| 231 | template <typename Path> | ||
| 232 | [[nodiscard]] bool CreateParentDirs(const Path& path) { | ||
| 233 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 234 | return CreateParentDirs(ToU8String(path)); | ||
| 235 | } else { | ||
| 236 | return CreateParentDirs(std::filesystem::path{path}); | ||
| 237 | } | ||
| 238 | } | ||
| 239 | #endif | ||
| 240 | |||
| 241 | /** | ||
| 242 | * Removes a directory at path. | ||
| 243 | * | ||
| 244 | * Failures occur when: | ||
| 245 | * - Input path is not valid | ||
| 246 | * - Filesystem object at path is not a directory | ||
| 247 | * - The given directory is not empty | ||
| 248 | * - Filesystem at path is read only | ||
| 249 | * | ||
| 250 | * @param path Filesystem path | ||
| 251 | * | ||
| 252 | * @returns True if directory removal succeeds or directory does not exist, false otherwise. | ||
| 253 | */ | ||
| 254 | [[nodiscard]] bool RemoveDir(const std::filesystem::path& path); | ||
| 255 | |||
| 256 | #ifdef _WIN32 | ||
| 257 | template <typename Path> | ||
| 258 | [[nodiscard]] bool RemoveDir(const Path& path) { | ||
| 259 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 260 | return RemoveDir(ToU8String(path)); | ||
| 261 | } else { | ||
| 262 | return RemoveDir(std::filesystem::path{path}); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | #endif | ||
| 266 | |||
| 267 | /** | ||
| 268 | * Removes all the contents within the given directory and removes the directory itself. | ||
| 269 | * | ||
| 270 | * Failures occur when: | ||
| 271 | * - Input path is not valid | ||
| 272 | * - Filesystem object at path is not a directory | ||
| 273 | * - Filesystem at path is read only | ||
| 274 | * | ||
| 275 | * @param path Filesystem path | ||
| 276 | * | ||
| 277 | * @returns True if the directory and all of its contents are removed successfully, false otherwise. | ||
| 278 | */ | ||
| 279 | [[nodiscard]] bool RemoveDirRecursively(const std::filesystem::path& path); | ||
| 280 | |||
| 281 | #ifdef _WIN32 | ||
| 282 | template <typename Path> | ||
| 283 | [[nodiscard]] bool RemoveDirRecursively(const Path& path) { | ||
| 284 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 285 | return RemoveDirRecursively(ToU8String(path)); | ||
| 286 | } else { | ||
| 287 | return RemoveDirRecursively(std::filesystem::path{path}); | ||
| 288 | } | ||
| 289 | } | ||
| 290 | #endif | ||
| 291 | |||
| 292 | /** | ||
| 293 | * Removes all the contents within the given directory without removing the directory itself. | ||
| 294 | * | ||
| 295 | * Failures occur when: | ||
| 296 | * - Input path is not valid | ||
| 297 | * - Filesystem object at path is not a directory | ||
| 298 | * - Filesystem at path is read only | ||
| 299 | * | ||
| 300 | * @param path Filesystem path | ||
| 301 | * | ||
| 302 | * @returns True if all of the directory's contents are removed successfully, false otherwise. | ||
| 303 | */ | ||
| 304 | [[nodiscard]] bool RemoveDirContentsRecursively(const std::filesystem::path& path); | ||
| 305 | |||
| 306 | #ifdef _WIN32 | ||
| 307 | template <typename Path> | ||
| 308 | [[nodiscard]] bool RemoveDirContentsRecursively(const Path& path) { | ||
| 309 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 310 | return RemoveDirContentsRecursively(ToU8String(path)); | ||
| 311 | } else { | ||
| 312 | return RemoveDirContentsRecursively(std::filesystem::path{path}); | ||
| 313 | } | ||
| 314 | } | ||
| 315 | #endif | ||
| 316 | |||
| 317 | /** | ||
| 318 | * Renames a directory from old_path to new_path. | ||
| 319 | * | ||
| 320 | * Failures occur when: | ||
| 321 | * - One or both input path(s) is not valid | ||
| 322 | * - Filesystem object at old_path does not exist | ||
| 323 | * - Filesystem object at old_path is not a directory | ||
| 324 | * - Filesystem object at new_path exists | ||
| 325 | * - Filesystem at either path is read only | ||
| 326 | * | ||
| 327 | * @param old_path Old filesystem path | ||
| 328 | * @param new_path New filesystem path | ||
| 329 | * | ||
| 330 | * @returns True if directory rename succeeds, false otherwise. | ||
| 331 | */ | ||
| 332 | [[nodiscard]] bool RenameDir(const std::filesystem::path& old_path, | ||
| 333 | const std::filesystem::path& new_path); | ||
| 334 | |||
| 335 | #ifdef _WIN32 | ||
| 336 | template <typename Path1, typename Path2> | ||
| 337 | [[nodiscard]] bool RenameDir(const Path1& old_path, const Path2& new_path) { | ||
| 338 | using ValueType1 = typename Path1::value_type; | ||
| 339 | using ValueType2 = typename Path2::value_type; | ||
| 340 | if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) { | ||
| 341 | return RenameDir(ToU8String(old_path), ToU8String(new_path)); | ||
| 342 | } else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) { | ||
| 343 | return RenameDir(ToU8String(old_path), new_path); | ||
| 344 | } else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) { | ||
| 345 | return RenameDir(old_path, ToU8String(new_path)); | ||
| 346 | } else { | ||
| 347 | return RenameDir(std::filesystem::path{old_path}, std::filesystem::path{new_path}); | ||
| 348 | } | ||
| 349 | } | ||
| 350 | #endif | ||
| 351 | |||
| 352 | /** | ||
| 353 | * Iterates over the directory entries of a given directory. | ||
| 354 | * This does not iterate over the sub-directories of the given directory. | ||
| 355 | * The DirEntryCallable callback is called for each visited directory entry. | ||
| 356 | * A filter can be set to control which directory entries are visited based on their type. | ||
| 357 | * By default, both files and directories are visited. | ||
| 358 | * If the callback returns false or there is an error, the iteration is immediately halted. | ||
| 359 | * | ||
| 360 | * Failures occur when: | ||
| 361 | * - Input path is not valid | ||
| 362 | * - Filesystem object at path is not a directory | ||
| 363 | * | ||
| 364 | * @param path Filesystem path | ||
| 365 | * @param callback Callback to be called for each visited directory entry | ||
| 366 | * @param filter Directory entry type filter | ||
| 367 | */ | ||
| 368 | void IterateDirEntries(const std::filesystem::path& path, const DirEntryCallable& callback, | ||
| 369 | DirEntryFilter filter = DirEntryFilter::All); | ||
| 370 | |||
| 371 | #ifdef _WIN32 | ||
| 372 | template <typename Path> | ||
| 373 | void IterateDirEntries(const Path& path, const DirEntryCallable& callback, | ||
| 374 | DirEntryFilter filter = DirEntryFilter::All) { | ||
| 375 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 376 | IterateDirEntries(ToU8String(path), callback, filter); | ||
| 377 | } else { | ||
| 378 | IterateDirEntries(std::filesystem::path{path}, callback, filter); | ||
| 379 | } | ||
| 380 | } | ||
| 381 | #endif | ||
| 382 | |||
| 383 | /** | ||
| 384 | * Iterates over the directory entries of a given directory and its sub-directories. | ||
| 385 | * The DirEntryCallable callback is called for each visited directory entry. | ||
| 386 | * A filter can be set to control which directory entries are visited based on their type. | ||
| 387 | * By default, both files and directories are visited. | ||
| 388 | * If the callback returns false or there is an error, the iteration is immediately halted. | ||
| 389 | * | ||
| 390 | * Failures occur when: | ||
| 391 | * - Input path is not valid | ||
| 392 | * - Filesystem object at path does not exist | ||
| 393 | * - Filesystem object at path is not a directory | ||
| 394 | * | ||
| 395 | * @param path Filesystem path | ||
| 396 | * @param callback Callback to be called for each visited directory entry | ||
| 397 | * @param filter Directory entry type filter | ||
| 398 | */ | ||
| 399 | void IterateDirEntriesRecursively(const std::filesystem::path& path, | ||
| 400 | const DirEntryCallable& callback, | ||
| 401 | DirEntryFilter filter = DirEntryFilter::All); | ||
| 402 | |||
| 403 | #ifdef _WIN32 | ||
| 404 | template <typename Path> | ||
| 405 | void IterateDirEntriesRecursively(const Path& path, const DirEntryCallable& callback, | ||
| 406 | DirEntryFilter filter = DirEntryFilter::All) { | ||
| 407 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 408 | IterateDirEntriesRecursively(ToU8String(path), callback, filter); | ||
| 409 | } else { | ||
| 410 | IterateDirEntriesRecursively(std::filesystem::path{path}, callback, filter); | ||
| 411 | } | ||
| 412 | } | ||
| 413 | #endif | ||
| 414 | |||
| 415 | // Generic Filesystem Operations | ||
| 416 | |||
| 417 | /** | ||
| 418 | * Returns whether a filesystem object at path exists. | ||
| 419 | * | ||
| 420 | * @param path Filesystem path | ||
| 421 | * | ||
| 422 | * @returns True if a filesystem object at path exists, false otherwise. | ||
| 423 | */ | ||
| 424 | [[nodiscard]] bool Exists(const std::filesystem::path& path); | ||
| 425 | |||
| 426 | #ifdef _WIN32 | ||
| 427 | template <typename Path> | ||
| 428 | [[nodiscard]] bool Exists(const Path& path) { | ||
| 429 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 430 | return Exists(ToU8String(path)); | ||
| 431 | } else { | ||
| 432 | return Exists(std::filesystem::path{path}); | ||
| 433 | } | ||
| 434 | } | ||
| 435 | #endif | ||
| 436 | |||
| 437 | /** | ||
| 438 | * Returns whether a filesystem object at path is a file. | ||
| 439 | * | ||
| 440 | * @param path Filesystem path | ||
| 441 | * | ||
| 442 | * @returns True if a filesystem object at path is a file, false otherwise. | ||
| 443 | */ | ||
| 444 | [[nodiscard]] bool IsFile(const std::filesystem::path& path); | ||
| 445 | |||
| 446 | #ifdef _WIN32 | ||
| 447 | template <typename Path> | ||
| 448 | [[nodiscard]] bool IsFile(const Path& path) { | ||
| 449 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 450 | return IsFile(ToU8String(path)); | ||
| 451 | } else { | ||
| 452 | return IsFile(std::filesystem::path{path}); | ||
| 453 | } | ||
| 454 | } | ||
| 455 | #endif | ||
| 456 | |||
| 457 | /** | ||
| 458 | * Returns whether a filesystem object at path is a directory. | ||
| 459 | * | ||
| 460 | * @param path Filesystem path | ||
| 461 | * | ||
| 462 | * @returns True if a filesystem object at path is a directory, false otherwise. | ||
| 463 | */ | ||
| 464 | [[nodiscard]] bool IsDir(const std::filesystem::path& path); | ||
| 465 | |||
| 466 | #ifdef _WIN32 | ||
| 467 | template <typename Path> | ||
| 468 | [[nodiscard]] bool IsDir(const Path& path) { | ||
| 469 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 470 | return IsDir(ToU8String(path)); | ||
| 471 | } else { | ||
| 472 | return IsDir(std::filesystem::path{path}); | ||
| 473 | } | ||
| 474 | } | ||
| 475 | #endif | ||
| 476 | |||
| 477 | /** | ||
| 478 | * Gets the current working directory. | ||
| 479 | * | ||
| 480 | * @returns The current working directory. Returns an empty path on failure. | ||
| 481 | */ | ||
| 482 | [[nodiscard]] std::filesystem::path GetCurrentDir(); | ||
| 483 | |||
| 484 | /** | ||
| 485 | * Sets the current working directory to path. | ||
| 486 | * | ||
| 487 | * @returns True if the current working directory is successfully set, false otherwise. | ||
| 488 | */ | ||
| 489 | [[nodiscard]] bool SetCurrentDir(const std::filesystem::path& path); | ||
| 490 | |||
| 491 | #ifdef _WIN32 | ||
| 492 | template <typename Path> | ||
| 493 | [[nodiscard]] bool SetCurrentDir(const Path& path) { | ||
| 494 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 495 | return SetCurrentDir(ToU8String(path)); | ||
| 496 | } else { | ||
| 497 | return SetCurrentDir(std::filesystem::path{path}); | ||
| 498 | } | ||
| 499 | } | ||
| 500 | #endif | ||
| 501 | |||
| 502 | /** | ||
| 503 | * Gets the entry type of the filesystem object at path. | ||
| 504 | * | ||
| 505 | * @param path Filesystem path | ||
| 506 | * | ||
| 507 | * @returns The entry type of the filesystem object. Returns file_type::not_found on failure. | ||
| 508 | */ | ||
| 509 | [[nodiscard]] std::filesystem::file_type GetEntryType(const std::filesystem::path& path); | ||
| 510 | |||
| 511 | #ifdef _WIN32 | ||
| 512 | template <typename Path> | ||
| 513 | [[nodiscard]] std::filesystem::file_type GetEntryType(const Path& path) { | ||
| 514 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 515 | return GetEntryType(ToU8String(path)); | ||
| 516 | } else { | ||
| 517 | return GetEntryType(std::filesystem::path{path}); | ||
| 518 | } | ||
| 519 | } | ||
| 520 | #endif | ||
| 521 | |||
| 522 | /** | ||
| 523 | * Gets the size of the filesystem object at path. | ||
| 524 | * | ||
| 525 | * @param path Filesystem path | ||
| 526 | * | ||
| 527 | * @returns The size in bytes of the filesystem object. Returns 0 on failure. | ||
| 528 | */ | ||
| 529 | [[nodiscard]] u64 GetSize(const std::filesystem::path& path); | ||
| 530 | |||
| 531 | #ifdef _WIN32 | ||
| 532 | template <typename Path> | ||
| 533 | [[nodiscard]] u64 GetSize(const Path& path) { | ||
| 534 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 535 | return GetSize(ToU8String(path)); | ||
| 536 | } else { | ||
| 537 | return GetSize(std::filesystem::path{path}); | ||
| 538 | } | ||
| 539 | } | ||
| 540 | #endif | ||
| 541 | |||
| 542 | /** | ||
| 543 | * Gets the free space size of the filesystem at path. | ||
| 544 | * | ||
| 545 | * @param path Filesystem path | ||
| 546 | * | ||
| 547 | * @returns The free space size in bytes of the filesystem at path. Returns 0 on failure. | ||
| 548 | */ | ||
| 549 | [[nodiscard]] u64 GetFreeSpaceSize(const std::filesystem::path& path); | ||
| 550 | |||
| 551 | #ifdef _WIN32 | ||
| 552 | template <typename Path> | ||
| 553 | [[nodiscard]] u64 GetFreeSpaceSize(const Path& path) { | ||
| 554 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 555 | return GetFreeSpaceSize(ToU8String(path)); | ||
| 556 | } else { | ||
| 557 | return GetFreeSpaceSize(std::filesystem::path{path}); | ||
| 558 | } | ||
| 559 | } | ||
| 560 | #endif | ||
| 561 | |||
| 562 | /** | ||
| 563 | * Gets the total capacity of the filesystem at path. | ||
| 564 | * | ||
| 565 | * @param path Filesystem path | ||
| 566 | * | ||
| 567 | * @returns The total capacity in bytes of the filesystem at path. Returns 0 on failure. | ||
| 568 | */ | ||
| 569 | [[nodiscard]] u64 GetTotalSpaceSize(const std::filesystem::path& path); | ||
| 570 | |||
| 571 | #ifdef _WIN32 | ||
| 572 | template <typename Path> | ||
| 573 | [[nodiscard]] u64 GetTotalSpaceSize(const Path& path) { | ||
| 574 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 575 | return GetTotalSpaceSize(ToU8String(path)); | ||
| 576 | } else { | ||
| 577 | return GetTotalSpaceSize(std::filesystem::path{path}); | ||
| 578 | } | ||
| 579 | } | ||
| 580 | #endif | ||
| 581 | |||
| 582 | } // namespace Common::FS | ||
diff --git a/src/common/fs/fs_paths.h b/src/common/fs/fs_paths.h new file mode 100644 index 000000000..b32614797 --- /dev/null +++ b/src/common/fs/fs_paths.h | |||
| @@ -0,0 +1,27 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | // yuzu data directories | ||
| 8 | |||
| 9 | #define YUZU_DIR "yuzu" | ||
| 10 | #define PORTABLE_DIR "user" | ||
| 11 | |||
| 12 | // Sub-directories contained within a yuzu data directory | ||
| 13 | |||
| 14 | #define CACHE_DIR "cache" | ||
| 15 | #define CONFIG_DIR "config" | ||
| 16 | #define DUMP_DIR "dump" | ||
| 17 | #define KEYS_DIR "keys" | ||
| 18 | #define LOAD_DIR "load" | ||
| 19 | #define LOG_DIR "log" | ||
| 20 | #define NAND_DIR "nand" | ||
| 21 | #define SCREENSHOTS_DIR "screenshots" | ||
| 22 | #define SDMC_DIR "sdmc" | ||
| 23 | #define SHADER_DIR "shader" | ||
| 24 | |||
| 25 | // yuzu-specific files | ||
| 26 | |||
| 27 | #define LOG_FILE "yuzu_log.txt" | ||
diff --git a/src/common/fs/fs_types.h b/src/common/fs/fs_types.h new file mode 100644 index 000000000..089980aee --- /dev/null +++ b/src/common/fs/fs_types.h | |||
| @@ -0,0 +1,73 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <functional> | ||
| 8 | |||
| 9 | #include "common/common_funcs.h" | ||
| 10 | #include "common/common_types.h" | ||
| 11 | |||
| 12 | namespace Common::FS { | ||
| 13 | |||
| 14 | enum class FileAccessMode { | ||
| 15 | /** | ||
| 16 | * If the file at path exists, it opens the file for reading. | ||
| 17 | * If the file at path does not exist, it fails to open the file. | ||
| 18 | */ | ||
| 19 | Read = 1 << 0, | ||
| 20 | /** | ||
| 21 | * If the file at path exists, the existing contents of the file are erased. | ||
| 22 | * The empty file is then opened for writing. | ||
| 23 | * If the file at path does not exist, it creates and opens a new empty file for writing. | ||
| 24 | */ | ||
| 25 | Write = 1 << 1, | ||
| 26 | /** | ||
| 27 | * If the file at path exists, it opens the file for reading and writing. | ||
| 28 | * If the file at path does not exist, it fails to open the file. | ||
| 29 | */ | ||
| 30 | ReadWrite = Read | Write, | ||
| 31 | /** | ||
| 32 | * If the file at path exists, it opens the file for appending. | ||
| 33 | * If the file at path does not exist, it creates and opens a new empty file for appending. | ||
| 34 | */ | ||
| 35 | Append = 1 << 2, | ||
| 36 | /** | ||
| 37 | * If the file at path exists, it opens the file for both reading and appending. | ||
| 38 | * If the file at path does not exist, it creates and opens a new empty file for both | ||
| 39 | * reading and appending. | ||
| 40 | */ | ||
| 41 | ReadAppend = Read | Append, | ||
| 42 | }; | ||
| 43 | |||
| 44 | enum class FileType { | ||
| 45 | BinaryFile, | ||
| 46 | TextFile, | ||
| 47 | }; | ||
| 48 | |||
| 49 | enum class FileShareFlag { | ||
| 50 | ShareNone, // Provides exclusive access to the file. | ||
| 51 | ShareReadOnly, // Provides read only shared access to the file. | ||
| 52 | ShareWriteOnly, // Provides write only shared access to the file. | ||
| 53 | ShareReadWrite, // Provides read and write shared access to the file. | ||
| 54 | }; | ||
| 55 | |||
| 56 | enum class DirEntryFilter { | ||
| 57 | File = 1 << 0, | ||
| 58 | Directory = 1 << 1, | ||
| 59 | All = File | Directory, | ||
| 60 | }; | ||
| 61 | DECLARE_ENUM_FLAG_OPERATORS(DirEntryFilter); | ||
| 62 | |||
| 63 | /** | ||
| 64 | * A callback function which takes in the path of a directory entry. | ||
| 65 | * | ||
| 66 | * @param path The path of a directory entry | ||
| 67 | * | ||
| 68 | * @returns A boolean value. | ||
| 69 | * Return true to indicate whether the callback is successful, false otherwise. | ||
| 70 | */ | ||
| 71 | using DirEntryCallable = std::function<bool(const std::filesystem::path& path)>; | ||
| 72 | |||
| 73 | } // namespace Common::FS | ||
diff --git a/src/common/fs/fs_util.cpp b/src/common/fs/fs_util.cpp new file mode 100644 index 000000000..0ddfc3131 --- /dev/null +++ b/src/common/fs/fs_util.cpp | |||
| @@ -0,0 +1,13 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/fs/fs_util.h" | ||
| 6 | |||
| 7 | namespace Common::FS { | ||
| 8 | |||
| 9 | std::u8string ToU8String(std::string_view utf8_string) { | ||
| 10 | return std::u8string{utf8_string.begin(), utf8_string.end()}; | ||
| 11 | } | ||
| 12 | |||
| 13 | } // namespace Common::FS | ||
diff --git a/src/common/fs/fs_util.h b/src/common/fs/fs_util.h new file mode 100644 index 000000000..951df53b6 --- /dev/null +++ b/src/common/fs/fs_util.h | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <concepts> | ||
| 8 | #include <string> | ||
| 9 | #include <string_view> | ||
| 10 | |||
| 11 | namespace Common::FS { | ||
| 12 | |||
| 13 | template <typename T> | ||
| 14 | concept IsChar = std::same_as<T, char>; | ||
| 15 | |||
| 16 | /** | ||
| 17 | * Converts a UTF-8 encoded std::string or std::string_view to a std::u8string. | ||
| 18 | * | ||
| 19 | * @param utf8_string UTF-8 encoded string | ||
| 20 | * | ||
| 21 | * @returns UTF-8 encoded std::u8string. | ||
| 22 | */ | ||
| 23 | [[nodiscard]] std::u8string ToU8String(std::string_view utf8_string); | ||
| 24 | |||
| 25 | } // namespace Common::FS | ||
diff --git a/src/common/fs/path_util.cpp b/src/common/fs/path_util.cpp new file mode 100644 index 000000000..8b732a21c --- /dev/null +++ b/src/common/fs/path_util.cpp | |||
| @@ -0,0 +1,432 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <unordered_map> | ||
| 7 | |||
| 8 | #include "common/fs/fs.h" | ||
| 9 | #include "common/fs/fs_paths.h" | ||
| 10 | #include "common/fs/path_util.h" | ||
| 11 | #include "common/logging/log.h" | ||
| 12 | |||
| 13 | #ifdef _WIN32 | ||
| 14 | #include <shlobj.h> // Used in GetExeDirectory() | ||
| 15 | #else | ||
| 16 | #include <cstdlib> // Used in Get(Home/Data)Directory() | ||
| 17 | #include <pwd.h> // Used in GetHomeDirectory() | ||
| 18 | #include <sys/types.h> // Used in GetHomeDirectory() | ||
| 19 | #include <unistd.h> // Used in GetDataDirectory() | ||
| 20 | #endif | ||
| 21 | |||
| 22 | #ifdef __APPLE__ | ||
| 23 | #include <sys/param.h> // Used in GetBundleDirectory() | ||
| 24 | |||
| 25 | // CFURL contains __attribute__ directives that gcc does not know how to parse, so we need to just | ||
| 26 | // ignore them if we're not using clang. The macro is only used to prevent linking against | ||
| 27 | // functions that don't exist on older versions of macOS, and the worst case scenario is a linker | ||
| 28 | // error, so this is perfectly safe, just inconvenient. | ||
| 29 | #ifndef __clang__ | ||
| 30 | #define availability(...) | ||
| 31 | #endif | ||
| 32 | #include <CoreFoundation/CFBundle.h> // Used in GetBundleDirectory() | ||
| 33 | #include <CoreFoundation/CFString.h> // Used in GetBundleDirectory() | ||
| 34 | #include <CoreFoundation/CFURL.h> // Used in GetBundleDirectory() | ||
| 35 | #ifdef availability | ||
| 36 | #undef availability | ||
| 37 | #endif | ||
| 38 | #endif | ||
| 39 | |||
| 40 | #ifndef MAX_PATH | ||
| 41 | #ifdef _WIN32 | ||
| 42 | // This is the maximum number of UTF-16 code units permissible in Windows file paths | ||
| 43 | #define MAX_PATH 260 | ||
| 44 | #else | ||
| 45 | // This is the maximum number of UTF-8 code units permissible in all other OSes' file paths | ||
| 46 | #define MAX_PATH 1024 | ||
| 47 | #endif | ||
| 48 | #endif | ||
| 49 | |||
| 50 | namespace Common::FS { | ||
| 51 | |||
| 52 | namespace fs = std::filesystem; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * The PathManagerImpl is a singleton allowing to manage the mapping of | ||
| 56 | * YuzuPath enums to real filesystem paths. | ||
| 57 | * This class provides 2 functions: GetYuzuPathImpl and SetYuzuPathImpl. | ||
| 58 | * These are used by GetYuzuPath and SetYuzuPath respectively to get or modify | ||
| 59 | * the path mapped by the YuzuPath enum. | ||
| 60 | */ | ||
| 61 | class PathManagerImpl { | ||
| 62 | public: | ||
| 63 | static PathManagerImpl& GetInstance() { | ||
| 64 | static PathManagerImpl path_manager_impl; | ||
| 65 | |||
| 66 | return path_manager_impl; | ||
| 67 | } | ||
| 68 | |||
| 69 | PathManagerImpl(const PathManagerImpl&) = delete; | ||
| 70 | PathManagerImpl& operator=(const PathManagerImpl&) = delete; | ||
| 71 | |||
| 72 | PathManagerImpl(PathManagerImpl&&) = delete; | ||
| 73 | PathManagerImpl& operator=(PathManagerImpl&&) = delete; | ||
| 74 | |||
| 75 | [[nodiscard]] const fs::path& GetYuzuPathImpl(YuzuPath yuzu_path) { | ||
| 76 | return yuzu_paths.at(yuzu_path); | ||
| 77 | } | ||
| 78 | |||
| 79 | void SetYuzuPathImpl(YuzuPath yuzu_path, const fs::path& new_path) { | ||
| 80 | yuzu_paths.insert_or_assign(yuzu_path, new_path); | ||
| 81 | } | ||
| 82 | |||
| 83 | private: | ||
| 84 | PathManagerImpl() { | ||
| 85 | #ifdef _WIN32 | ||
| 86 | auto yuzu_path = GetExeDirectory() / PORTABLE_DIR; | ||
| 87 | |||
| 88 | if (!IsDir(yuzu_path)) { | ||
| 89 | yuzu_path = GetAppDataRoamingDirectory() / YUZU_DIR; | ||
| 90 | } | ||
| 91 | |||
| 92 | GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path); | ||
| 93 | GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path / CACHE_DIR); | ||
| 94 | GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path / CONFIG_DIR); | ||
| 95 | #else | ||
| 96 | auto yuzu_path = GetCurrentDir() / PORTABLE_DIR; | ||
| 97 | |||
| 98 | if (Exists(yuzu_path) && IsDir(yuzu_path)) { | ||
| 99 | GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path); | ||
| 100 | GenerateYuzuPath(YuzuPath::CacheDir, yuzu_path / CACHE_DIR); | ||
| 101 | GenerateYuzuPath(YuzuPath::ConfigDir, yuzu_path / CONFIG_DIR); | ||
| 102 | } else { | ||
| 103 | yuzu_path = GetDataDirectory("XDG_DATA_HOME") / YUZU_DIR; | ||
| 104 | |||
| 105 | GenerateYuzuPath(YuzuPath::YuzuDir, yuzu_path); | ||
| 106 | GenerateYuzuPath(YuzuPath::CacheDir, GetDataDirectory("XDG_CACHE_HOME") / YUZU_DIR); | ||
| 107 | GenerateYuzuPath(YuzuPath::ConfigDir, GetDataDirectory("XDG_CONFIG_HOME") / YUZU_DIR); | ||
| 108 | } | ||
| 109 | #endif | ||
| 110 | |||
| 111 | GenerateYuzuPath(YuzuPath::DumpDir, yuzu_path / DUMP_DIR); | ||
| 112 | GenerateYuzuPath(YuzuPath::KeysDir, yuzu_path / KEYS_DIR); | ||
| 113 | GenerateYuzuPath(YuzuPath::LoadDir, yuzu_path / LOAD_DIR); | ||
| 114 | GenerateYuzuPath(YuzuPath::LogDir, yuzu_path / LOG_DIR); | ||
| 115 | GenerateYuzuPath(YuzuPath::NANDDir, yuzu_path / NAND_DIR); | ||
| 116 | GenerateYuzuPath(YuzuPath::ScreenshotsDir, yuzu_path / SCREENSHOTS_DIR); | ||
| 117 | GenerateYuzuPath(YuzuPath::SDMCDir, yuzu_path / SDMC_DIR); | ||
| 118 | GenerateYuzuPath(YuzuPath::ShaderDir, yuzu_path / SHADER_DIR); | ||
| 119 | } | ||
| 120 | |||
| 121 | ~PathManagerImpl() = default; | ||
| 122 | |||
| 123 | void GenerateYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) { | ||
| 124 | void(FS::CreateDir(new_path)); | ||
| 125 | |||
| 126 | SetYuzuPathImpl(yuzu_path, new_path); | ||
| 127 | } | ||
| 128 | |||
| 129 | std::unordered_map<YuzuPath, fs::path> yuzu_paths; | ||
| 130 | }; | ||
| 131 | |||
| 132 | std::string PathToUTF8String(const fs::path& path) { | ||
| 133 | const auto utf8_string = path.u8string(); | ||
| 134 | |||
| 135 | return std::string{utf8_string.begin(), utf8_string.end()}; | ||
| 136 | } | ||
| 137 | |||
| 138 | bool ValidatePath(const fs::path& path) { | ||
| 139 | if (path.empty()) { | ||
| 140 | LOG_ERROR(Common_Filesystem, "Input path is empty, path={}", PathToUTF8String(path)); | ||
| 141 | return false; | ||
| 142 | } | ||
| 143 | |||
| 144 | #ifdef _WIN32 | ||
| 145 | if (path.u16string().size() >= MAX_PATH) { | ||
| 146 | LOG_ERROR(Common_Filesystem, "Input path is too long, path={}", PathToUTF8String(path)); | ||
| 147 | return false; | ||
| 148 | } | ||
| 149 | #else | ||
| 150 | if (path.u8string().size() >= MAX_PATH) { | ||
| 151 | LOG_ERROR(Common_Filesystem, "Input path is too long, path={}", PathToUTF8String(path)); | ||
| 152 | return false; | ||
| 153 | } | ||
| 154 | #endif | ||
| 155 | |||
| 156 | return true; | ||
| 157 | } | ||
| 158 | |||
| 159 | fs::path ConcatPath(const fs::path& first, const fs::path& second) { | ||
| 160 | const bool second_has_dir_sep = IsDirSeparator(second.u8string().front()); | ||
| 161 | |||
| 162 | if (!second_has_dir_sep) { | ||
| 163 | return (first / second).lexically_normal(); | ||
| 164 | } | ||
| 165 | |||
| 166 | fs::path concat_path = first; | ||
| 167 | concat_path += second; | ||
| 168 | |||
| 169 | return concat_path.lexically_normal(); | ||
| 170 | } | ||
| 171 | |||
| 172 | fs::path ConcatPathSafe(const fs::path& base, const fs::path& offset) { | ||
| 173 | const auto concatenated_path = ConcatPath(base, offset); | ||
| 174 | |||
| 175 | if (!IsPathSandboxed(base, concatenated_path)) { | ||
| 176 | return base; | ||
| 177 | } | ||
| 178 | |||
| 179 | return concatenated_path; | ||
| 180 | } | ||
| 181 | |||
| 182 | bool IsPathSandboxed(const fs::path& base, const fs::path& path) { | ||
| 183 | const auto base_string = RemoveTrailingSeparators(base.lexically_normal()).u8string(); | ||
| 184 | const auto path_string = RemoveTrailingSeparators(path.lexically_normal()).u8string(); | ||
| 185 | |||
| 186 | if (path_string.size() < base_string.size()) { | ||
| 187 | return false; | ||
| 188 | } | ||
| 189 | |||
| 190 | return base_string.compare(0, base_string.size(), path_string, 0, base_string.size()) == 0; | ||
| 191 | } | ||
| 192 | |||
| 193 | bool IsDirSeparator(char character) { | ||
| 194 | return character == '/' || character == '\\'; | ||
| 195 | } | ||
| 196 | |||
| 197 | bool IsDirSeparator(char8_t character) { | ||
| 198 | return character == u8'/' || character == u8'\\'; | ||
| 199 | } | ||
| 200 | |||
| 201 | fs::path RemoveTrailingSeparators(const fs::path& path) { | ||
| 202 | if (path.empty()) { | ||
| 203 | return path; | ||
| 204 | } | ||
| 205 | |||
| 206 | auto string_path = path.u8string(); | ||
| 207 | |||
| 208 | while (IsDirSeparator(string_path.back())) { | ||
| 209 | string_path.pop_back(); | ||
| 210 | } | ||
| 211 | |||
| 212 | return fs::path{string_path}; | ||
| 213 | } | ||
| 214 | |||
| 215 | const fs::path& GetYuzuPath(YuzuPath yuzu_path) { | ||
| 216 | return PathManagerImpl::GetInstance().GetYuzuPathImpl(yuzu_path); | ||
| 217 | } | ||
| 218 | |||
| 219 | std::string GetYuzuPathString(YuzuPath yuzu_path) { | ||
| 220 | return PathToUTF8String(GetYuzuPath(yuzu_path)); | ||
| 221 | } | ||
| 222 | |||
| 223 | void SetYuzuPath(YuzuPath yuzu_path, const fs::path& new_path) { | ||
| 224 | if (!FS::IsDir(new_path)) { | ||
| 225 | LOG_ERROR(Common_Filesystem, "Filesystem object at new_path={} is not a directory", | ||
| 226 | PathToUTF8String(new_path)); | ||
| 227 | return; | ||
| 228 | } | ||
| 229 | |||
| 230 | PathManagerImpl::GetInstance().SetYuzuPathImpl(yuzu_path, new_path); | ||
| 231 | } | ||
| 232 | |||
| 233 | #ifdef _WIN32 | ||
| 234 | |||
| 235 | fs::path GetExeDirectory() { | ||
| 236 | wchar_t exe_path[MAX_PATH]; | ||
| 237 | |||
| 238 | GetModuleFileNameW(nullptr, exe_path, MAX_PATH); | ||
| 239 | |||
| 240 | if (!exe_path) { | ||
| 241 | LOG_ERROR(Common_Filesystem, | ||
| 242 | "Failed to get the path to the executable of the current process"); | ||
| 243 | } | ||
| 244 | |||
| 245 | return fs::path{exe_path}.parent_path(); | ||
| 246 | } | ||
| 247 | |||
| 248 | fs::path GetAppDataRoamingDirectory() { | ||
| 249 | PWSTR appdata_roaming_path = nullptr; | ||
| 250 | |||
| 251 | SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, nullptr, &appdata_roaming_path); | ||
| 252 | |||
| 253 | auto fs_appdata_roaming_path = fs::path{appdata_roaming_path}; | ||
| 254 | |||
| 255 | CoTaskMemFree(appdata_roaming_path); | ||
| 256 | |||
| 257 | if (fs_appdata_roaming_path.empty()) { | ||
| 258 | LOG_ERROR(Common_Filesystem, "Failed to get the path to the %APPDATA% directory"); | ||
| 259 | } | ||
| 260 | |||
| 261 | return fs_appdata_roaming_path; | ||
| 262 | } | ||
| 263 | |||
| 264 | #else | ||
| 265 | |||
| 266 | fs::path GetHomeDirectory() { | ||
| 267 | const char* home_env_var = getenv("HOME"); | ||
| 268 | |||
| 269 | if (home_env_var) { | ||
| 270 | return fs::path{home_env_var}; | ||
| 271 | } | ||
| 272 | |||
| 273 | LOG_INFO(Common_Filesystem, | ||
| 274 | "$HOME is not defined in the environment variables, " | ||
| 275 | "attempting to query passwd to get the home path of the current user"); | ||
| 276 | |||
| 277 | const auto* pw = getpwuid(getuid()); | ||
| 278 | |||
| 279 | if (!pw) { | ||
| 280 | LOG_ERROR(Common_Filesystem, "Failed to get the home path of the current user"); | ||
| 281 | return {}; | ||
| 282 | } | ||
| 283 | |||
| 284 | return fs::path{pw->pw_dir}; | ||
| 285 | } | ||
| 286 | |||
| 287 | fs::path GetDataDirectory(const std::string& env_name) { | ||
| 288 | const char* data_env_var = getenv(env_name.c_str()); | ||
| 289 | |||
| 290 | if (data_env_var) { | ||
| 291 | return fs::path{data_env_var}; | ||
| 292 | } | ||
| 293 | |||
| 294 | if (env_name == "XDG_DATA_HOME") { | ||
| 295 | return GetHomeDirectory() / ".local/share"; | ||
| 296 | } else if (env_name == "XDG_CACHE_HOME") { | ||
| 297 | return GetHomeDirectory() / ".cache"; | ||
| 298 | } else if (env_name == "XDG_CONFIG_HOME") { | ||
| 299 | return GetHomeDirectory() / ".config"; | ||
| 300 | } | ||
| 301 | |||
| 302 | return {}; | ||
| 303 | } | ||
| 304 | |||
| 305 | #endif | ||
| 306 | |||
| 307 | #ifdef __APPLE__ | ||
| 308 | |||
| 309 | fs::path GetBundleDirectory() { | ||
| 310 | char app_bundle_path[MAXPATHLEN]; | ||
| 311 | |||
| 312 | // Get the main bundle for the app | ||
| 313 | CFURLRef bundle_ref = CFBundleCopyBundleURL(CFBundleGetMainBundle()); | ||
| 314 | CFStringRef bundle_path = CFURLCopyFileSystemPath(bundle_ref, kCFURLPOSIXPathStyle); | ||
| 315 | |||
| 316 | CFStringGetFileSystemRepresentation(bundle_path, app_bundle_path, sizeof(app_bundle_path)); | ||
| 317 | |||
| 318 | CFRelease(bundle_ref); | ||
| 319 | CFRelease(bundle_path); | ||
| 320 | |||
| 321 | return fs::path{app_bundle_path}; | ||
| 322 | } | ||
| 323 | |||
| 324 | #endif | ||
| 325 | |||
| 326 | // vvvvvvvvvv Deprecated vvvvvvvvvv // | ||
| 327 | |||
| 328 | std::string_view RemoveTrailingSlash(std::string_view path) { | ||
| 329 | if (path.empty()) { | ||
| 330 | return path; | ||
| 331 | } | ||
| 332 | |||
| 333 | if (path.back() == '\\' || path.back() == '/') { | ||
| 334 | path.remove_suffix(1); | ||
| 335 | return path; | ||
| 336 | } | ||
| 337 | |||
| 338 | return path; | ||
| 339 | } | ||
| 340 | |||
| 341 | std::vector<std::string> SplitPathComponents(std::string_view filename) { | ||
| 342 | std::string copy(filename); | ||
| 343 | std::replace(copy.begin(), copy.end(), '\\', '/'); | ||
| 344 | std::vector<std::string> out; | ||
| 345 | |||
| 346 | std::stringstream stream(copy); | ||
| 347 | std::string item; | ||
| 348 | while (std::getline(stream, item, '/')) { | ||
| 349 | out.push_back(std::move(item)); | ||
| 350 | } | ||
| 351 | |||
| 352 | return out; | ||
| 353 | } | ||
| 354 | |||
| 355 | std::string SanitizePath(std::string_view path_, DirectorySeparator directory_separator) { | ||
| 356 | std::string path(path_); | ||
| 357 | char type1 = directory_separator == DirectorySeparator::BackwardSlash ? '/' : '\\'; | ||
| 358 | char type2 = directory_separator == DirectorySeparator::BackwardSlash ? '\\' : '/'; | ||
| 359 | |||
| 360 | if (directory_separator == DirectorySeparator::PlatformDefault) { | ||
| 361 | #ifdef _WIN32 | ||
| 362 | type1 = '/'; | ||
| 363 | type2 = '\\'; | ||
| 364 | #endif | ||
| 365 | } | ||
| 366 | |||
| 367 | std::replace(path.begin(), path.end(), type1, type2); | ||
| 368 | |||
| 369 | auto start = path.begin(); | ||
| 370 | #ifdef _WIN32 | ||
| 371 | // allow network paths which start with a double backslash (e.g. \\server\share) | ||
| 372 | if (start != path.end()) | ||
| 373 | ++start; | ||
| 374 | #endif | ||
| 375 | path.erase(std::unique(start, path.end(), | ||
| 376 | [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }), | ||
| 377 | path.end()); | ||
| 378 | return std::string(RemoveTrailingSlash(path)); | ||
| 379 | } | ||
| 380 | |||
| 381 | std::string_view GetParentPath(std::string_view path) { | ||
| 382 | const auto name_bck_index = path.rfind('\\'); | ||
| 383 | const auto name_fwd_index = path.rfind('/'); | ||
| 384 | std::size_t name_index; | ||
| 385 | |||
| 386 | if (name_bck_index == std::string_view::npos || name_fwd_index == std::string_view::npos) { | ||
| 387 | name_index = std::min(name_bck_index, name_fwd_index); | ||
| 388 | } else { | ||
| 389 | name_index = std::max(name_bck_index, name_fwd_index); | ||
| 390 | } | ||
| 391 | |||
| 392 | return path.substr(0, name_index); | ||
| 393 | } | ||
| 394 | |||
| 395 | std::string_view GetPathWithoutTop(std::string_view path) { | ||
| 396 | if (path.empty()) { | ||
| 397 | return path; | ||
| 398 | } | ||
| 399 | |||
| 400 | while (path[0] == '\\' || path[0] == '/') { | ||
| 401 | path.remove_prefix(1); | ||
| 402 | if (path.empty()) { | ||
| 403 | return path; | ||
| 404 | } | ||
| 405 | } | ||
| 406 | |||
| 407 | const auto name_bck_index = path.find('\\'); | ||
| 408 | const auto name_fwd_index = path.find('/'); | ||
| 409 | return path.substr(std::min(name_bck_index, name_fwd_index) + 1); | ||
| 410 | } | ||
| 411 | |||
| 412 | std::string_view GetFilename(std::string_view path) { | ||
| 413 | const auto name_index = path.find_last_of("\\/"); | ||
| 414 | |||
| 415 | if (name_index == std::string_view::npos) { | ||
| 416 | return {}; | ||
| 417 | } | ||
| 418 | |||
| 419 | return path.substr(name_index + 1); | ||
| 420 | } | ||
| 421 | |||
| 422 | std::string_view GetExtensionFromFilename(std::string_view name) { | ||
| 423 | const std::size_t index = name.rfind('.'); | ||
| 424 | |||
| 425 | if (index == std::string_view::npos) { | ||
| 426 | return {}; | ||
| 427 | } | ||
| 428 | |||
| 429 | return name.substr(index + 1); | ||
| 430 | } | ||
| 431 | |||
| 432 | } // namespace Common::FS | ||
diff --git a/src/common/fs/path_util.h b/src/common/fs/path_util.h new file mode 100644 index 000000000..a9fadbceb --- /dev/null +++ b/src/common/fs/path_util.h | |||
| @@ -0,0 +1,309 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <filesystem> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include "common/fs/fs_util.h" | ||
| 11 | |||
| 12 | namespace Common::FS { | ||
| 13 | |||
| 14 | enum class YuzuPath { | ||
| 15 | YuzuDir, // Where yuzu stores its data. | ||
| 16 | CacheDir, // Where cached filesystem data is stored. | ||
| 17 | ConfigDir, // Where config files are stored. | ||
| 18 | DumpDir, // Where dumped data is stored. | ||
| 19 | KeysDir, // Where key files are stored. | ||
| 20 | LoadDir, // Where cheat/mod files are stored. | ||
| 21 | LogDir, // Where log files are stored. | ||
| 22 | NANDDir, // Where the emulated NAND is stored. | ||
| 23 | ScreenshotsDir, // Where yuzu screenshots are stored. | ||
| 24 | SDMCDir, // Where the emulated SDMC is stored. | ||
| 25 | ShaderDir, // Where shaders are stored. | ||
| 26 | }; | ||
| 27 | |||
| 28 | /** | ||
| 29 | * Converts a filesystem path to a UTF-8 encoded std::string. | ||
| 30 | * | ||
| 31 | * @param path Filesystem path | ||
| 32 | * | ||
| 33 | * @returns UTF-8 encoded std::string. | ||
| 34 | */ | ||
| 35 | [[nodiscard]] std::string PathToUTF8String(const std::filesystem::path& path); | ||
| 36 | |||
| 37 | /** | ||
| 38 | * Validates a given path. | ||
| 39 | * | ||
| 40 | * A given path is valid if it meets these conditions: | ||
| 41 | * - The path is not empty | ||
| 42 | * - The path is not too long | ||
| 43 | * | ||
| 44 | * @param path Filesystem path | ||
| 45 | * | ||
| 46 | * @returns True if the path is valid, false otherwise. | ||
| 47 | */ | ||
| 48 | [[nodiscard]] bool ValidatePath(const std::filesystem::path& path); | ||
| 49 | |||
| 50 | #ifdef _WIN32 | ||
| 51 | template <typename Path> | ||
| 52 | [[nodiscard]] bool ValidatePath(const Path& path) { | ||
| 53 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 54 | return ValidatePath(ToU8String(path)); | ||
| 55 | } else { | ||
| 56 | return ValidatePath(std::filesystem::path{path}); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | #endif | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Concatenates two filesystem paths together. | ||
| 63 | * | ||
| 64 | * This is needed since the following occurs when using std::filesystem::path's operator/: | ||
| 65 | * first: "/first/path" | ||
| 66 | * second: "/second/path" (Note that the second path has a directory separator in the front) | ||
| 67 | * first / second yields "/second/path" when the desired result is first/path/second/path | ||
| 68 | * | ||
| 69 | * @param first First filesystem path | ||
| 70 | * @param second Second filesystem path | ||
| 71 | * | ||
| 72 | * @returns A concatenated filesystem path. | ||
| 73 | */ | ||
| 74 | [[nodiscard]] std::filesystem::path ConcatPath(const std::filesystem::path& first, | ||
| 75 | const std::filesystem::path& second); | ||
| 76 | |||
| 77 | #ifdef _WIN32 | ||
| 78 | template <typename Path1, typename Path2> | ||
| 79 | [[nodiscard]] std::filesystem::path ConcatPath(const Path1& first, const Path2& second) { | ||
| 80 | using ValueType1 = typename Path1::value_type; | ||
| 81 | using ValueType2 = typename Path2::value_type; | ||
| 82 | if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) { | ||
| 83 | return ConcatPath(ToU8String(first), ToU8String(second)); | ||
| 84 | } else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) { | ||
| 85 | return ConcatPath(ToU8String(first), second); | ||
| 86 | } else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) { | ||
| 87 | return ConcatPath(first, ToU8String(second)); | ||
| 88 | } else { | ||
| 89 | return ConcatPath(std::filesystem::path{first}, std::filesystem::path{second}); | ||
| 90 | } | ||
| 91 | } | ||
| 92 | #endif | ||
| 93 | |||
| 94 | /** | ||
| 95 | * Safe variant of ConcatPath that takes in a base path and an offset path from the given base path. | ||
| 96 | * | ||
| 97 | * If ConcatPath(base, offset) resolves to a path that is sandboxed within the base path, | ||
| 98 | * this will return the concatenated path. Otherwise this will return the base path. | ||
| 99 | * | ||
| 100 | * @param base Base filesystem path | ||
| 101 | * @param offset Offset filesystem path | ||
| 102 | * | ||
| 103 | * @returns A concatenated filesystem path if it is within the base path, | ||
| 104 | * returns the base path otherwise. | ||
| 105 | */ | ||
| 106 | [[nodiscard]] std::filesystem::path ConcatPathSafe(const std::filesystem::path& base, | ||
| 107 | const std::filesystem::path& offset); | ||
| 108 | |||
| 109 | #ifdef _WIN32 | ||
| 110 | template <typename Path1, typename Path2> | ||
| 111 | [[nodiscard]] std::filesystem::path ConcatPathSafe(const Path1& base, const Path2& offset) { | ||
| 112 | using ValueType1 = typename Path1::value_type; | ||
| 113 | using ValueType2 = typename Path2::value_type; | ||
| 114 | if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) { | ||
| 115 | return ConcatPathSafe(ToU8String(base), ToU8String(offset)); | ||
| 116 | } else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) { | ||
| 117 | return ConcatPathSafe(ToU8String(base), offset); | ||
| 118 | } else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) { | ||
| 119 | return ConcatPathSafe(base, ToU8String(offset)); | ||
| 120 | } else { | ||
| 121 | return ConcatPathSafe(std::filesystem::path{base}, std::filesystem::path{offset}); | ||
| 122 | } | ||
| 123 | } | ||
| 124 | #endif | ||
| 125 | |||
| 126 | /** | ||
| 127 | * Checks whether a given path is sandboxed within a given base path. | ||
| 128 | * | ||
| 129 | * @param base Base filesystem path | ||
| 130 | * @param path Filesystem path | ||
| 131 | * | ||
| 132 | * @returns True if the given path is sandboxed within the given base path, false otherwise. | ||
| 133 | */ | ||
| 134 | [[nodiscard]] bool IsPathSandboxed(const std::filesystem::path& base, | ||
| 135 | const std::filesystem::path& path); | ||
| 136 | |||
| 137 | #ifdef _WIN32 | ||
| 138 | template <typename Path1, typename Path2> | ||
| 139 | [[nodiscard]] bool IsPathSandboxed(const Path1& base, const Path2& path) { | ||
| 140 | using ValueType1 = typename Path1::value_type; | ||
| 141 | using ValueType2 = typename Path2::value_type; | ||
| 142 | if constexpr (IsChar<ValueType1> && IsChar<ValueType2>) { | ||
| 143 | return IsPathSandboxed(ToU8String(base), ToU8String(path)); | ||
| 144 | } else if constexpr (IsChar<ValueType1> && !IsChar<ValueType2>) { | ||
| 145 | return IsPathSandboxed(ToU8String(base), path); | ||
| 146 | } else if constexpr (!IsChar<ValueType1> && IsChar<ValueType2>) { | ||
| 147 | return IsPathSandboxed(base, ToU8String(path)); | ||
| 148 | } else { | ||
| 149 | return IsPathSandboxed(std::filesystem::path{base}, std::filesystem::path{path}); | ||
| 150 | } | ||
| 151 | } | ||
| 152 | #endif | ||
| 153 | |||
| 154 | /** | ||
| 155 | * Checks if a character is a directory separator (either a forward slash or backslash). | ||
| 156 | * | ||
| 157 | * @param character Character | ||
| 158 | * | ||
| 159 | * @returns True if the character is a directory separator, false otherwise. | ||
| 160 | */ | ||
| 161 | [[nodiscard]] bool IsDirSeparator(char character); | ||
| 162 | |||
| 163 | /** | ||
| 164 | * Checks if a character is a directory separator (either a forward slash or backslash). | ||
| 165 | * | ||
| 166 | * @param character Character | ||
| 167 | * | ||
| 168 | * @returns True if the character is a directory separator, false otherwise. | ||
| 169 | */ | ||
| 170 | [[nodiscard]] bool IsDirSeparator(char8_t character); | ||
| 171 | |||
| 172 | /** | ||
| 173 | * Removes any trailing directory separators if they exist in the given path. | ||
| 174 | * | ||
| 175 | * @param path Filesystem path | ||
| 176 | * | ||
| 177 | * @returns The filesystem path without any trailing directory separators. | ||
| 178 | */ | ||
| 179 | [[nodiscard]] std::filesystem::path RemoveTrailingSeparators(const std::filesystem::path& path); | ||
| 180 | |||
| 181 | #ifdef _WIN32 | ||
| 182 | template <typename Path> | ||
| 183 | [[nodiscard]] std::filesystem::path RemoveTrailingSeparators(const Path& path) { | ||
| 184 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 185 | return RemoveTrailingSeparators(ToU8String(path)); | ||
| 186 | } else { | ||
| 187 | return RemoveTrailingSeparators(std::filesystem::path{path}); | ||
| 188 | } | ||
| 189 | } | ||
| 190 | #endif | ||
| 191 | |||
| 192 | /** | ||
| 193 | * Gets the filesystem path associated with the YuzuPath enum. | ||
| 194 | * | ||
| 195 | * @param yuzu_path YuzuPath enum | ||
| 196 | * | ||
| 197 | * @returns The filesystem path associated with the YuzuPath enum. | ||
| 198 | */ | ||
| 199 | [[nodiscard]] const std::filesystem::path& GetYuzuPath(YuzuPath yuzu_path); | ||
| 200 | |||
| 201 | /** | ||
| 202 | * Gets the filesystem path associated with the YuzuPath enum as a UTF-8 encoded std::string. | ||
| 203 | * | ||
| 204 | * @param yuzu_path YuzuPath enum | ||
| 205 | * | ||
| 206 | * @returns The filesystem path associated with the YuzuPath enum as a UTF-8 encoded std::string. | ||
| 207 | */ | ||
| 208 | [[nodiscard]] std::string GetYuzuPathString(YuzuPath yuzu_path); | ||
| 209 | |||
| 210 | /** | ||
| 211 | * Sets a new filesystem path associated with the YuzuPath enum. | ||
| 212 | * If the filesystem object at new_path is not a directory, this function will not do anything. | ||
| 213 | * | ||
| 214 | * @param yuzu_path YuzuPath enum | ||
| 215 | * @param new_path New filesystem path | ||
| 216 | */ | ||
| 217 | void SetYuzuPath(YuzuPath yuzu_path, const std::filesystem::path& new_path); | ||
| 218 | |||
| 219 | #ifdef _WIN32 | ||
| 220 | template <typename Path> | ||
| 221 | [[nodiscard]] void SetYuzuPath(YuzuPath yuzu_path, const Path& new_path) { | ||
| 222 | if constexpr (IsChar<typename Path::value_type>) { | ||
| 223 | SetYuzuPath(yuzu_path, ToU8String(new_path)); | ||
| 224 | } else { | ||
| 225 | SetYuzuPath(yuzu_path, std::filesystem::path{new_path}); | ||
| 226 | } | ||
| 227 | } | ||
| 228 | #endif | ||
| 229 | |||
| 230 | #ifdef _WIN32 | ||
| 231 | |||
| 232 | /** | ||
| 233 | * Gets the path of the directory containing the executable of the current process. | ||
| 234 | * | ||
| 235 | * @returns The path of the directory containing the executable of the current process. | ||
| 236 | */ | ||
| 237 | [[nodiscard]] std::filesystem::path GetExeDirectory(); | ||
| 238 | |||
| 239 | /** | ||
| 240 | * Gets the path of the current user's %APPDATA% directory (%USERPROFILE%/AppData/Roaming). | ||
| 241 | * | ||
| 242 | * @returns The path of the current user's %APPDATA% directory. | ||
| 243 | */ | ||
| 244 | [[nodiscard]] std::filesystem::path GetAppDataRoamingDirectory(); | ||
| 245 | |||
| 246 | #else | ||
| 247 | |||
| 248 | /** | ||
| 249 | * Gets the path of the directory specified by the #HOME environment variable. | ||
| 250 | * If $HOME is not defined, it will attempt to query the user database in passwd instead. | ||
| 251 | * | ||
| 252 | * @returns The path of the current user's home directory. | ||
| 253 | */ | ||
| 254 | [[nodiscard]] std::filesystem::path GetHomeDirectory(); | ||
| 255 | |||
| 256 | /** | ||
| 257 | * Gets the relevant paths for yuzu to store its data based on the given XDG environment variable. | ||
| 258 | * See https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html | ||
| 259 | * Defaults to $HOME/.local/share for main application data, | ||
| 260 | * $HOME/.cache for cached data, and $HOME/.config for configuration files. | ||
| 261 | * | ||
| 262 | * @param env_name XDG environment variable name | ||
| 263 | * | ||
| 264 | * @returns The path where yuzu should store its data. | ||
| 265 | */ | ||
| 266 | [[nodiscard]] std::filesystem::path GetDataDirectory(const std::string& env_name); | ||
| 267 | |||
| 268 | #endif | ||
| 269 | |||
| 270 | #ifdef __APPLE__ | ||
| 271 | |||
| 272 | [[nodiscard]] std::filesystem::path GetBundleDirectory(); | ||
| 273 | |||
| 274 | #endif | ||
| 275 | |||
| 276 | // vvvvvvvvvv Deprecated vvvvvvvvvv // | ||
| 277 | |||
| 278 | // Removes the final '/' or '\' if one exists | ||
| 279 | [[nodiscard]] std::string_view RemoveTrailingSlash(std::string_view path); | ||
| 280 | |||
| 281 | enum class DirectorySeparator { | ||
| 282 | ForwardSlash, | ||
| 283 | BackwardSlash, | ||
| 284 | PlatformDefault, | ||
| 285 | }; | ||
| 286 | |||
| 287 | // Splits the path on '/' or '\' and put the components into a vector | ||
| 288 | // i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } | ||
| 289 | [[nodiscard]] std::vector<std::string> SplitPathComponents(std::string_view filename); | ||
| 290 | |||
| 291 | // Removes trailing slash, makes all '\\' into '/', and removes duplicate '/'. Makes '/' into '\\' | ||
| 292 | // depending if directory_separator is BackwardSlash or PlatformDefault and running on windows | ||
| 293 | [[nodiscard]] std::string SanitizePath( | ||
| 294 | std::string_view path, | ||
| 295 | DirectorySeparator directory_separator = DirectorySeparator::ForwardSlash); | ||
| 296 | |||
| 297 | // Gets all of the text up to the last '/' or '\' in the path. | ||
| 298 | [[nodiscard]] std::string_view GetParentPath(std::string_view path); | ||
| 299 | |||
| 300 | // Gets all of the text after the first '/' or '\' in the path. | ||
| 301 | [[nodiscard]] std::string_view GetPathWithoutTop(std::string_view path); | ||
| 302 | |||
| 303 | // Gets the filename of the path | ||
| 304 | [[nodiscard]] std::string_view GetFilename(std::string_view path); | ||
| 305 | |||
| 306 | // Gets the extension of the filename | ||
| 307 | [[nodiscard]] std::string_view GetExtensionFromFilename(std::string_view name); | ||
| 308 | |||
| 309 | } // namespace Common::FS | ||
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp index 96efa977d..6aa8ac960 100644 --- a/src/common/logging/backend.cpp +++ b/src/common/logging/backend.cpp | |||
| @@ -11,13 +11,13 @@ | |||
| 11 | #include <mutex> | 11 | #include <mutex> |
| 12 | #include <thread> | 12 | #include <thread> |
| 13 | #include <vector> | 13 | #include <vector> |
| 14 | |||
| 14 | #ifdef _WIN32 | 15 | #ifdef _WIN32 |
| 15 | #include <share.h> // For _SH_DENYWR | ||
| 16 | #include <windows.h> // For OutputDebugStringW | 16 | #include <windows.h> // For OutputDebugStringW |
| 17 | #else | ||
| 18 | #define _SH_DENYWR 0 | ||
| 19 | #endif | 17 | #endif |
| 18 | |||
| 20 | #include "common/assert.h" | 19 | #include "common/assert.h" |
| 20 | #include "common/fs/fs.h" | ||
| 21 | #include "common/logging/backend.h" | 21 | #include "common/logging/backend.h" |
| 22 | #include "common/logging/log.h" | 22 | #include "common/logging/log.h" |
| 23 | #include "common/logging/text_formatter.h" | 23 | #include "common/logging/text_formatter.h" |
| @@ -148,19 +148,16 @@ void ColorConsoleBackend::Write(const Entry& entry) { | |||
| 148 | PrintColoredMessage(entry); | 148 | PrintColoredMessage(entry); |
| 149 | } | 149 | } |
| 150 | 150 | ||
| 151 | FileBackend::FileBackend(const std::string& filename) { | 151 | FileBackend::FileBackend(const std::filesystem::path& filename) { |
| 152 | const auto old_filename = filename + ".old.txt"; | 152 | auto old_filename = filename; |
| 153 | old_filename += ".old.txt"; | ||
| 153 | 154 | ||
| 154 | if (FS::Exists(old_filename)) { | 155 | // Existence checks are done within the functions themselves. |
| 155 | FS::Delete(old_filename); | 156 | // We don't particularly care if these succeed or not. |
| 156 | } | 157 | void(FS::RemoveFile(old_filename)); |
| 157 | if (FS::Exists(filename)) { | 158 | void(FS::RenameFile(filename, old_filename)); |
| 158 | FS::Rename(filename, old_filename); | ||
| 159 | } | ||
| 160 | 159 | ||
| 161 | // _SH_DENYWR allows read only access to the file for other programs. | 160 | file = FS::IOFile(filename, FS::FileAccessMode::Write, FS::FileType::TextFile); |
| 162 | // It is #defined to 0 on other platforms | ||
| 163 | file = FS::IOFile(filename, "w", _SH_DENYWR); | ||
| 164 | } | 161 | } |
| 165 | 162 | ||
| 166 | void FileBackend::Write(const Entry& entry) { | 163 | void FileBackend::Write(const Entry& entry) { |
| @@ -181,7 +178,7 @@ void FileBackend::Write(const Entry& entry) { | |||
| 181 | 178 | ||
| 182 | bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n')); | 179 | bytes_written += file.WriteString(FormatLogMessage(entry).append(1, '\n')); |
| 183 | if (entry.log_level >= Level::Error) { | 180 | if (entry.log_level >= Level::Error) { |
| 184 | file.Flush(); | 181 | void(file.Flush()); |
| 185 | } | 182 | } |
| 186 | } | 183 | } |
| 187 | 184 | ||
diff --git a/src/common/logging/backend.h b/src/common/logging/backend.h index 9dd2589c3..eb629a33f 100644 --- a/src/common/logging/backend.h +++ b/src/common/logging/backend.h | |||
| @@ -4,10 +4,11 @@ | |||
| 4 | #pragma once | 4 | #pragma once |
| 5 | 5 | ||
| 6 | #include <chrono> | 6 | #include <chrono> |
| 7 | #include <filesystem> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <string_view> | 10 | #include <string_view> |
| 10 | #include "common/file_util.h" | 11 | #include "common/fs/file.h" |
| 11 | #include "common/logging/filter.h" | 12 | #include "common/logging/filter.h" |
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 13 | 14 | ||
| @@ -81,7 +82,7 @@ public: | |||
| 81 | */ | 82 | */ |
| 82 | class FileBackend : public Backend { | 83 | class FileBackend : public Backend { |
| 83 | public: | 84 | public: |
| 84 | explicit FileBackend(const std::string& filename); | 85 | explicit FileBackend(const std::filesystem::path& filename); |
| 85 | 86 | ||
| 86 | static const char* Name() { | 87 | static const char* Name() { |
| 87 | return "file"; | 88 | return "file"; |
diff --git a/src/common/nvidia_flags.cpp b/src/common/nvidia_flags.cpp index d537517db..d1afd1f1d 100644 --- a/src/common/nvidia_flags.cpp +++ b/src/common/nvidia_flags.cpp | |||
| @@ -2,24 +2,30 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <filesystem> | 5 | #include <cstdlib> |
| 6 | #include <stdlib.h> | ||
| 7 | 6 | ||
| 8 | #include <fmt/format.h> | 7 | #include <fmt/format.h> |
| 9 | 8 | ||
| 10 | #include "common/file_util.h" | 9 | #include "common/fs/file.h" |
| 10 | #include "common/fs/fs.h" | ||
| 11 | #include "common/fs/path_util.h" | ||
| 11 | #include "common/nvidia_flags.h" | 12 | #include "common/nvidia_flags.h" |
| 12 | 13 | ||
| 13 | namespace Common { | 14 | namespace Common { |
| 14 | 15 | ||
| 15 | void ConfigureNvidiaEnvironmentFlags() { | 16 | void ConfigureNvidiaEnvironmentFlags() { |
| 16 | #ifdef _WIN32 | 17 | #ifdef _WIN32 |
| 17 | const std::string shader_path = Common::FS::SanitizePath( | 18 | const auto nvidia_shader_dir = |
| 18 | fmt::format("{}/nvidia/", Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir))); | 19 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir) / "nvidia"; |
| 19 | const std::string windows_path = | 20 | |
| 20 | Common::FS::SanitizePath(shader_path, Common::FS::DirectorySeparator::BackwardSlash); | 21 | if (!Common::FS::CreateDirs(nvidia_shader_dir)) { |
| 21 | void(Common::FS::CreateFullPath(shader_path + '/')); | 22 | return; |
| 22 | void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path).c_str())); | 23 | } |
| 24 | |||
| 25 | const auto windows_path_string = | ||
| 26 | Common::FS::PathToUTF8String(nvidia_shader_dir.lexically_normal()); | ||
| 27 | |||
| 28 | void(_putenv(fmt::format("__GL_SHADER_DISK_CACHE_PATH={}", windows_path_string).c_str())); | ||
| 23 | void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1")); | 29 | void(_putenv("__GL_SHADER_DISK_CACHE_SKIP_CLEANUP=1")); |
| 24 | #endif | 30 | #endif |
| 25 | } | 31 | } |
diff --git a/src/common/settings.cpp b/src/common/settings.cpp index e29cbf506..bcb4e4be1 100644 --- a/src/common/settings.cpp +++ b/src/common/settings.cpp | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #include <string_view> | 5 | #include <string_view> |
| 6 | 6 | ||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/file_util.h" | 8 | #include "common/fs/path_util.h" |
| 9 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/settings.h" | 10 | #include "common/settings.h" |
| 11 | 11 | ||
| @@ -34,6 +34,10 @@ void LogSettings() { | |||
| 34 | LOG_INFO(Config, "{}: {}", name, value); | 34 | LOG_INFO(Config, "{}: {}", name, value); |
| 35 | }; | 35 | }; |
| 36 | 36 | ||
| 37 | const auto log_path = [](std::string_view name, const std::filesystem::path& path) { | ||
| 38 | LOG_INFO(Config, "{}: {}", name, Common::FS::PathToUTF8String(path)); | ||
| 39 | }; | ||
| 40 | |||
| 37 | LOG_INFO(Config, "yuzu Configuration:"); | 41 | LOG_INFO(Config, "yuzu Configuration:"); |
| 38 | log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); | 42 | log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue()); |
| 39 | log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); | 43 | log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0)); |
| @@ -59,11 +63,11 @@ void LogSettings() { | |||
| 59 | log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); | 63 | log_setting("Audio_EnableAudioStretching", values.enable_audio_stretching.GetValue()); |
| 60 | log_setting("Audio_OutputDevice", values.audio_device_id); | 64 | log_setting("Audio_OutputDevice", values.audio_device_id); |
| 61 | log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); | 65 | log_setting("DataStorage_UseVirtualSd", values.use_virtual_sd); |
| 62 | log_setting("DataStorage_CacheDir", Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)); | 66 | log_path("DataStorage_CacheDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir)); |
| 63 | log_setting("DataStorage_ConfigDir", Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir)); | 67 | log_path("DataStorage_ConfigDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir)); |
| 64 | log_setting("DataStorage_LoadDir", Common::FS::GetUserPath(Common::FS::UserPath::LoadDir)); | 68 | log_path("DataStorage_LoadDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::LoadDir)); |
| 65 | log_setting("DataStorage_NandDir", Common::FS::GetUserPath(Common::FS::UserPath::NANDDir)); | 69 | log_path("DataStorage_NANDDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir)); |
| 66 | log_setting("DataStorage_SdmcDir", Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir)); | 70 | log_path("DataStorage_SDMCDir", Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir)); |
| 67 | log_setting("Debugging_ProgramArgs", values.program_args); | 71 | log_setting("Debugging_ProgramArgs", values.program_args); |
| 68 | log_setting("Services_BCATBackend", values.bcat_backend); | 72 | log_setting("Services_BCATBackend", values.bcat_backend); |
| 69 | log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local); | 73 | log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local); |
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp index 7b614ad89..e6344fd41 100644 --- a/src/common/string_util.cpp +++ b/src/common/string_util.cpp | |||
| @@ -9,7 +9,6 @@ | |||
| 9 | #include <locale> | 9 | #include <locale> |
| 10 | #include <sstream> | 10 | #include <sstream> |
| 11 | 11 | ||
| 12 | #include "common/common_paths.h" | ||
| 13 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 14 | #include "common/string_util.h" | 13 | #include "common/string_util.h" |
| 15 | 14 | ||
| @@ -93,18 +92,6 @@ bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _ | |||
| 93 | return true; | 92 | return true; |
| 94 | } | 93 | } |
| 95 | 94 | ||
| 96 | void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, | ||
| 97 | const std::string& _Filename) { | ||
| 98 | _CompleteFilename = _Path; | ||
| 99 | |||
| 100 | // check for seperator | ||
| 101 | if (DIR_SEP_CHR != *_CompleteFilename.rbegin()) | ||
| 102 | _CompleteFilename += DIR_SEP_CHR; | ||
| 103 | |||
| 104 | // add the filename | ||
| 105 | _CompleteFilename += _Filename; | ||
| 106 | } | ||
| 107 | |||
| 108 | void SplitString(const std::string& str, const char delim, std::vector<std::string>& output) { | 95 | void SplitString(const std::string& str, const char delim, std::vector<std::string>& output) { |
| 109 | std::istringstream iss(str); | 96 | std::istringstream iss(str); |
| 110 | output.resize(1); | 97 | output.resize(1); |
diff --git a/src/common/string_util.h b/src/common/string_util.h index a32c07c06..7e90a9ca5 100644 --- a/src/common/string_util.h +++ b/src/common/string_util.h | |||
| @@ -32,8 +32,6 @@ void SplitString(const std::string& str, char delim, std::vector<std::string>& o | |||
| 32 | bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, | 32 | bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, |
| 33 | std::string* _pExtension); | 33 | std::string* _pExtension); |
| 34 | 34 | ||
| 35 | void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, | ||
| 36 | const std::string& _Filename); | ||
| 37 | [[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src, | 35 | [[nodiscard]] std::string ReplaceAll(std::string result, const std::string& src, |
| 38 | const std::string& dest); | 36 | const std::string& dest); |
| 39 | 37 | ||
diff --git a/src/core/core.cpp b/src/core/core.cpp index 826a00ad6..c5004b7b4 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include <memory> | 6 | #include <memory> |
| 7 | #include <utility> | 7 | #include <utility> |
| 8 | 8 | ||
| 9 | #include "common/file_util.h" | 9 | #include "common/fs/fs.h" |
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/microprofile.h" | 11 | #include "common/microprofile.h" |
| 12 | #include "common/settings.h" | 12 | #include "common/settings.h" |
| @@ -121,7 +121,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs, | |||
| 121 | dir->GetName()); | 121 | dir->GetName()); |
| 122 | } | 122 | } |
| 123 | 123 | ||
| 124 | if (Common::FS::IsDirectory(path)) { | 124 | if (Common::FS::IsDir(path)) { |
| 125 | return vfs->OpenFile(path + "/main", FileSys::Mode::Read); | 125 | return vfs->OpenFile(path + "/main", FileSys::Mode::Read); |
| 126 | } | 126 | } |
| 127 | 127 | ||
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index a4b739c63..fb451a423 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp | |||
| @@ -18,8 +18,9 @@ | |||
| 18 | #include <mbedtls/cmac.h> | 18 | #include <mbedtls/cmac.h> |
| 19 | #include <mbedtls/sha256.h> | 19 | #include <mbedtls/sha256.h> |
| 20 | #include "common/common_funcs.h" | 20 | #include "common/common_funcs.h" |
| 21 | #include "common/common_paths.h" | 21 | #include "common/fs/file.h" |
| 22 | #include "common/file_util.h" | 22 | #include "common/fs/fs.h" |
| 23 | #include "common/fs/path_util.h" | ||
| 23 | #include "common/hex_util.h" | 24 | #include "common/hex_util.h" |
| 24 | #include "common/logging/log.h" | 25 | #include "common/logging/log.h" |
| 25 | #include "common/settings.h" | 26 | #include "common/settings.h" |
| @@ -325,46 +326,55 @@ Key128 DeriveKeyblobMACKey(const Key128& keyblob_key, const Key128& mac_source) | |||
| 325 | } | 326 | } |
| 326 | 327 | ||
| 327 | std::optional<Key128> DeriveSDSeed() { | 328 | std::optional<Key128> DeriveSDSeed() { |
| 328 | const Common::FS::IOFile save_43(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + | 329 | const auto system_save_43_path = |
| 329 | "/system/save/8000000000000043", | 330 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000043"; |
| 330 | "rb+"); | 331 | const Common::FS::IOFile save_43{system_save_43_path, Common::FS::FileAccessMode::Read, |
| 332 | Common::FS::FileType::BinaryFile}; | ||
| 333 | |||
| 331 | if (!save_43.IsOpen()) { | 334 | if (!save_43.IsOpen()) { |
| 332 | return std::nullopt; | 335 | return std::nullopt; |
| 333 | } | 336 | } |
| 334 | 337 | ||
| 335 | const Common::FS::IOFile sd_private(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) + | 338 | const auto sd_private_path = |
| 336 | "/Nintendo/Contents/private", | 339 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "Nintendo/Contents/private"; |
| 337 | "rb+"); | 340 | |
| 341 | const Common::FS::IOFile sd_private{sd_private_path, Common::FS::FileAccessMode::Read, | ||
| 342 | Common::FS::FileType::BinaryFile}; | ||
| 343 | |||
| 338 | if (!sd_private.IsOpen()) { | 344 | if (!sd_private.IsOpen()) { |
| 339 | return std::nullopt; | 345 | return std::nullopt; |
| 340 | } | 346 | } |
| 341 | 347 | ||
| 342 | std::array<u8, 0x10> private_seed{}; | 348 | std::array<u8, 0x10> private_seed{}; |
| 343 | if (sd_private.ReadBytes(private_seed.data(), private_seed.size()) != private_seed.size()) { | 349 | if (sd_private.Read(private_seed) != private_seed.size()) { |
| 344 | return std::nullopt; | 350 | return std::nullopt; |
| 345 | } | 351 | } |
| 346 | 352 | ||
| 347 | std::array<u8, 0x10> buffer{}; | 353 | std::array<u8, 0x10> buffer{}; |
| 348 | std::size_t offset = 0; | 354 | s64 offset = 0; |
| 349 | for (; offset + 0x10 < save_43.GetSize(); ++offset) { | 355 | for (; offset + 0x10 < static_cast<s64>(save_43.GetSize()); ++offset) { |
| 350 | if (!save_43.Seek(offset, SEEK_SET)) { | 356 | if (!save_43.Seek(offset)) { |
| 357 | return std::nullopt; | ||
| 358 | } | ||
| 359 | |||
| 360 | if (save_43.Read(buffer) != buffer.size()) { | ||
| 351 | return std::nullopt; | 361 | return std::nullopt; |
| 352 | } | 362 | } |
| 353 | 363 | ||
| 354 | save_43.ReadBytes(buffer.data(), buffer.size()); | ||
| 355 | if (buffer == private_seed) { | 364 | if (buffer == private_seed) { |
| 356 | break; | 365 | break; |
| 357 | } | 366 | } |
| 358 | } | 367 | } |
| 359 | 368 | ||
| 360 | if (!save_43.Seek(offset + 0x10, SEEK_SET)) { | 369 | if (!save_43.Seek(offset + 0x10)) { |
| 361 | return std::nullopt; | 370 | return std::nullopt; |
| 362 | } | 371 | } |
| 363 | 372 | ||
| 364 | Key128 seed{}; | 373 | Key128 seed{}; |
| 365 | if (save_43.ReadBytes(seed.data(), seed.size()) != seed.size()) { | 374 | if (save_43.Read(seed) != seed.size()) { |
| 366 | return std::nullopt; | 375 | return std::nullopt; |
| 367 | } | 376 | } |
| 377 | |||
| 368 | return seed; | 378 | return seed; |
| 369 | } | 379 | } |
| 370 | 380 | ||
| @@ -435,7 +445,7 @@ std::vector<Ticket> GetTicketblob(const Common::FS::IOFile& ticket_save) { | |||
| 435 | } | 445 | } |
| 436 | 446 | ||
| 437 | std::vector<u8> buffer(ticket_save.GetSize()); | 447 | std::vector<u8> buffer(ticket_save.GetSize()); |
| 438 | if (ticket_save.ReadBytes(buffer.data(), buffer.size()) != buffer.size()) { | 448 | if (ticket_save.Read(buffer) != buffer.size()) { |
| 439 | return {}; | 449 | return {}; |
| 440 | } | 450 | } |
| 441 | 451 | ||
| @@ -566,27 +576,26 @@ std::optional<std::pair<Key128, Key128>> ParseTicket(const Ticket& ticket, | |||
| 566 | 576 | ||
| 567 | KeyManager::KeyManager() { | 577 | KeyManager::KeyManager() { |
| 568 | // Initialize keys | 578 | // Initialize keys |
| 569 | const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath(); | 579 | const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); |
| 570 | const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir); | ||
| 571 | 580 | ||
| 572 | if (!Common::FS::Exists(yuzu_keys_dir)) { | 581 | if (!Common::FS::CreateDir(yuzu_keys_dir)) { |
| 573 | Common::FS::CreateDir(yuzu_keys_dir); | 582 | LOG_ERROR(Core, "Failed to create the keys directory."); |
| 574 | } | 583 | } |
| 575 | 584 | ||
| 576 | if (Settings::values.use_dev_keys) { | 585 | if (Settings::values.use_dev_keys) { |
| 577 | dev_mode = true; | 586 | dev_mode = true; |
| 578 | AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "dev.keys", false); | 587 | LoadFromFile(yuzu_keys_dir / "dev.keys", false); |
| 579 | AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "dev.keys_autogenerated", false); | 588 | LoadFromFile(yuzu_keys_dir / "dev.keys_autogenerated", false); |
| 580 | } else { | 589 | } else { |
| 581 | dev_mode = false; | 590 | dev_mode = false; |
| 582 | AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "prod.keys", false); | 591 | LoadFromFile(yuzu_keys_dir / "prod.keys", false); |
| 583 | AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "prod.keys_autogenerated", false); | 592 | LoadFromFile(yuzu_keys_dir / "prod.keys_autogenerated", false); |
| 584 | } | 593 | } |
| 585 | 594 | ||
| 586 | AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "title.keys", true); | 595 | LoadFromFile(yuzu_keys_dir / "title.keys", true); |
| 587 | AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "title.keys_autogenerated", true); | 596 | LoadFromFile(yuzu_keys_dir / "title.keys_autogenerated", true); |
| 588 | AttemptLoadKeyFile(yuzu_keys_dir, hactool_keys_dir, "console.keys", false); | 597 | LoadFromFile(yuzu_keys_dir / "console.keys", false); |
| 589 | AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, "console.keys_autogenerated", false); | 598 | LoadFromFile(yuzu_keys_dir / "console.keys_autogenerated", false); |
| 590 | } | 599 | } |
| 591 | 600 | ||
| 592 | static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) { | 601 | static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_t length) { |
| @@ -597,9 +606,14 @@ static bool ValidCryptoRevisionString(std::string_view base, size_t begin, size_ | |||
| 597 | [](u8 c) { return std::isxdigit(c); }); | 606 | [](u8 c) { return std::isxdigit(c); }); |
| 598 | } | 607 | } |
| 599 | 608 | ||
| 600 | void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { | 609 | void KeyManager::LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys) { |
| 610 | if (!Common::FS::Exists(file_path)) { | ||
| 611 | return; | ||
| 612 | } | ||
| 613 | |||
| 601 | std::ifstream file; | 614 | std::ifstream file; |
| 602 | Common::FS::OpenFStream(file, filename, std::ios_base::in); | 615 | Common::FS::OpenFileStream(file, file_path, std::ios_base::in); |
| 616 | |||
| 603 | if (!file.is_open()) { | 617 | if (!file.is_open()) { |
| 604 | return; | 618 | return; |
| 605 | } | 619 | } |
| @@ -694,15 +708,6 @@ void KeyManager::LoadFromFile(const std::string& filename, bool is_title_keys) { | |||
| 694 | } | 708 | } |
| 695 | } | 709 | } |
| 696 | 710 | ||
| 697 | void KeyManager::AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, | ||
| 698 | const std::string& filename, bool title) { | ||
| 699 | if (Common::FS::Exists(dir1 + DIR_SEP + filename)) { | ||
| 700 | LoadFromFile(dir1 + DIR_SEP + filename, title); | ||
| 701 | } else if (Common::FS::Exists(dir2 + DIR_SEP + filename)) { | ||
| 702 | LoadFromFile(dir2 + DIR_SEP + filename, title); | ||
| 703 | } | ||
| 704 | } | ||
| 705 | |||
| 706 | bool KeyManager::BaseDeriveNecessary() const { | 711 | bool KeyManager::BaseDeriveNecessary() const { |
| 707 | const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) { | 712 | const auto check_key_existence = [this](auto key_type, u64 index1 = 0, u64 index2 = 0) { |
| 708 | return !HasKey(key_type, index1, index2); | 713 | return !HasKey(key_type, index1, index2); |
| @@ -766,30 +771,35 @@ Key256 KeyManager::GetBISKey(u8 partition_id) const { | |||
| 766 | template <size_t Size> | 771 | template <size_t Size> |
| 767 | void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname, | 772 | void KeyManager::WriteKeyToFile(KeyCategory category, std::string_view keyname, |
| 768 | const std::array<u8, Size>& key) { | 773 | const std::array<u8, Size>& key) { |
| 769 | const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir); | 774 | const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); |
| 775 | |||
| 770 | std::string filename = "title.keys_autogenerated"; | 776 | std::string filename = "title.keys_autogenerated"; |
| 777 | |||
| 771 | if (category == KeyCategory::Standard) { | 778 | if (category == KeyCategory::Standard) { |
| 772 | filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; | 779 | filename = dev_mode ? "dev.keys_autogenerated" : "prod.keys_autogenerated"; |
| 773 | } else if (category == KeyCategory::Console) { | 780 | } else if (category == KeyCategory::Console) { |
| 774 | filename = "console.keys_autogenerated"; | 781 | filename = "console.keys_autogenerated"; |
| 775 | } | 782 | } |
| 776 | 783 | ||
| 777 | const auto path = yuzu_keys_dir + DIR_SEP + filename; | 784 | const auto path = yuzu_keys_dir / filename; |
| 778 | const auto add_info_text = !Common::FS::Exists(path); | 785 | const auto add_info_text = !Common::FS::Exists(path); |
| 779 | Common::FS::CreateFullPath(path); | 786 | |
| 780 | Common::FS::IOFile file{path, "a"}; | 787 | Common::FS::IOFile file{path, Common::FS::FileAccessMode::Append, |
| 788 | Common::FS::FileType::TextFile}; | ||
| 789 | |||
| 781 | if (!file.IsOpen()) { | 790 | if (!file.IsOpen()) { |
| 782 | return; | 791 | return; |
| 783 | } | 792 | } |
| 793 | |||
| 784 | if (add_info_text) { | 794 | if (add_info_text) { |
| 785 | file.WriteString( | 795 | void(file.WriteString( |
| 786 | "# This file is autogenerated by Yuzu\n" | 796 | "# This file is autogenerated by Yuzu\n" |
| 787 | "# It serves to store keys that were automatically generated from the normal keys\n" | 797 | "# It serves to store keys that were automatically generated from the normal keys\n" |
| 788 | "# If you are experiencing issues involving keys, it may help to delete this file\n"); | 798 | "# If you are experiencing issues involving keys, it may help to delete this file\n")); |
| 789 | } | 799 | } |
| 790 | 800 | ||
| 791 | file.WriteString(fmt::format("\n{} = {}", keyname, Common::HexToString(key))); | 801 | void(file.WriteString(fmt::format("\n{} = {}", keyname, Common::HexToString(key)))); |
| 792 | AttemptLoadKeyFile(yuzu_keys_dir, yuzu_keys_dir, filename, category == KeyCategory::Title); | 802 | LoadFromFile(path, category == KeyCategory::Title); |
| 793 | } | 803 | } |
| 794 | 804 | ||
| 795 | void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { | 805 | void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { |
| @@ -861,20 +871,17 @@ void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { | |||
| 861 | } | 871 | } |
| 862 | 872 | ||
| 863 | bool KeyManager::KeyFileExists(bool title) { | 873 | bool KeyManager::KeyFileExists(bool title) { |
| 864 | const std::string hactool_keys_dir = Common::FS::GetHactoolConfigurationPath(); | 874 | const auto yuzu_keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); |
| 865 | const std::string yuzu_keys_dir = Common::FS::GetUserPath(Common::FS::UserPath::KeysDir); | 875 | |
| 866 | if (title) { | 876 | if (title) { |
| 867 | return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "title.keys") || | 877 | return Common::FS::Exists(yuzu_keys_dir / "title.keys"); |
| 868 | Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "title.keys"); | ||
| 869 | } | 878 | } |
| 870 | 879 | ||
| 871 | if (Settings::values.use_dev_keys) { | 880 | if (Settings::values.use_dev_keys) { |
| 872 | return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "dev.keys") || | 881 | return Common::FS::Exists(yuzu_keys_dir / "dev.keys"); |
| 873 | Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "dev.keys"); | ||
| 874 | } | 882 | } |
| 875 | 883 | ||
| 876 | return Common::FS::Exists(hactool_keys_dir + DIR_SEP + "prod.keys") || | 884 | return Common::FS::Exists(yuzu_keys_dir / "prod.keys"); |
| 877 | Common::FS::Exists(yuzu_keys_dir + DIR_SEP + "prod.keys"); | ||
| 878 | } | 885 | } |
| 879 | 886 | ||
| 880 | void KeyManager::DeriveSDSeedLazy() { | 887 | void KeyManager::DeriveSDSeedLazy() { |
| @@ -1115,15 +1122,21 @@ void KeyManager::PopulateTickets() { | |||
| 1115 | return; | 1122 | return; |
| 1116 | } | 1123 | } |
| 1117 | 1124 | ||
| 1118 | const Common::FS::IOFile save1(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + | 1125 | const auto system_save_e1_path = |
| 1119 | "/system/save/80000000000000e1", | 1126 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e1"; |
| 1120 | "rb+"); | 1127 | |
| 1121 | const Common::FS::IOFile save2(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + | 1128 | const Common::FS::IOFile save_e1{system_save_e1_path, Common::FS::FileAccessMode::Read, |
| 1122 | "/system/save/80000000000000e2", | 1129 | Common::FS::FileType::BinaryFile}; |
| 1123 | "rb+"); | 1130 | |
| 1131 | const auto system_save_e2_path = | ||
| 1132 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/80000000000000e2"; | ||
| 1133 | |||
| 1134 | const Common::FS::IOFile save_e2{system_save_e2_path, Common::FS::FileAccessMode::Read, | ||
| 1135 | Common::FS::FileType::BinaryFile}; | ||
| 1136 | |||
| 1137 | const auto blob2 = GetTicketblob(save_e2); | ||
| 1138 | auto res = GetTicketblob(save_e1); | ||
| 1124 | 1139 | ||
| 1125 | const auto blob2 = GetTicketblob(save2); | ||
| 1126 | auto res = GetTicketblob(save1); | ||
| 1127 | const auto idx = res.size(); | 1140 | const auto idx = res.size(); |
| 1128 | res.insert(res.end(), blob2.begin(), blob2.end()); | 1141 | res.insert(res.end(), blob2.begin(), blob2.end()); |
| 1129 | 1142 | ||
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index 0a7220286..e771625e1 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <filesystem> | ||
| 8 | #include <map> | 9 | #include <map> |
| 9 | #include <optional> | 10 | #include <optional> |
| 10 | #include <string> | 11 | #include <string> |
| @@ -283,9 +284,8 @@ private: | |||
| 283 | std::array<u8, 576> eticket_extended_kek{}; | 284 | std::array<u8, 576> eticket_extended_kek{}; |
| 284 | 285 | ||
| 285 | bool dev_mode; | 286 | bool dev_mode; |
| 286 | void LoadFromFile(const std::string& filename, bool is_title_keys); | 287 | void LoadFromFile(const std::filesystem::path& file_path, bool is_title_keys); |
| 287 | void AttemptLoadKeyFile(const std::string& dir1, const std::string& dir2, | 288 | |
| 288 | const std::string& filename, bool title); | ||
| 289 | template <size_t Size> | 289 | template <size_t Size> |
| 290 | void WriteKeyToFile(KeyCategory category, std::string_view keyname, | 290 | void WriteKeyToFile(KeyCategory category, std::string_view keyname, |
| 291 | const std::array<u8, Size>& key); | 291 | const std::array<u8, Size>& key); |
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index 7c6304ff0..f3891acf1 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp | |||
| @@ -3,7 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <fmt/format.h> | 5 | #include <fmt/format.h> |
| 6 | #include "common/file_util.h" | 6 | #include "common/fs/path_util.h" |
| 7 | #include "core/file_sys/bis_factory.h" | 7 | #include "core/file_sys/bis_factory.h" |
| 8 | #include "core/file_sys/mode.h" | 8 | #include "core/file_sys/mode.h" |
| 9 | #include "core/file_sys/registered_cache.h" | 9 | #include "core/file_sys/registered_cache.h" |
| @@ -85,7 +85,7 @@ VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id, | |||
| 85 | VirtualFilesystem file_system) const { | 85 | VirtualFilesystem file_system) const { |
| 86 | auto& keys = Core::Crypto::KeyManager::Instance(); | 86 | auto& keys = Core::Crypto::KeyManager::Instance(); |
| 87 | Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory( | 87 | Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory( |
| 88 | Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)}; | 88 | Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir), Mode::Read)}; |
| 89 | keys.PopulateFromPartitionData(pdm); | 89 | keys.PopulateFromPartitionData(pdm); |
| 90 | 90 | ||
| 91 | switch (id) { | 91 | switch (id) { |
diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h index 2b4f21073..6c49a64e2 100644 --- a/src/core/file_sys/mode.h +++ b/src/core/file_sys/mode.h | |||
| @@ -10,11 +10,13 @@ | |||
| 10 | namespace FileSys { | 10 | namespace FileSys { |
| 11 | 11 | ||
| 12 | enum class Mode : u32 { | 12 | enum class Mode : u32 { |
| 13 | Read = 1, | 13 | Read = 1 << 0, |
| 14 | Write = 2, | 14 | Write = 1 << 1, |
| 15 | ReadWrite = Read | Write, | 15 | ReadWrite = Read | Write, |
| 16 | Append = 4, | 16 | Append = 1 << 2, |
| 17 | ReadAppend = Read | Append, | ||
| 17 | WriteAppend = Write | Append, | 18 | WriteAppend = Write | Append, |
| 19 | All = ReadWrite | Append, | ||
| 18 | }; | 20 | }; |
| 19 | 21 | ||
| 20 | DECLARE_ENUM_FLAG_OPERATORS(Mode) | 22 | DECLARE_ENUM_FLAG_OPERATORS(Mode) |
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 48a2ed4d4..c5967049e 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp | |||
| @@ -8,7 +8,6 @@ | |||
| 8 | #include <iterator> | 8 | #include <iterator> |
| 9 | #include <utility> | 9 | #include <utility> |
| 10 | 10 | ||
| 11 | #include "common/file_util.h" | ||
| 12 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 13 | #include "core/file_sys/partition_filesystem.h" | 12 | #include "core/file_sys/partition_filesystem.h" |
| 14 | #include "core/file_sys/vfs_offset.h" | 13 | #include "core/file_sys/vfs_offset.h" |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index cc9b4b637..53b8b7ca0 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include <cstddef> | 7 | #include <cstddef> |
| 8 | #include <cstring> | 8 | #include <cstring> |
| 9 | 9 | ||
| 10 | #include "common/file_util.h" | ||
| 11 | #include "common/hex_util.h" | 10 | #include "common/hex_util.h" |
| 12 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 13 | #include "common/settings.h" | 12 | #include "common/settings.h" |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index b0cb65952..066c6789a 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include <regex> | 7 | #include <regex> |
| 8 | #include <mbedtls/sha256.h> | 8 | #include <mbedtls/sha256.h> |
| 9 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 10 | #include "common/file_util.h" | 10 | #include "common/fs/path_util.h" |
| 11 | #include "common/hex_util.h" | 11 | #include "common/hex_util.h" |
| 12 | #include "common/logging/log.h" | 12 | #include "common/logging/log.h" |
| 13 | #include "core/crypto/key_manager.h" | 13 | #include "core/crypto/key_manager.h" |
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp index f497e9396..215e1cb1a 100644 --- a/src/core/file_sys/vfs.cpp +++ b/src/core/file_sys/vfs.cpp | |||
| @@ -5,8 +5,7 @@ | |||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <numeric> | 6 | #include <numeric> |
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include "common/common_paths.h" | 8 | #include "common/fs/path_util.h" |
| 9 | #include "common/file_util.h" | ||
| 10 | #include "common/logging/backend.h" | 9 | #include "common/logging/backend.h" |
| 11 | #include "core/file_sys/mode.h" | 10 | #include "core/file_sys/mode.h" |
| 12 | #include "core/file_sys/vfs.h" | 11 | #include "core/file_sys/vfs.h" |
| @@ -122,15 +121,14 @@ VirtualDir VfsFilesystem::CopyDirectory(std::string_view old_path_, std::string_ | |||
| 122 | return nullptr; | 121 | return nullptr; |
| 123 | 122 | ||
| 124 | for (const auto& file : old_dir->GetFiles()) { | 123 | for (const auto& file : old_dir->GetFiles()) { |
| 125 | const auto x = | 124 | const auto x = CopyFile(old_path + '/' + file->GetName(), new_path + '/' + file->GetName()); |
| 126 | CopyFile(old_path + DIR_SEP + file->GetName(), new_path + DIR_SEP + file->GetName()); | ||
| 127 | if (x == nullptr) | 125 | if (x == nullptr) |
| 128 | return nullptr; | 126 | return nullptr; |
| 129 | } | 127 | } |
| 130 | 128 | ||
| 131 | for (const auto& dir : old_dir->GetSubdirectories()) { | 129 | for (const auto& dir : old_dir->GetSubdirectories()) { |
| 132 | const auto x = | 130 | const auto x = |
| 133 | CopyDirectory(old_path + DIR_SEP + dir->GetName(), new_path + DIR_SEP + dir->GetName()); | 131 | CopyDirectory(old_path + '/' + dir->GetName(), new_path + '/' + dir->GetName()); |
| 134 | if (x == nullptr) | 132 | if (x == nullptr) |
| 135 | return nullptr; | 133 | return nullptr; |
| 136 | } | 134 | } |
diff --git a/src/core/file_sys/vfs_libzip.cpp b/src/core/file_sys/vfs_libzip.cpp index 618eb658a..cd162c0c3 100644 --- a/src/core/file_sys/vfs_libzip.cpp +++ b/src/core/file_sys/vfs_libzip.cpp | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #pragma GCC diagnostic pop | 13 | #pragma GCC diagnostic pop |
| 14 | #endif | 14 | #endif |
| 15 | 15 | ||
| 16 | #include "common/fs/path_util.h" | ||
| 16 | #include "common/logging/backend.h" | 17 | #include "common/logging/backend.h" |
| 17 | #include "core/file_sys/vfs.h" | 18 | #include "core/file_sys/vfs.h" |
| 18 | #include "core/file_sys/vfs_libzip.h" | 19 | #include "core/file_sys/vfs_libzip.h" |
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 3d89dd644..d0b8fd046 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp | |||
| @@ -7,8 +7,9 @@ | |||
| 7 | #include <iterator> | 7 | #include <iterator> |
| 8 | #include <utility> | 8 | #include <utility> |
| 9 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 10 | #include "common/common_paths.h" | 10 | #include "common/fs/file.h" |
| 11 | #include "common/file_util.h" | 11 | #include "common/fs/fs.h" |
| 12 | #include "common/fs/path_util.h" | ||
| 12 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 13 | #include "core/file_sys/vfs_real.h" | 14 | #include "core/file_sys/vfs_real.h" |
| 14 | 15 | ||
| @@ -16,33 +17,31 @@ namespace FileSys { | |||
| 16 | 17 | ||
| 17 | namespace FS = Common::FS; | 18 | namespace FS = Common::FS; |
| 18 | 19 | ||
| 19 | static std::string ModeFlagsToString(Mode mode) { | 20 | namespace { |
| 20 | std::string mode_str; | 21 | |
| 21 | 22 | constexpr FS::FileAccessMode ModeFlagsToFileAccessMode(Mode mode) { | |
| 22 | // Calculate the correct open mode for the file. | 23 | switch (mode) { |
| 23 | if (True(mode & Mode::Read) && True(mode & Mode::Write)) { | 24 | case Mode::Read: |
| 24 | if (True(mode & Mode::Append)) { | 25 | return FS::FileAccessMode::Read; |
| 25 | mode_str = "a+"; | 26 | case Mode::Write: |
| 26 | } else { | 27 | return FS::FileAccessMode::Write; |
| 27 | mode_str = "r+"; | 28 | case Mode::ReadWrite: |
| 28 | } | 29 | return FS::FileAccessMode::ReadWrite; |
| 29 | } else { | 30 | case Mode::Append: |
| 30 | if (True(mode & Mode::Read)) { | 31 | return FS::FileAccessMode::Append; |
| 31 | mode_str = "r"; | 32 | case Mode::ReadAppend: |
| 32 | } else if (True(mode & Mode::Append)) { | 33 | return FS::FileAccessMode::ReadAppend; |
| 33 | mode_str = "a"; | 34 | case Mode::WriteAppend: |
| 34 | } else if (True(mode & Mode::Write)) { | 35 | return FS::FileAccessMode::Append; |
| 35 | mode_str = "w"; | 36 | case Mode::All: |
| 36 | } else { | 37 | return FS::FileAccessMode::ReadAppend; |
| 37 | UNREACHABLE_MSG("Invalid file open mode: {:02X}", static_cast<u8>(mode)); | 38 | default: |
| 38 | } | 39 | return {}; |
| 39 | } | 40 | } |
| 40 | |||
| 41 | mode_str += "b"; | ||
| 42 | |||
| 43 | return mode_str; | ||
| 44 | } | 41 | } |
| 45 | 42 | ||
| 43 | } // Anonymous namespace | ||
| 44 | |||
| 46 | RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} | 45 | RealVfsFilesystem::RealVfsFilesystem() : VfsFilesystem(nullptr) {} |
| 47 | RealVfsFilesystem::~RealVfsFilesystem() = default; | 46 | RealVfsFilesystem::~RealVfsFilesystem() = default; |
| 48 | 47 | ||
| @@ -63,7 +62,7 @@ VfsEntryType RealVfsFilesystem::GetEntryType(std::string_view path_) const { | |||
| 63 | if (!FS::Exists(path)) { | 62 | if (!FS::Exists(path)) { |
| 64 | return VfsEntryType::None; | 63 | return VfsEntryType::None; |
| 65 | } | 64 | } |
| 66 | if (FS::IsDirectory(path)) { | 65 | if (FS::IsDir(path)) { |
| 67 | return VfsEntryType::Directory; | 66 | return VfsEntryType::Directory; |
| 68 | } | 67 | } |
| 69 | 68 | ||
| @@ -81,12 +80,13 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { | |||
| 81 | } | 80 | } |
| 82 | } | 81 | } |
| 83 | 82 | ||
| 84 | if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) { | 83 | auto backing = FS::FileOpen(path, ModeFlagsToFileAccessMode(perms), FS::FileType::BinaryFile); |
| 85 | FS::CreateEmptyFile(path); | 84 | |
| 85 | if (!backing) { | ||
| 86 | return nullptr; | ||
| 86 | } | 87 | } |
| 87 | 88 | ||
| 88 | auto backing = std::make_shared<FS::IOFile>(path, ModeFlagsToString(perms).c_str()); | 89 | cache.insert_or_assign(path, std::move(backing)); |
| 89 | cache.insert_or_assign(path, backing); | ||
| 90 | 90 | ||
| 91 | // Cannot use make_shared as RealVfsFile constructor is private | 91 | // Cannot use make_shared as RealVfsFile constructor is private |
| 92 | return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); | 92 | return std::shared_ptr<RealVfsFile>(new RealVfsFile(*this, backing, path, perms)); |
| @@ -94,25 +94,29 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { | |||
| 94 | 94 | ||
| 95 | VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { | 95 | VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { |
| 96 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); | 96 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); |
| 97 | const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash); | 97 | // Current usages of CreateFile expect to delete the contents of an existing file. |
| 98 | if (!FS::Exists(path)) { | 98 | if (FS::IsFile(path)) { |
| 99 | FS::CreateFullPath(path_fwd); | 99 | FS::IOFile temp{path, FS::FileAccessMode::Write, FS::FileType::BinaryFile}; |
| 100 | if (!FS::CreateEmptyFile(path)) { | 100 | |
| 101 | if (!temp.IsOpen()) { | ||
| 101 | return nullptr; | 102 | return nullptr; |
| 102 | } | 103 | } |
| 104 | |||
| 105 | temp.Close(); | ||
| 106 | |||
| 107 | return OpenFile(path, perms); | ||
| 108 | } | ||
| 109 | |||
| 110 | if (!FS::NewFile(path)) { | ||
| 111 | return nullptr; | ||
| 103 | } | 112 | } |
| 113 | |||
| 104 | return OpenFile(path, perms); | 114 | return OpenFile(path, perms); |
| 105 | } | 115 | } |
| 106 | 116 | ||
| 107 | VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { | 117 | VirtualFile RealVfsFilesystem::CopyFile(std::string_view old_path_, std::string_view new_path_) { |
| 108 | const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); | 118 | // Unused |
| 109 | const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); | 119 | return nullptr; |
| 110 | |||
| 111 | if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) || | ||
| 112 | !FS::Copy(old_path, new_path)) { | ||
| 113 | return nullptr; | ||
| 114 | } | ||
| 115 | return OpenFile(new_path, Mode::ReadWrite); | ||
| 116 | } | 120 | } |
| 117 | 121 | ||
| 118 | VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { | 122 | VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_view new_path_) { |
| @@ -127,13 +131,13 @@ VirtualFile RealVfsFilesystem::MoveFile(std::string_view old_path_, std::string_ | |||
| 127 | file->Close(); | 131 | file->Close(); |
| 128 | } | 132 | } |
| 129 | 133 | ||
| 130 | if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) || | 134 | if (!FS::RenameFile(old_path, new_path)) { |
| 131 | !FS::Rename(old_path, new_path)) { | ||
| 132 | return nullptr; | 135 | return nullptr; |
| 133 | } | 136 | } |
| 134 | 137 | ||
| 135 | cache.erase(old_path); | 138 | cache.erase(old_path); |
| 136 | if (file->Open(new_path, "r+b")) { | 139 | file->Open(new_path, FS::FileAccessMode::Read, FS::FileType::BinaryFile); |
| 140 | if (file->IsOpen()) { | ||
| 137 | cache.insert_or_assign(new_path, std::move(file)); | 141 | cache.insert_or_assign(new_path, std::move(file)); |
| 138 | } else { | 142 | } else { |
| 139 | LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path); | 143 | LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", new_path); |
| @@ -157,7 +161,7 @@ bool RealVfsFilesystem::DeleteFile(std::string_view path_) { | |||
| 157 | cache.erase(path); | 161 | cache.erase(path); |
| 158 | } | 162 | } |
| 159 | 163 | ||
| 160 | return FS::Delete(path); | 164 | return FS::RemoveFile(path); |
| 161 | } | 165 | } |
| 162 | 166 | ||
| 163 | VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { | 167 | VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) { |
| @@ -168,12 +172,8 @@ VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) | |||
| 168 | 172 | ||
| 169 | VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { | 173 | VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { |
| 170 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); | 174 | const auto path = FS::SanitizePath(path_, FS::DirectorySeparator::PlatformDefault); |
| 171 | const auto path_fwd = FS::SanitizePath(path, FS::DirectorySeparator::ForwardSlash); | 175 | if (!FS::CreateDirs(path)) { |
| 172 | if (!FS::Exists(path)) { | 176 | return nullptr; |
| 173 | FS::CreateFullPath(path_fwd); | ||
| 174 | if (!FS::CreateDir(path)) { | ||
| 175 | return nullptr; | ||
| 176 | } | ||
| 177 | } | 177 | } |
| 178 | // Cannot use make_shared as RealVfsDirectory constructor is private | 178 | // Cannot use make_shared as RealVfsDirectory constructor is private |
| 179 | return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); | 179 | return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); |
| @@ -181,13 +181,8 @@ VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms | |||
| 181 | 181 | ||
| 182 | VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, | 182 | VirtualDir RealVfsFilesystem::CopyDirectory(std::string_view old_path_, |
| 183 | std::string_view new_path_) { | 183 | std::string_view new_path_) { |
| 184 | const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); | 184 | // Unused |
| 185 | const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); | 185 | return nullptr; |
| 186 | if (!FS::Exists(old_path) || FS::Exists(new_path) || !FS::IsDirectory(old_path)) { | ||
| 187 | return nullptr; | ||
| 188 | } | ||
| 189 | FS::CopyDir(old_path, new_path); | ||
| 190 | return OpenDirectory(new_path, Mode::ReadWrite); | ||
| 191 | } | 186 | } |
| 192 | 187 | ||
| 193 | VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, | 188 | VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, |
| @@ -195,8 +190,7 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, | |||
| 195 | const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); | 190 | const auto old_path = FS::SanitizePath(old_path_, FS::DirectorySeparator::PlatformDefault); |
| 196 | const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); | 191 | const auto new_path = FS::SanitizePath(new_path_, FS::DirectorySeparator::PlatformDefault); |
| 197 | 192 | ||
| 198 | if (!FS::Exists(old_path) || FS::Exists(new_path) || FS::IsDirectory(old_path) || | 193 | if (!FS::RenameDir(old_path, new_path)) { |
| 199 | !FS::Rename(old_path, new_path)) { | ||
| 200 | return nullptr; | 194 | return nullptr; |
| 201 | } | 195 | } |
| 202 | 196 | ||
| @@ -208,7 +202,7 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, | |||
| 208 | 202 | ||
| 209 | const auto file_old_path = | 203 | const auto file_old_path = |
| 210 | FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault); | 204 | FS::SanitizePath(kv.first, FS::DirectorySeparator::PlatformDefault); |
| 211 | auto file_new_path = FS::SanitizePath(new_path + DIR_SEP + kv.first.substr(old_path.size()), | 205 | auto file_new_path = FS::SanitizePath(new_path + '/' + kv.first.substr(old_path.size()), |
| 212 | FS::DirectorySeparator::PlatformDefault); | 206 | FS::DirectorySeparator::PlatformDefault); |
| 213 | const auto& cached = cache[file_old_path]; | 207 | const auto& cached = cache[file_old_path]; |
| 214 | 208 | ||
| @@ -218,7 +212,8 @@ VirtualDir RealVfsFilesystem::MoveDirectory(std::string_view old_path_, | |||
| 218 | 212 | ||
| 219 | auto file = cached.lock(); | 213 | auto file = cached.lock(); |
| 220 | cache.erase(file_old_path); | 214 | cache.erase(file_old_path); |
| 221 | if (file->Open(file_new_path, "r+b")) { | 215 | file->Open(file_new_path, FS::FileAccessMode::Read, FS::FileType::BinaryFile); |
| 216 | if (file->IsOpen()) { | ||
| 222 | cache.insert_or_assign(std::move(file_new_path), std::move(file)); | 217 | cache.insert_or_assign(std::move(file_new_path), std::move(file)); |
| 223 | } else { | 218 | } else { |
| 224 | LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path); | 219 | LOG_ERROR(Service_FS, "Failed to open path {} in order to re-cache it", file_new_path); |
| @@ -245,15 +240,13 @@ bool RealVfsFilesystem::DeleteDirectory(std::string_view path_) { | |||
| 245 | cache.erase(kv.first); | 240 | cache.erase(kv.first); |
| 246 | } | 241 | } |
| 247 | 242 | ||
| 248 | return FS::DeleteDirRecursively(path); | 243 | return FS::RemoveDirRecursively(path); |
| 249 | } | 244 | } |
| 250 | 245 | ||
| 251 | RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_, | 246 | RealVfsFile::RealVfsFile(RealVfsFilesystem& base_, std::shared_ptr<FS::IOFile> backing_, |
| 252 | const std::string& path_, Mode perms_) | 247 | const std::string& path_, Mode perms_) |
| 253 | : base(base_), backing(std::move(backing_)), path(path_), parent_path(FS::GetParentPath(path_)), | 248 | : base(base_), backing(std::move(backing_)), path(path_), parent_path(FS::GetParentPath(path_)), |
| 254 | path_components(FS::SplitPathComponents(path_)), | 249 | path_components(FS::SplitPathComponents(path_)), perms(perms_) {} |
| 255 | parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)), | ||
| 256 | perms(perms_) {} | ||
| 257 | 250 | ||
| 258 | RealVfsFile::~RealVfsFile() = default; | 251 | RealVfsFile::~RealVfsFile() = default; |
| 259 | 252 | ||
| @@ -266,7 +259,7 @@ std::size_t RealVfsFile::GetSize() const { | |||
| 266 | } | 259 | } |
| 267 | 260 | ||
| 268 | bool RealVfsFile::Resize(std::size_t new_size) { | 261 | bool RealVfsFile::Resize(std::size_t new_size) { |
| 269 | return backing->Resize(new_size); | 262 | return backing->SetSize(new_size); |
| 270 | } | 263 | } |
| 271 | 264 | ||
| 272 | VirtualDir RealVfsFile::GetContainingDirectory() const { | 265 | VirtualDir RealVfsFile::GetContainingDirectory() const { |
| @@ -274,33 +267,33 @@ VirtualDir RealVfsFile::GetContainingDirectory() const { | |||
| 274 | } | 267 | } |
| 275 | 268 | ||
| 276 | bool RealVfsFile::IsWritable() const { | 269 | bool RealVfsFile::IsWritable() const { |
| 277 | return True(perms & Mode::WriteAppend); | 270 | return True(perms & Mode::Write); |
| 278 | } | 271 | } |
| 279 | 272 | ||
| 280 | bool RealVfsFile::IsReadable() const { | 273 | bool RealVfsFile::IsReadable() const { |
| 281 | return True(perms & Mode::ReadWrite); | 274 | return True(perms & Mode::Read); |
| 282 | } | 275 | } |
| 283 | 276 | ||
| 284 | std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { | 277 | std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const { |
| 285 | if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) { | 278 | if (!backing->Seek(static_cast<s64>(offset))) { |
| 286 | return 0; | 279 | return 0; |
| 287 | } | 280 | } |
| 288 | return backing->ReadBytes(data, length); | 281 | return backing->ReadSpan(std::span{data, length}); |
| 289 | } | 282 | } |
| 290 | 283 | ||
| 291 | std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { | 284 | std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) { |
| 292 | if (!backing->Seek(static_cast<s64>(offset), SEEK_SET)) { | 285 | if (!backing->Seek(static_cast<s64>(offset))) { |
| 293 | return 0; | 286 | return 0; |
| 294 | } | 287 | } |
| 295 | return backing->WriteBytes(data, length); | 288 | return backing->WriteSpan(std::span{data, length}); |
| 296 | } | 289 | } |
| 297 | 290 | ||
| 298 | bool RealVfsFile::Rename(std::string_view name) { | 291 | bool RealVfsFile::Rename(std::string_view name) { |
| 299 | return base.MoveFile(path, parent_path + DIR_SEP + std::string(name)) != nullptr; | 292 | return base.MoveFile(path, parent_path + '/' + std::string(name)) != nullptr; |
| 300 | } | 293 | } |
| 301 | 294 | ||
| 302 | bool RealVfsFile::Close() { | 295 | void RealVfsFile::Close() { |
| 303 | return backing->Close(); | 296 | backing->Close(); |
| 304 | } | 297 | } |
| 305 | 298 | ||
| 306 | // TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if | 299 | // TODO(DarkLordZach): MSVC would not let me combine the following two functions using 'if |
| @@ -313,15 +306,16 @@ std::vector<VirtualFile> RealVfsDirectory::IterateEntries<RealVfsFile, VfsFile>( | |||
| 313 | } | 306 | } |
| 314 | 307 | ||
| 315 | std::vector<VirtualFile> out; | 308 | std::vector<VirtualFile> out; |
| 316 | FS::ForeachDirectoryEntry( | 309 | |
| 317 | nullptr, path, | 310 | const FS::DirEntryCallable callback = [this, &out](const std::filesystem::path& full_path) { |
| 318 | [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { | 311 | const auto full_path_string = FS::PathToUTF8String(full_path); |
| 319 | const std::string full_path = directory + DIR_SEP + filename; | 312 | |
| 320 | if (!FS::IsDirectory(full_path)) { | 313 | out.emplace_back(base.OpenFile(full_path_string, perms)); |
| 321 | out.emplace_back(base.OpenFile(full_path, perms)); | 314 | |
| 322 | } | 315 | return true; |
| 323 | return true; | 316 | }; |
| 324 | }); | 317 | |
| 318 | FS::IterateDirEntries(path, callback, FS::DirEntryFilter::File); | ||
| 325 | 319 | ||
| 326 | return out; | 320 | return out; |
| 327 | } | 321 | } |
| @@ -333,42 +327,41 @@ std::vector<VirtualDir> RealVfsDirectory::IterateEntries<RealVfsDirectory, VfsDi | |||
| 333 | } | 327 | } |
| 334 | 328 | ||
| 335 | std::vector<VirtualDir> out; | 329 | std::vector<VirtualDir> out; |
| 336 | FS::ForeachDirectoryEntry( | 330 | |
| 337 | nullptr, path, | 331 | const FS::DirEntryCallable callback = [this, &out](const std::filesystem::path& full_path) { |
| 338 | [&out, this](u64* entries_out, const std::string& directory, const std::string& filename) { | 332 | const auto full_path_string = FS::PathToUTF8String(full_path); |
| 339 | const std::string full_path = directory + DIR_SEP + filename; | 333 | |
| 340 | if (FS::IsDirectory(full_path)) { | 334 | out.emplace_back(base.OpenDirectory(full_path_string, perms)); |
| 341 | out.emplace_back(base.OpenDirectory(full_path, perms)); | 335 | |
| 342 | } | 336 | return true; |
| 343 | return true; | 337 | }; |
| 344 | }); | 338 | |
| 339 | FS::IterateDirEntries(path, callback, FS::DirEntryFilter::Directory); | ||
| 345 | 340 | ||
| 346 | return out; | 341 | return out; |
| 347 | } | 342 | } |
| 348 | 343 | ||
| 349 | RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) | 344 | RealVfsDirectory::RealVfsDirectory(RealVfsFilesystem& base_, const std::string& path_, Mode perms_) |
| 350 | : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)), | 345 | : base(base_), path(FS::RemoveTrailingSlash(path_)), parent_path(FS::GetParentPath(path)), |
| 351 | path_components(FS::SplitPathComponents(path)), | 346 | path_components(FS::SplitPathComponents(path)), perms(perms_) { |
| 352 | parent_components(FS::SliceVector(path_components, 0, path_components.size() - 1)), | 347 | if (!FS::Exists(path) && True(perms & Mode::Write)) { |
| 353 | perms(perms_) { | 348 | void(FS::CreateDirs(path)); |
| 354 | if (!FS::Exists(path) && True(perms & Mode::WriteAppend)) { | ||
| 355 | FS::CreateDir(path); | ||
| 356 | } | 349 | } |
| 357 | } | 350 | } |
| 358 | 351 | ||
| 359 | RealVfsDirectory::~RealVfsDirectory() = default; | 352 | RealVfsDirectory::~RealVfsDirectory() = default; |
| 360 | 353 | ||
| 361 | VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const { | 354 | VirtualFile RealVfsDirectory::GetFileRelative(std::string_view relative_path) const { |
| 362 | const auto full_path = FS::SanitizePath(path + DIR_SEP + std::string(relative_path)); | 355 | const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); |
| 363 | if (!FS::Exists(full_path) || FS::IsDirectory(full_path)) { | 356 | if (!FS::Exists(full_path) || FS::IsDir(full_path)) { |
| 364 | return nullptr; | 357 | return nullptr; |
| 365 | } | 358 | } |
| 366 | return base.OpenFile(full_path, perms); | 359 | return base.OpenFile(full_path, perms); |
| 367 | } | 360 | } |
| 368 | 361 | ||
| 369 | VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const { | 362 | VirtualDir RealVfsDirectory::GetDirectoryRelative(std::string_view relative_path) const { |
| 370 | const auto full_path = FS::SanitizePath(path + DIR_SEP + std::string(relative_path)); | 363 | const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); |
| 371 | if (!FS::Exists(full_path) || !FS::IsDirectory(full_path)) { | 364 | if (!FS::Exists(full_path) || !FS::IsDir(full_path)) { |
| 372 | return nullptr; | 365 | return nullptr; |
| 373 | } | 366 | } |
| 374 | return base.OpenDirectory(full_path, perms); | 367 | return base.OpenDirectory(full_path, perms); |
| @@ -383,17 +376,20 @@ VirtualDir RealVfsDirectory::GetSubdirectory(std::string_view name) const { | |||
| 383 | } | 376 | } |
| 384 | 377 | ||
| 385 | VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) { | 378 | VirtualFile RealVfsDirectory::CreateFileRelative(std::string_view relative_path) { |
| 386 | const auto full_path = FS::SanitizePath(path + DIR_SEP + std::string(relative_path)); | 379 | const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); |
| 380 | if (!FS::CreateParentDirs(full_path)) { | ||
| 381 | return nullptr; | ||
| 382 | } | ||
| 387 | return base.CreateFile(full_path, perms); | 383 | return base.CreateFile(full_path, perms); |
| 388 | } | 384 | } |
| 389 | 385 | ||
| 390 | VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_path) { | 386 | VirtualDir RealVfsDirectory::CreateDirectoryRelative(std::string_view relative_path) { |
| 391 | const auto full_path = FS::SanitizePath(path + DIR_SEP + std::string(relative_path)); | 387 | const auto full_path = FS::SanitizePath(path + '/' + std::string(relative_path)); |
| 392 | return base.CreateDirectory(full_path, perms); | 388 | return base.CreateDirectory(full_path, perms); |
| 393 | } | 389 | } |
| 394 | 390 | ||
| 395 | bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { | 391 | bool RealVfsDirectory::DeleteSubdirectoryRecursive(std::string_view name) { |
| 396 | const auto full_path = FS::SanitizePath(this->path + DIR_SEP + std::string(name)); | 392 | const auto full_path = FS::SanitizePath(this->path + '/' + std::string(name)); |
| 397 | return base.DeleteDirectory(full_path); | 393 | return base.DeleteDirectory(full_path); |
| 398 | } | 394 | } |
| 399 | 395 | ||
| @@ -406,11 +402,11 @@ std::vector<VirtualDir> RealVfsDirectory::GetSubdirectories() const { | |||
| 406 | } | 402 | } |
| 407 | 403 | ||
| 408 | bool RealVfsDirectory::IsWritable() const { | 404 | bool RealVfsDirectory::IsWritable() const { |
| 409 | return True(perms & Mode::WriteAppend); | 405 | return True(perms & Mode::Write); |
| 410 | } | 406 | } |
| 411 | 407 | ||
| 412 | bool RealVfsDirectory::IsReadable() const { | 408 | bool RealVfsDirectory::IsReadable() const { |
| 413 | return True(perms & Mode::ReadWrite); | 409 | return True(perms & Mode::Read); |
| 414 | } | 410 | } |
| 415 | 411 | ||
| 416 | std::string RealVfsDirectory::GetName() const { | 412 | std::string RealVfsDirectory::GetName() const { |
| @@ -426,27 +422,27 @@ VirtualDir RealVfsDirectory::GetParentDirectory() const { | |||
| 426 | } | 422 | } |
| 427 | 423 | ||
| 428 | VirtualDir RealVfsDirectory::CreateSubdirectory(std::string_view name) { | 424 | VirtualDir RealVfsDirectory::CreateSubdirectory(std::string_view name) { |
| 429 | const std::string subdir_path = (path + DIR_SEP).append(name); | 425 | const std::string subdir_path = (path + '/').append(name); |
| 430 | return base.CreateDirectory(subdir_path, perms); | 426 | return base.CreateDirectory(subdir_path, perms); |
| 431 | } | 427 | } |
| 432 | 428 | ||
| 433 | VirtualFile RealVfsDirectory::CreateFile(std::string_view name) { | 429 | VirtualFile RealVfsDirectory::CreateFile(std::string_view name) { |
| 434 | const std::string file_path = (path + DIR_SEP).append(name); | 430 | const std::string file_path = (path + '/').append(name); |
| 435 | return base.CreateFile(file_path, perms); | 431 | return base.CreateFile(file_path, perms); |
| 436 | } | 432 | } |
| 437 | 433 | ||
| 438 | bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) { | 434 | bool RealVfsDirectory::DeleteSubdirectory(std::string_view name) { |
| 439 | const std::string subdir_path = (path + DIR_SEP).append(name); | 435 | const std::string subdir_path = (path + '/').append(name); |
| 440 | return base.DeleteDirectory(subdir_path); | 436 | return base.DeleteDirectory(subdir_path); |
| 441 | } | 437 | } |
| 442 | 438 | ||
| 443 | bool RealVfsDirectory::DeleteFile(std::string_view name) { | 439 | bool RealVfsDirectory::DeleteFile(std::string_view name) { |
| 444 | const std::string file_path = (path + DIR_SEP).append(name); | 440 | const std::string file_path = (path + '/').append(name); |
| 445 | return base.DeleteFile(file_path); | 441 | return base.DeleteFile(file_path); |
| 446 | } | 442 | } |
| 447 | 443 | ||
| 448 | bool RealVfsDirectory::Rename(std::string_view name) { | 444 | bool RealVfsDirectory::Rename(std::string_view name) { |
| 449 | const std::string new_name = (parent_path + DIR_SEP).append(name); | 445 | const std::string new_name = (parent_path + '/').append(name); |
| 450 | return base.MoveFile(path, new_name) != nullptr; | 446 | return base.MoveFile(path, new_name) != nullptr; |
| 451 | } | 447 | } |
| 452 | 448 | ||
| @@ -462,14 +458,17 @@ std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() | |||
| 462 | } | 458 | } |
| 463 | 459 | ||
| 464 | std::map<std::string, VfsEntryType, std::less<>> out; | 460 | std::map<std::string, VfsEntryType, std::less<>> out; |
| 465 | FS::ForeachDirectoryEntry( | 461 | |
| 466 | nullptr, path, | 462 | const FS::DirEntryCallable callback = [&out](const std::filesystem::path& full_path) { |
| 467 | [&out](u64* entries_out, const std::string& directory, const std::string& filename) { | 463 | const auto filename = FS::PathToUTF8String(full_path.filename()); |
| 468 | const std::string full_path = directory + DIR_SEP + filename; | 464 | |
| 469 | out.emplace(filename, | 465 | out.insert_or_assign(filename, |
| 470 | FS::IsDirectory(full_path) ? VfsEntryType::Directory : VfsEntryType::File); | 466 | FS::IsDir(full_path) ? VfsEntryType::Directory : VfsEntryType::File); |
| 471 | return true; | 467 | |
| 472 | }); | 468 | return true; |
| 469 | }; | ||
| 470 | |||
| 471 | FS::IterateDirEntries(path, callback); | ||
| 473 | 472 | ||
| 474 | return out; | 473 | return out; |
| 475 | } | 474 | } |
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h index 0666f2679..e4d1bba79 100644 --- a/src/core/file_sys/vfs_real.h +++ b/src/core/file_sys/vfs_real.h | |||
| @@ -61,14 +61,13 @@ private: | |||
| 61 | RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing, | 61 | RealVfsFile(RealVfsFilesystem& base, std::shared_ptr<Common::FS::IOFile> backing, |
| 62 | const std::string& path, Mode perms = Mode::Read); | 62 | const std::string& path, Mode perms = Mode::Read); |
| 63 | 63 | ||
| 64 | bool Close(); | 64 | void Close(); |
| 65 | 65 | ||
| 66 | RealVfsFilesystem& base; | 66 | RealVfsFilesystem& base; |
| 67 | std::shared_ptr<Common::FS::IOFile> backing; | 67 | std::shared_ptr<Common::FS::IOFile> backing; |
| 68 | std::string path; | 68 | std::string path; |
| 69 | std::string parent_path; | 69 | std::string parent_path; |
| 70 | std::vector<std::string> path_components; | 70 | std::vector<std::string> path_components; |
| 71 | std::vector<std::string> parent_components; | ||
| 72 | Mode perms; | 71 | Mode perms; |
| 73 | }; | 72 | }; |
| 74 | 73 | ||
| @@ -110,7 +109,6 @@ private: | |||
| 110 | std::string path; | 109 | std::string path; |
| 111 | std::string parent_path; | 110 | std::string parent_path; |
| 112 | std::vector<std::string> path_components; | 111 | std::vector<std::string> path_components; |
| 113 | std::vector<std::string> parent_components; | ||
| 114 | Mode perms; | 112 | Mode perms; |
| 115 | }; | 113 | }; |
| 116 | 114 | ||
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp index 814fd5680..d6fe1af47 100644 --- a/src/core/file_sys/xts_archive.cpp +++ b/src/core/file_sys/xts_archive.cpp | |||
| @@ -11,7 +11,7 @@ | |||
| 11 | #include <mbedtls/md.h> | 11 | #include <mbedtls/md.h> |
| 12 | #include <mbedtls/sha256.h> | 12 | #include <mbedtls/sha256.h> |
| 13 | 13 | ||
| 14 | #include "common/file_util.h" | 14 | #include "common/fs/path_util.h" |
| 15 | #include "common/hex_util.h" | 15 | #include "common/hex_util.h" |
| 16 | #include "common/string_util.h" | 16 | #include "common/string_util.h" |
| 17 | #include "core/crypto/aes_util.h" | 17 | #include "core/crypto/aes_util.h" |
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp index 49c09a570..39cd1efc1 100644 --- a/src/core/hle/service/acc/acc.cpp +++ b/src/core/hle/service/acc/acc.cpp | |||
| @@ -4,9 +4,9 @@ | |||
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <array> | 6 | #include <array> |
| 7 | #include "common/common_paths.h" | ||
| 8 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 9 | #include "common/file_util.h" | 8 | #include "common/fs/file.h" |
| 9 | #include "common/fs/path_util.h" | ||
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "common/string_util.h" | 11 | #include "common/string_util.h" |
| 12 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| @@ -41,9 +41,9 @@ constexpr ResultCode ERR_FAILED_SAVE_DATA{ErrorModule::Account, 100}; | |||
| 41 | // Thumbnails are hard coded to be at least this size | 41 | // Thumbnails are hard coded to be at least this size |
| 42 | constexpr std::size_t THUMBNAIL_SIZE = 0x24000; | 42 | constexpr std::size_t THUMBNAIL_SIZE = 0x24000; |
| 43 | 43 | ||
| 44 | static std::string GetImagePath(Common::UUID uuid) { | 44 | static std::filesystem::path GetImagePath(Common::UUID uuid) { |
| 45 | return Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + | 45 | return Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / |
| 46 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | 46 | fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch()); |
| 47 | } | 47 | } |
| 48 | 48 | ||
| 49 | static constexpr u32 SanitizeJPEGSize(std::size_t size) { | 49 | static constexpr u32 SanitizeJPEGSize(std::size_t size) { |
| @@ -328,7 +328,8 @@ protected: | |||
| 328 | IPC::ResponseBuilder rb{ctx, 3}; | 328 | IPC::ResponseBuilder rb{ctx, 3}; |
| 329 | rb.Push(RESULT_SUCCESS); | 329 | rb.Push(RESULT_SUCCESS); |
| 330 | 330 | ||
| 331 | const Common::FS::IOFile image(GetImagePath(user_id), "rb"); | 331 | const Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Read, |
| 332 | Common::FS::FileType::BinaryFile); | ||
| 332 | if (!image.IsOpen()) { | 333 | if (!image.IsOpen()) { |
| 333 | LOG_WARNING(Service_ACC, | 334 | LOG_WARNING(Service_ACC, |
| 334 | "Failed to load user provided image! Falling back to built-in backup..."); | 335 | "Failed to load user provided image! Falling back to built-in backup..."); |
| @@ -339,7 +340,10 @@ protected: | |||
| 339 | 340 | ||
| 340 | const u32 size = SanitizeJPEGSize(image.GetSize()); | 341 | const u32 size = SanitizeJPEGSize(image.GetSize()); |
| 341 | std::vector<u8> buffer(size); | 342 | std::vector<u8> buffer(size); |
| 342 | image.ReadBytes(buffer.data(), buffer.size()); | 343 | |
| 344 | if (image.Read(buffer) != buffer.size()) { | ||
| 345 | LOG_ERROR(Service_ACC, "Failed to read all the bytes in the user provided image."); | ||
| 346 | } | ||
| 343 | 347 | ||
| 344 | ctx.WriteBuffer(buffer); | 348 | ctx.WriteBuffer(buffer); |
| 345 | rb.Push<u32>(size); | 349 | rb.Push<u32>(size); |
| @@ -350,7 +354,8 @@ protected: | |||
| 350 | IPC::ResponseBuilder rb{ctx, 3}; | 354 | IPC::ResponseBuilder rb{ctx, 3}; |
| 351 | rb.Push(RESULT_SUCCESS); | 355 | rb.Push(RESULT_SUCCESS); |
| 352 | 356 | ||
| 353 | const Common::FS::IOFile image(GetImagePath(user_id), "rb"); | 357 | const Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Read, |
| 358 | Common::FS::FileType::BinaryFile); | ||
| 354 | 359 | ||
| 355 | if (!image.IsOpen()) { | 360 | if (!image.IsOpen()) { |
| 356 | LOG_WARNING(Service_ACC, | 361 | LOG_WARNING(Service_ACC, |
| @@ -415,10 +420,11 @@ protected: | |||
| 415 | ProfileData data; | 420 | ProfileData data; |
| 416 | std::memcpy(&data, user_data.data(), sizeof(ProfileData)); | 421 | std::memcpy(&data, user_data.data(), sizeof(ProfileData)); |
| 417 | 422 | ||
| 418 | Common::FS::IOFile image(GetImagePath(user_id), "wb"); | 423 | Common::FS::IOFile image(GetImagePath(user_id), Common::FS::FileAccessMode::Write, |
| 424 | Common::FS::FileType::BinaryFile); | ||
| 419 | 425 | ||
| 420 | if (!image.IsOpen() || !image.Resize(image_data.size()) || | 426 | if (!image.IsOpen() || !image.SetSize(image_data.size()) || |
| 421 | image.WriteBytes(image_data.data(), image_data.size()) != image_data.size() || | 427 | image.Write(image_data) != image_data.size() || |
| 422 | !profile_manager.SetProfileBaseAndData(user_id, base, data)) { | 428 | !profile_manager.SetProfileBaseAndData(user_id, base, data)) { |
| 423 | LOG_ERROR(Service_ACC, "Failed to update profile data, base, and image!"); | 429 | LOG_ERROR(Service_ACC, "Failed to update profile data, base, and image!"); |
| 424 | IPC::ResponseBuilder rb{ctx, 2}; | 430 | IPC::ResponseBuilder rb{ctx, 2}; |
diff --git a/src/core/hle/service/acc/profile_manager.cpp b/src/core/hle/service/acc/profile_manager.cpp index de83d82a4..77510489c 100644 --- a/src/core/hle/service/acc/profile_manager.cpp +++ b/src/core/hle/service/acc/profile_manager.cpp | |||
| @@ -7,7 +7,9 @@ | |||
| 7 | 7 | ||
| 8 | #include <fmt/format.h> | 8 | #include <fmt/format.h> |
| 9 | 9 | ||
| 10 | #include "common/file_util.h" | 10 | #include "common/fs/file.h" |
| 11 | #include "common/fs/fs.h" | ||
| 12 | #include "common/fs/path_util.h" | ||
| 11 | #include "common/settings.h" | 13 | #include "common/settings.h" |
| 12 | #include "core/hle/service/acc/profile_manager.h" | 14 | #include "core/hle/service/acc/profile_manager.h" |
| 13 | 15 | ||
| @@ -36,7 +38,7 @@ constexpr ResultCode ERROR_TOO_MANY_USERS(ErrorModule::Account, u32(-1)); | |||
| 36 | constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, u32(-2)); | 38 | constexpr ResultCode ERROR_USER_ALREADY_EXISTS(ErrorModule::Account, u32(-2)); |
| 37 | constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); | 39 | constexpr ResultCode ERROR_ARGUMENT_IS_NULL(ErrorModule::Account, 20); |
| 38 | 40 | ||
| 39 | constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "/system/save/8000000000000010/su/avators/"; | 41 | constexpr char ACC_SAVE_AVATORS_BASE_PATH[] = "system/save/8000000000000010/su/avators"; |
| 40 | 42 | ||
| 41 | ProfileManager::ProfileManager() { | 43 | ProfileManager::ProfileManager() { |
| 42 | ParseUserSaveFile(); | 44 | ParseUserSaveFile(); |
| @@ -325,8 +327,9 @@ bool ProfileManager::SetProfileBaseAndData(Common::UUID uuid, const ProfileBase& | |||
| 325 | } | 327 | } |
| 326 | 328 | ||
| 327 | void ProfileManager::ParseUserSaveFile() { | 329 | void ProfileManager::ParseUserSaveFile() { |
| 328 | const FS::IOFile save( | 330 | const auto save_path(FS::GetYuzuPath(FS::YuzuPath::NANDDir) / ACC_SAVE_AVATORS_BASE_PATH / |
| 329 | FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat", "rb"); | 331 | "profiles.dat"); |
| 332 | const FS::IOFile save(save_path, FS::FileAccessMode::Read, FS::FileType::BinaryFile); | ||
| 330 | 333 | ||
| 331 | if (!save.IsOpen()) { | 334 | if (!save.IsOpen()) { |
| 332 | LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " | 335 | LOG_WARNING(Service_ACC, "Failed to load profile data from save data... Generating new " |
| @@ -335,7 +338,7 @@ void ProfileManager::ParseUserSaveFile() { | |||
| 335 | } | 338 | } |
| 336 | 339 | ||
| 337 | ProfileDataRaw data; | 340 | ProfileDataRaw data; |
| 338 | if (save.ReadBytes(&data, sizeof(ProfileDataRaw)) != sizeof(ProfileDataRaw)) { | 341 | if (!save.ReadObject(data)) { |
| 339 | LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user " | 342 | LOG_WARNING(Service_ACC, "profiles.dat is smaller than expected... Generating new user " |
| 340 | "'yuzu' with random UUID."); | 343 | "'yuzu' with random UUID."); |
| 341 | return; | 344 | return; |
| @@ -372,31 +375,27 @@ void ProfileManager::WriteUserSaveFile() { | |||
| 372 | }; | 375 | }; |
| 373 | } | 376 | } |
| 374 | 377 | ||
| 375 | const auto raw_path = FS::GetUserPath(FS::UserPath::NANDDir) + "/system/save/8000000000000010"; | 378 | const auto raw_path(FS::GetYuzuPath(FS::YuzuPath::NANDDir) / "system/save/8000000000000010"); |
| 376 | if (FS::Exists(raw_path) && !FS::IsDirectory(raw_path)) { | 379 | if (FS::IsFile(raw_path) && !FS::RemoveFile(raw_path)) { |
| 377 | FS::Delete(raw_path); | 380 | return; |
| 378 | } | 381 | } |
| 379 | 382 | ||
| 380 | const auto path = | 383 | const auto save_path(FS::GetYuzuPath(FS::YuzuPath::NANDDir) / ACC_SAVE_AVATORS_BASE_PATH / |
| 381 | FS::GetUserPath(FS::UserPath::NANDDir) + ACC_SAVE_AVATORS_BASE_PATH + "profiles.dat"; | 384 | "profiles.dat"); |
| 382 | 385 | ||
| 383 | if (!FS::CreateFullPath(path)) { | 386 | if (!FS::CreateParentDirs(save_path)) { |
| 384 | LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory " | 387 | LOG_WARNING(Service_ACC, "Failed to create full path of profiles.dat. Create the directory " |
| 385 | "nand/system/save/8000000000000010/su/avators to mitigate this " | 388 | "nand/system/save/8000000000000010/su/avators to mitigate this " |
| 386 | "issue."); | 389 | "issue."); |
| 387 | return; | 390 | return; |
| 388 | } | 391 | } |
| 389 | 392 | ||
| 390 | FS::IOFile save(path, "wb"); | 393 | FS::IOFile save(save_path, FS::FileAccessMode::Write, FS::FileType::BinaryFile); |
| 391 | 394 | ||
| 392 | if (!save.IsOpen()) { | 395 | if (!save.IsOpen() || !save.SetSize(sizeof(ProfileDataRaw)) || !save.WriteObject(raw)) { |
| 393 | LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " | 396 | LOG_WARNING(Service_ACC, "Failed to write save data to file... No changes to user data " |
| 394 | "made in current session will be saved."); | 397 | "made in current session will be saved."); |
| 395 | return; | ||
| 396 | } | 398 | } |
| 397 | |||
| 398 | save.Resize(sizeof(ProfileDataRaw)); | ||
| 399 | save.WriteBytes(&raw, sizeof(ProfileDataRaw)); | ||
| 400 | } | 399 | } |
| 401 | 400 | ||
| 402 | }; // namespace Service::Account | 401 | }; // namespace Service::Account |
diff --git a/src/core/hle/service/am/applets/web_browser.cpp b/src/core/hle/service/am/applets/web_browser.cpp index e5f4a4485..3b28e829b 100644 --- a/src/core/hle/service/am/applets/web_browser.cpp +++ b/src/core/hle/service/am/applets/web_browser.cpp | |||
| @@ -3,8 +3,9 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 6 | #include "common/common_paths.h" | 6 | #include "common/fs/file.h" |
| 7 | #include "common/file_util.h" | 7 | #include "common/fs/fs.h" |
| 8 | #include "common/fs/path_util.h" | ||
| 8 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 9 | #include "common/string_util.h" | 10 | #include "common/string_util.h" |
| 10 | #include "core/core.h" | 11 | #include "core/core.h" |
| @@ -135,14 +136,10 @@ void ExtractSharedFonts(Core::System& system) { | |||
| 135 | "FontNintendoExtended2.ttf", | 136 | "FontNintendoExtended2.ttf", |
| 136 | }; | 137 | }; |
| 137 | 138 | ||
| 138 | for (std::size_t i = 0; i < NS::SHARED_FONTS.size(); ++i) { | 139 | const auto fonts_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "fonts"; |
| 139 | const auto fonts_dir = Common::FS::SanitizePath( | ||
| 140 | fmt::format("{}/fonts", Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)), | ||
| 141 | Common::FS::DirectorySeparator::PlatformDefault); | ||
| 142 | 140 | ||
| 143 | const auto font_file_path = | 141 | for (std::size_t i = 0; i < NS::SHARED_FONTS.size(); ++i) { |
| 144 | Common::FS::SanitizePath(fmt::format("{}/{}", fonts_dir, DECRYPTED_SHARED_FONTS[i]), | 142 | const auto font_file_path = fonts_dir / DECRYPTED_SHARED_FONTS[i]; |
| 145 | Common::FS::DirectorySeparator::PlatformDefault); | ||
| 146 | 143 | ||
| 147 | if (Common::FS::Exists(font_file_path)) { | 144 | if (Common::FS::Exists(font_file_path)) { |
| 148 | continue; | 145 | continue; |
| @@ -197,8 +194,8 @@ void ExtractSharedFonts(Core::System& system) { | |||
| 197 | FileSys::VirtualFile decrypted_font = std::make_shared<FileSys::VectorVfsFile>( | 194 | FileSys::VirtualFile decrypted_font = std::make_shared<FileSys::VectorVfsFile>( |
| 198 | std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]); | 195 | std::move(decrypted_data), DECRYPTED_SHARED_FONTS[i]); |
| 199 | 196 | ||
| 200 | const auto temp_dir = | 197 | const auto temp_dir = system.GetFilesystem()->CreateDirectory( |
| 201 | system.GetFilesystem()->CreateDirectory(fonts_dir, FileSys::Mode::ReadWrite); | 198 | Common::FS::PathToUTF8String(fonts_dir), FileSys::Mode::ReadWrite); |
| 202 | 199 | ||
| 203 | const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]); | 200 | const auto out_file = temp_dir->CreateFile(DECRYPTED_SHARED_FONTS[i]); |
| 204 | 201 | ||
| @@ -312,13 +309,14 @@ void WebBrowser::Execute() { | |||
| 312 | } | 309 | } |
| 313 | 310 | ||
| 314 | void WebBrowser::ExtractOfflineRomFS() { | 311 | void WebBrowser::ExtractOfflineRomFS() { |
| 315 | LOG_DEBUG(Service_AM, "Extracting RomFS to {}", offline_cache_dir); | 312 | LOG_DEBUG(Service_AM, "Extracting RomFS to {}", |
| 313 | Common::FS::PathToUTF8String(offline_cache_dir)); | ||
| 316 | 314 | ||
| 317 | const auto extracted_romfs_dir = | 315 | const auto extracted_romfs_dir = |
| 318 | FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); | 316 | FileSys::ExtractRomFS(offline_romfs, FileSys::RomFSExtractionType::SingleDiscard); |
| 319 | 317 | ||
| 320 | const auto temp_dir = | 318 | const auto temp_dir = system.GetFilesystem()->CreateDirectory( |
| 321 | system.GetFilesystem()->CreateDirectory(offline_cache_dir, FileSys::Mode::ReadWrite); | 319 | Common::FS::PathToUTF8String(offline_cache_dir), FileSys::Mode::ReadWrite); |
| 322 | 320 | ||
| 323 | FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir); | 321 | FileSys::VfsRawCopyD(extracted_romfs_dir, temp_dir); |
| 324 | } | 322 | } |
| @@ -397,15 +395,12 @@ void WebBrowser::InitializeOffline() { | |||
| 397 | "system_data", | 395 | "system_data", |
| 398 | }; | 396 | }; |
| 399 | 397 | ||
| 400 | offline_cache_dir = Common::FS::SanitizePath( | 398 | offline_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / |
| 401 | fmt::format("{}/offline_web_applet_{}/{:016X}", | 399 | fmt::format("offline_web_applet_{}/{:016X}", |
| 402 | Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), | 400 | RESOURCE_TYPES[static_cast<u32>(document_kind) - 1], title_id); |
| 403 | RESOURCE_TYPES[static_cast<u32>(document_kind) - 1], title_id), | ||
| 404 | Common::FS::DirectorySeparator::PlatformDefault); | ||
| 405 | 401 | ||
| 406 | offline_document = Common::FS::SanitizePath( | 402 | offline_document = Common::FS::ConcatPathSafe( |
| 407 | fmt::format("{}/{}/{}", offline_cache_dir, additional_paths, document_path), | 403 | offline_cache_dir, fmt::format("{}/{}", additional_paths, document_path)); |
| 408 | Common::FS::DirectorySeparator::PlatformDefault); | ||
| 409 | } | 404 | } |
| 410 | 405 | ||
| 411 | void WebBrowser::InitializeShare() {} | 406 | void WebBrowser::InitializeShare() {} |
| @@ -429,8 +424,7 @@ void WebBrowser::ExecuteLogin() { | |||
| 429 | } | 424 | } |
| 430 | 425 | ||
| 431 | void WebBrowser::ExecuteOffline() { | 426 | void WebBrowser::ExecuteOffline() { |
| 432 | const auto main_url = Common::FS::SanitizePath(GetMainURL(offline_document), | 427 | const auto main_url = GetMainURL(Common::FS::PathToUTF8String(offline_document)); |
| 433 | Common::FS::DirectorySeparator::PlatformDefault); | ||
| 434 | 428 | ||
| 435 | if (!Common::FS::Exists(main_url)) { | 429 | if (!Common::FS::Exists(main_url)) { |
| 436 | offline_romfs = GetOfflineRomFS(system, title_id, nca_type); | 430 | offline_romfs = GetOfflineRomFS(system, title_id, nca_type); |
| @@ -444,10 +438,11 @@ void WebBrowser::ExecuteOffline() { | |||
| 444 | } | 438 | } |
| 445 | } | 439 | } |
| 446 | 440 | ||
| 447 | LOG_INFO(Service_AM, "Opening offline document at {}", offline_document); | 441 | LOG_INFO(Service_AM, "Opening offline document at {}", |
| 442 | Common::FS::PathToUTF8String(offline_document)); | ||
| 448 | 443 | ||
| 449 | frontend.OpenLocalWebPage( | 444 | frontend.OpenLocalWebPage( |
| 450 | offline_document, [this] { ExtractOfflineRomFS(); }, | 445 | Common::FS::PathToUTF8String(offline_document), [this] { ExtractOfflineRomFS(); }, |
| 451 | [this](WebExitReason exit_reason, std::string last_url) { | 446 | [this](WebExitReason exit_reason, std::string last_url) { |
| 452 | WebBrowserExit(exit_reason, last_url); | 447 | WebBrowserExit(exit_reason, last_url); |
| 453 | }); | 448 | }); |
diff --git a/src/core/hle/service/am/applets/web_browser.h b/src/core/hle/service/am/applets/web_browser.h index 1e1812f36..cdeaf2c40 100644 --- a/src/core/hle/service/am/applets/web_browser.h +++ b/src/core/hle/service/am/applets/web_browser.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <filesystem> | ||
| 7 | #include <optional> | 8 | #include <optional> |
| 8 | 9 | ||
| 9 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| @@ -75,8 +76,8 @@ private: | |||
| 75 | 76 | ||
| 76 | u64 title_id{}; | 77 | u64 title_id{}; |
| 77 | FileSys::ContentRecordType nca_type{}; | 78 | FileSys::ContentRecordType nca_type{}; |
| 78 | std::string offline_cache_dir; | 79 | std::filesystem::path offline_cache_dir; |
| 79 | std::string offline_document; | 80 | std::filesystem::path offline_document; |
| 80 | FileSys::VirtualFile offline_romfs; | 81 | FileSys::VirtualFile offline_romfs; |
| 81 | 82 | ||
| 82 | std::string external_url; | 83 | std::string external_url; |
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp index d6d2f52e5..3cc397604 100644 --- a/src/core/hle/service/bcat/backend/boxcat.cpp +++ b/src/core/hle/service/bcat/backend/boxcat.cpp | |||
| @@ -15,6 +15,9 @@ | |||
| 15 | #pragma GCC diagnostic pop | 15 | #pragma GCC diagnostic pop |
| 16 | #endif | 16 | #endif |
| 17 | 17 | ||
| 18 | #include "common/fs/file.h" | ||
| 19 | #include "common/fs/fs.h" | ||
| 20 | #include "common/fs/path_util.h" | ||
| 18 | #include "common/hex_util.h" | 21 | #include "common/hex_util.h" |
| 19 | #include "common/logging/backend.h" | 22 | #include "common/logging/backend.h" |
| 20 | #include "common/logging/log.h" | 23 | #include "common/logging/log.h" |
| @@ -96,14 +99,14 @@ constexpr u32 PORT = 443; | |||
| 96 | constexpr u32 TIMEOUT_SECONDS = 30; | 99 | constexpr u32 TIMEOUT_SECONDS = 30; |
| 97 | [[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB | 100 | [[maybe_unused]] constexpr u64 VFS_COPY_BLOCK_SIZE = 1ULL << 24; // 4MB |
| 98 | 101 | ||
| 99 | std::string GetBINFilePath(u64 title_id) { | 102 | std::filesystem::path GetBINFilePath(u64 title_id) { |
| 100 | return fmt::format("{}bcat/{:016X}/launchparam.bin", | 103 | return Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "bcat" / |
| 101 | Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id); | 104 | fmt::format("{:016X}/launchparam.bin", title_id); |
| 102 | } | 105 | } |
| 103 | 106 | ||
| 104 | std::string GetZIPFilePath(u64 title_id) { | 107 | std::filesystem::path GetZIPFilePath(u64 title_id) { |
| 105 | return fmt::format("{}bcat/{:016X}/data.zip", | 108 | return Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "bcat" / |
| 106 | Common::FS::GetUserPath(Common::FS::UserPath::CacheDir), title_id); | 109 | fmt::format("{:016X}/data.zip", title_id); |
| 107 | } | 110 | } |
| 108 | 111 | ||
| 109 | // If the error is something the user should know about (build ID mismatch, bad client version), | 112 | // If the error is something the user should know about (build ID mismatch, bad client version), |
| @@ -187,7 +190,7 @@ bool VfsRawCopyDProgress(FileSys::VirtualDir src, FileSys::VirtualDir dest, | |||
| 187 | 190 | ||
| 188 | class Boxcat::Client { | 191 | class Boxcat::Client { |
| 189 | public: | 192 | public: |
| 190 | Client(std::string path_, u64 title_id_, u64 build_id_) | 193 | Client(std::filesystem::path path_, u64 title_id_, u64 build_id_) |
| 191 | : path(std::move(path_)), title_id(title_id_), build_id(build_id_) {} | 194 | : path(std::move(path_)), title_id(title_id_), build_id(build_id_) {} |
| 192 | 195 | ||
| 193 | DownloadResult DownloadDataZip() { | 196 | DownloadResult DownloadDataZip() { |
| @@ -217,10 +220,11 @@ private: | |||
| 217 | }; | 220 | }; |
| 218 | 221 | ||
| 219 | if (Common::FS::Exists(path)) { | 222 | if (Common::FS::Exists(path)) { |
| 220 | Common::FS::IOFile file{path, "rb"}; | 223 | Common::FS::IOFile file{path, Common::FS::FileAccessMode::Read, |
| 224 | Common::FS::FileType::BinaryFile}; | ||
| 221 | if (file.IsOpen()) { | 225 | if (file.IsOpen()) { |
| 222 | std::vector<u8> bytes(file.GetSize()); | 226 | std::vector<u8> bytes(file.GetSize()); |
| 223 | file.ReadBytes(bytes.data(), bytes.size()); | 227 | void(file.Read(bytes)); |
| 224 | const auto digest = DigestFile(bytes); | 228 | const auto digest = DigestFile(bytes); |
| 225 | headers.insert({std::string("If-None-Match"), Common::HexToString(digest, false)}); | 229 | headers.insert({std::string("If-None-Match"), Common::HexToString(digest, false)}); |
| 226 | } | 230 | } |
| @@ -247,14 +251,23 @@ private: | |||
| 247 | return DownloadResult::InvalidContentType; | 251 | return DownloadResult::InvalidContentType; |
| 248 | } | 252 | } |
| 249 | 253 | ||
| 250 | Common::FS::CreateFullPath(path); | 254 | if (!Common::FS::CreateDirs(path)) { |
| 251 | Common::FS::IOFile file{path, "wb"}; | ||
| 252 | if (!file.IsOpen()) | ||
| 253 | return DownloadResult::GeneralFSError; | 255 | return DownloadResult::GeneralFSError; |
| 254 | if (!file.Resize(response->body.size())) | 256 | } |
| 257 | |||
| 258 | Common::FS::IOFile file{path, Common::FS::FileAccessMode::Append, | ||
| 259 | Common::FS::FileType::BinaryFile}; | ||
| 260 | if (!file.IsOpen()) { | ||
| 261 | return DownloadResult::GeneralFSError; | ||
| 262 | } | ||
| 263 | |||
| 264 | if (!file.SetSize(response->body.size())) { | ||
| 255 | return DownloadResult::GeneralFSError; | 265 | return DownloadResult::GeneralFSError; |
| 256 | if (file.WriteBytes(response->body.data(), response->body.size()) != response->body.size()) | 266 | } |
| 267 | |||
| 268 | if (file.Write(response->body) != response->body.size()) { | ||
| 257 | return DownloadResult::GeneralFSError; | 269 | return DownloadResult::GeneralFSError; |
| 270 | } | ||
| 258 | 271 | ||
| 259 | return DownloadResult::Success; | 272 | return DownloadResult::Success; |
| 260 | } | 273 | } |
| @@ -267,7 +280,7 @@ private: | |||
| 267 | } | 280 | } |
| 268 | 281 | ||
| 269 | std::unique_ptr<httplib::SSLClient> client; | 282 | std::unique_ptr<httplib::SSLClient> client; |
| 270 | std::string path; | 283 | std::filesystem::path path; |
| 271 | u64 title_id; | 284 | u64 title_id; |
| 272 | u64 build_id; | 285 | u64 build_id; |
| 273 | }; | 286 | }; |
| @@ -291,7 +304,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe | |||
| 291 | return; | 304 | return; |
| 292 | } | 305 | } |
| 293 | 306 | ||
| 294 | const auto zip_path{GetZIPFilePath(title.title_id)}; | 307 | const auto zip_path = GetZIPFilePath(title.title_id); |
| 295 | Boxcat::Client client{zip_path, title.title_id, title.build_id}; | 308 | Boxcat::Client client{zip_path, title.title_id, title.build_id}; |
| 296 | 309 | ||
| 297 | progress.StartConnecting(); | 310 | progress.StartConnecting(); |
| @@ -301,7 +314,7 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe | |||
| 301 | LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); | 314 | LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); |
| 302 | 315 | ||
| 303 | if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { | 316 | if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { |
| 304 | Common::FS::Delete(zip_path); | 317 | void(Common::FS::RemoveFile(zip_path)); |
| 305 | } | 318 | } |
| 306 | 319 | ||
| 307 | HandleDownloadDisplayResult(applet_manager, res); | 320 | HandleDownloadDisplayResult(applet_manager, res); |
| @@ -311,11 +324,13 @@ void SynchronizeInternal(AM::Applets::AppletManager& applet_manager, DirectoryGe | |||
| 311 | 324 | ||
| 312 | progress.StartProcessingDataList(); | 325 | progress.StartProcessingDataList(); |
| 313 | 326 | ||
| 314 | Common::FS::IOFile zip{zip_path, "rb"}; | 327 | Common::FS::IOFile zip{zip_path, Common::FS::FileAccessMode::Read, |
| 328 | Common::FS::FileType::BinaryFile}; | ||
| 315 | const auto size = zip.GetSize(); | 329 | const auto size = zip.GetSize(); |
| 316 | std::vector<u8> bytes(size); | 330 | std::vector<u8> bytes(size); |
| 317 | if (!zip.IsOpen() || size == 0 || zip.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { | 331 | if (!zip.IsOpen() || size == 0 || zip.Read(bytes) != bytes.size()) { |
| 318 | LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", zip_path); | 332 | LOG_ERROR(Service_BCAT, "Boxcat failed to read ZIP file at path '{}'!", |
| 333 | Common::FS::PathToUTF8String(zip_path)); | ||
| 319 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); | 334 | progress.FinishDownload(ERROR_GENERAL_BCAT_FAILURE); |
| 320 | return; | 335 | return; |
| 321 | } | 336 | } |
| @@ -419,19 +434,19 @@ void Boxcat::SetPassphrase(u64 title_id, const Passphrase& passphrase) { | |||
| 419 | } | 434 | } |
| 420 | 435 | ||
| 421 | std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) { | 436 | std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) { |
| 422 | const auto path{GetBINFilePath(title.title_id)}; | 437 | const auto bin_file_path = GetBINFilePath(title.title_id); |
| 423 | 438 | ||
| 424 | if (Settings::values.bcat_boxcat_local) { | 439 | if (Settings::values.bcat_boxcat_local) { |
| 425 | LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download."); | 440 | LOG_INFO(Service_BCAT, "Boxcat using local data by override, skipping download."); |
| 426 | } else { | 441 | } else { |
| 427 | Client launch_client{path, title.title_id, title.build_id}; | 442 | Client launch_client{bin_file_path, title.title_id, title.build_id}; |
| 428 | 443 | ||
| 429 | const auto res = launch_client.DownloadLaunchParam(); | 444 | const auto res = launch_client.DownloadLaunchParam(); |
| 430 | if (res != DownloadResult::Success) { | 445 | if (res != DownloadResult::Success) { |
| 431 | LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); | 446 | LOG_ERROR(Service_BCAT, "Boxcat synchronization failed with error '{}'!", res); |
| 432 | 447 | ||
| 433 | if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { | 448 | if (res == DownloadResult::NoMatchBuildId || res == DownloadResult::NoMatchTitleId) { |
| 434 | Common::FS::Delete(path); | 449 | void(Common::FS::RemoveFile(bin_file_path)); |
| 435 | } | 450 | } |
| 436 | 451 | ||
| 437 | HandleDownloadDisplayResult(applet_manager, res); | 452 | HandleDownloadDisplayResult(applet_manager, res); |
| @@ -439,12 +454,13 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title) | |||
| 439 | } | 454 | } |
| 440 | } | 455 | } |
| 441 | 456 | ||
| 442 | Common::FS::IOFile bin{path, "rb"}; | 457 | Common::FS::IOFile bin{bin_file_path, Common::FS::FileAccessMode::Read, |
| 458 | Common::FS::FileType::BinaryFile}; | ||
| 443 | const auto size = bin.GetSize(); | 459 | const auto size = bin.GetSize(); |
| 444 | std::vector<u8> bytes(size); | 460 | std::vector<u8> bytes(size); |
| 445 | if (!bin.IsOpen() || size == 0 || bin.ReadBytes(bytes.data(), bytes.size()) != bytes.size()) { | 461 | if (!bin.IsOpen() || size == 0 || bin.Read(bytes) != bytes.size()) { |
| 446 | LOG_ERROR(Service_BCAT, "Boxcat failed to read launch parameter binary at path '{}'!", | 462 | LOG_ERROR(Service_BCAT, "Boxcat failed to read launch parameter binary at path '{}'!", |
| 447 | path); | 463 | Common::FS::PathToUTF8String(bin_file_path)); |
| 448 | return std::nullopt; | 464 | return std::nullopt; |
| 449 | } | 465 | } |
| 450 | 466 | ||
diff --git a/src/core/hle/service/fatal/fatal.cpp b/src/core/hle/service/fatal/fatal.cpp index 432abde76..b7666e95a 100644 --- a/src/core/hle/service/fatal/fatal.cpp +++ b/src/core/hle/service/fatal/fatal.cpp | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include <ctime> | 7 | #include <ctime> |
| 8 | #include <fmt/chrono.h> | 8 | #include <fmt/chrono.h> |
| 9 | #include "common/file_util.h" | ||
| 10 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 11 | #include "common/scm_rev.h" | 10 | #include "common/scm_rev.h" |
| 12 | #include "common/swap.h" | 11 | #include "common/swap.h" |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 67baaee9b..78664439d 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -5,7 +5,7 @@ | |||
| 5 | #include <utility> | 5 | #include <utility> |
| 6 | 6 | ||
| 7 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 8 | #include "common/file_util.h" | 8 | #include "common/fs/path_util.h" |
| 9 | #include "common/settings.h" | 9 | #include "common/settings.h" |
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/file_sys/bis_factory.h" | 11 | #include "core/file_sys/bis_factory.h" |
| @@ -728,14 +728,17 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove | |||
| 728 | sdmc_factory = nullptr; | 728 | sdmc_factory = nullptr; |
| 729 | } | 729 | } |
| 730 | 730 | ||
| 731 | auto nand_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir), | 731 | using YuzuPath = Common::FS::YuzuPath; |
| 732 | FileSys::Mode::ReadWrite); | 732 | const auto rw_mode = FileSys::Mode::ReadWrite; |
| 733 | auto sd_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir), | 733 | |
| 734 | FileSys::Mode::ReadWrite); | 734 | auto nand_directory = |
| 735 | auto load_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir), | 735 | vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::NANDDir), rw_mode); |
| 736 | FileSys::Mode::ReadWrite); | 736 | auto sd_directory = |
| 737 | auto dump_directory = vfs.OpenDirectory(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir), | 737 | vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::SDMCDir), rw_mode); |
| 738 | FileSys::Mode::ReadWrite); | 738 | auto load_directory = |
| 739 | vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::LoadDir), FileSys::Mode::Read); | ||
| 740 | auto dump_directory = | ||
| 741 | vfs.OpenDirectory(Common::FS::GetYuzuPathString(YuzuPath::DumpDir), rw_mode); | ||
| 739 | 742 | ||
| 740 | if (bis_factory == nullptr) { | 743 | if (bis_factory == nullptr) { |
| 741 | bis_factory = | 744 | bis_factory = |
diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/manager.cpp index 70350a2a3..114aff31c 100644 --- a/src/core/hle/service/mii/manager.cpp +++ b/src/core/hle/service/mii/manager.cpp | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | #include <random> | 6 | #include <random> |
| 7 | 7 | ||
| 8 | #include "common/assert.h" | 8 | #include "common/assert.h" |
| 9 | #include "common/file_util.h" | ||
| 10 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 11 | #include "common/string_util.h" | 10 | #include "common/string_util.h" |
| 12 | 11 | ||
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp index e14acce58..90ba5c752 100644 --- a/src/core/hle/service/ns/pl_u.cpp +++ b/src/core/hle/service/ns/pl_u.cpp | |||
| @@ -7,9 +7,7 @@ | |||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | 8 | ||
| 9 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 10 | #include "common/common_paths.h" | ||
| 11 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 12 | #include "common/file_util.h" | ||
| 13 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 14 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 15 | #include "core/core.h" | 13 | #include "core/core.h" |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 022885c1b..a19bb220a 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | #include <cinttypes> | 5 | #include <cinttypes> |
| 6 | #include <cstring> | 6 | #include <cstring> |
| 7 | #include "common/common_funcs.h" | 7 | #include "common/common_funcs.h" |
| 8 | #include "common/file_util.h" | ||
| 9 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 10 | #include "core/core.h" | 9 | #include "core/core.h" |
| 11 | #include "core/file_sys/content_archive.h" | 10 | #include "core/file_sys/content_archive.h" |
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index c062a4259..3d9276f15 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include "common/common_funcs.h" | 8 | #include "common/common_funcs.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/file_util.h" | ||
| 11 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 12 | #include "core/hle/kernel/code_set.h" | 11 | #include "core/hle/kernel/code_set.h" |
| 13 | #include "core/hle/kernel/k_page_table.h" | 12 | #include "core/hle/kernel/k_page_table.h" |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index d4808fb5b..228dc6389 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -7,7 +7,7 @@ | |||
| 7 | #include <ostream> | 7 | #include <ostream> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include "common/concepts.h" | 9 | #include "common/concepts.h" |
| 10 | #include "common/file_util.h" | 10 | #include "common/fs/path_util.h" |
| 11 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 12 | #include "common/string_util.h" | 12 | #include "common/string_util.h" |
| 13 | #include "core/core.h" | 13 | #include "core/core.h" |
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 418cbf61b..aa51b0daa 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp | |||
| @@ -4,7 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #include <utility> | 5 | #include <utility> |
| 6 | 6 | ||
| 7 | #include "common/file_util.h" | ||
| 8 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | 8 | #include "core/core.h" |
| 10 | #include "core/file_sys/content_archive.h" | 9 | #include "core/file_sys/content_archive.h" |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index ef54fa574..618555202 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | 7 | ||
| 8 | #include "common/common_funcs.h" | 8 | #include "common/common_funcs.h" |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "common/file_util.h" | ||
| 11 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 12 | #include "common/settings.h" | 11 | #include "common/settings.h" |
| 13 | #include "common/swap.h" | 12 | #include "common/swap.h" |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index df59412cf..0f5cfda68 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | 8 | ||
| 9 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 10 | #include "common/file_util.h" | ||
| 11 | #include "common/hex_util.h" | 10 | #include "common/hex_util.h" |
| 12 | #include "common/logging/log.h" | 11 | #include "common/logging/log.h" |
| 13 | #include "common/lz4_compression.h" | 12 | #include "common/lz4_compression.h" |
diff --git a/src/core/perf_stats.cpp b/src/core/perf_stats.cpp index c42c437b7..6635a1339 100644 --- a/src/core/perf_stats.cpp +++ b/src/core/perf_stats.cpp | |||
| @@ -11,7 +11,9 @@ | |||
| 11 | #include <thread> | 11 | #include <thread> |
| 12 | #include <fmt/chrono.h> | 12 | #include <fmt/chrono.h> |
| 13 | #include <fmt/format.h> | 13 | #include <fmt/format.h> |
| 14 | #include "common/file_util.h" | 14 | #include "common/fs/file.h" |
| 15 | #include "common/fs/fs.h" | ||
| 16 | #include "common/fs/path_util.h" | ||
| 15 | #include "common/math_util.h" | 17 | #include "common/math_util.h" |
| 16 | #include "common/settings.h" | 18 | #include "common/settings.h" |
| 17 | #include "core/perf_stats.h" | 19 | #include "core/perf_stats.h" |
| @@ -38,12 +40,17 @@ PerfStats::~PerfStats() { | |||
| 38 | std::ostringstream stream; | 40 | std::ostringstream stream; |
| 39 | std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index, | 41 | std::copy(perf_history.begin() + IgnoreFrames, perf_history.begin() + current_index, |
| 40 | std::ostream_iterator<double>(stream, "\n")); | 42 | std::ostream_iterator<double>(stream, "\n")); |
| 41 | const std::string& path = Common::FS::GetUserPath(Common::FS::UserPath::LogDir); | 43 | |
| 44 | const auto path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::LogDir); | ||
| 42 | // %F Date format expanded is "%Y-%m-%d" | 45 | // %F Date format expanded is "%Y-%m-%d" |
| 43 | const std::string filename = | 46 | const auto filename = fmt::format("{:%F-%H-%M}_{:016X}.csv", *std::localtime(&t), title_id); |
| 44 | fmt::format("{}/{:%F-%H-%M}_{:016X}.csv", path, *std::localtime(&t), title_id); | 47 | const auto filepath = path / filename; |
| 45 | Common::FS::IOFile file(filename, "w"); | 48 | |
| 46 | file.WriteString(stream.str()); | 49 | if (Common::FS::CreateParentDir(filepath)) { |
| 50 | Common::FS::IOFile file(filepath, Common::FS::FileAccessMode::Write, | ||
| 51 | Common::FS::FileType::TextFile); | ||
| 52 | void(file.WriteString(stream.str())); | ||
| 53 | } | ||
| 47 | } | 54 | } |
| 48 | 55 | ||
| 49 | void PerfStats::BeginSystemFrame() { | 56 | void PerfStats::BeginSystemFrame() { |
diff --git a/src/core/reporter.cpp b/src/core/reporter.cpp index d1e807dd4..a9596fe4d 100644 --- a/src/core/reporter.cpp +++ b/src/core/reporter.cpp | |||
| @@ -11,7 +11,9 @@ | |||
| 11 | #include <fmt/ostream.h> | 11 | #include <fmt/ostream.h> |
| 12 | #include <nlohmann/json.hpp> | 12 | #include <nlohmann/json.hpp> |
| 13 | 13 | ||
| 14 | #include "common/file_util.h" | 14 | #include "common/fs/file.h" |
| 15 | #include "common/fs/fs.h" | ||
| 16 | #include "common/fs/path_util.h" | ||
| 15 | #include "common/hex_util.h" | 17 | #include "common/hex_util.h" |
| 16 | #include "common/scm_rev.h" | 18 | #include "common/scm_rev.h" |
| 17 | #include "common/settings.h" | 19 | #include "common/settings.h" |
| @@ -26,10 +28,9 @@ | |||
| 26 | 28 | ||
| 27 | namespace { | 29 | namespace { |
| 28 | 30 | ||
| 29 | std::string GetPath(std::string_view type, u64 title_id, std::string_view timestamp) { | 31 | std::filesystem::path GetPath(std::string_view type, u64 title_id, std::string_view timestamp) { |
| 30 | return fmt::format("{}{}/{:016X}_{}.json", | 32 | return Common::FS::GetYuzuPath(Common::FS::YuzuPath::LogDir) / type / |
| 31 | Common::FS::GetUserPath(Common::FS::UserPath::LogDir), type, title_id, | 33 | fmt::format("{:016X}_{}.json", title_id, timestamp); |
| 32 | timestamp); | ||
| 33 | } | 34 | } |
| 34 | 35 | ||
| 35 | std::string GetTimestamp() { | 36 | std::string GetTimestamp() { |
| @@ -39,14 +40,16 @@ std::string GetTimestamp() { | |||
| 39 | 40 | ||
| 40 | using namespace nlohmann; | 41 | using namespace nlohmann; |
| 41 | 42 | ||
| 42 | void SaveToFile(json json, const std::string& filename) { | 43 | void SaveToFile(json json, const std::filesystem::path& filename) { |
| 43 | if (!Common::FS::CreateFullPath(filename)) { | 44 | if (!Common::FS::CreateParentDirs(filename)) { |
| 44 | LOG_ERROR(Core, "Failed to create path for '{}' to save report!", filename); | 45 | LOG_ERROR(Core, "Failed to create path for '{}' to save report!", |
| 46 | Common::FS::PathToUTF8String(filename)); | ||
| 45 | return; | 47 | return; |
| 46 | } | 48 | } |
| 47 | 49 | ||
| 48 | std::ofstream file( | 50 | std::ofstream file; |
| 49 | Common::FS::SanitizePath(filename, Common::FS::DirectorySeparator::PlatformDefault)); | 51 | Common::FS::OpenFileStream(file, filename, std::ios_base::out | std::ios_base::trunc); |
| 52 | |||
| 50 | file << std::setw(4) << json << std::endl; | 53 | file << std::setw(4) << json << std::endl; |
| 51 | } | 54 | } |
| 52 | 55 | ||
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp index 6dcff5400..ad1a9ffb4 100644 --- a/src/core/telemetry_session.cpp +++ b/src/core/telemetry_session.cpp | |||
| @@ -9,7 +9,9 @@ | |||
| 9 | 9 | ||
| 10 | #include "common/assert.h" | 10 | #include "common/assert.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/file_util.h" | 12 | #include "common/fs/file.h" |
| 13 | #include "common/fs/fs.h" | ||
| 14 | #include "common/fs/path_util.h" | ||
| 13 | #include "common/logging/log.h" | 15 | #include "common/logging/log.h" |
| 14 | 16 | ||
| 15 | #include "common/settings.h" | 17 | #include "common/settings.h" |
| @@ -72,31 +74,41 @@ static const char* TranslateGPUAccuracyLevel(Settings::GPUAccuracy backend) { | |||
| 72 | 74 | ||
| 73 | u64 GetTelemetryId() { | 75 | u64 GetTelemetryId() { |
| 74 | u64 telemetry_id{}; | 76 | u64 telemetry_id{}; |
| 75 | const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) + | 77 | const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id"; |
| 76 | "telemetry_id"}; | ||
| 77 | 78 | ||
| 78 | bool generate_new_id = !Common::FS::Exists(filename); | 79 | bool generate_new_id = !Common::FS::Exists(filename); |
| 80 | |||
| 79 | if (!generate_new_id) { | 81 | if (!generate_new_id) { |
| 80 | Common::FS::IOFile file(filename, "rb"); | 82 | Common::FS::IOFile file{filename, Common::FS::FileAccessMode::Read, |
| 83 | Common::FS::FileType::BinaryFile}; | ||
| 84 | |||
| 81 | if (!file.IsOpen()) { | 85 | if (!file.IsOpen()) { |
| 82 | LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); | 86 | LOG_ERROR(Core, "failed to open telemetry_id: {}", |
| 87 | Common::FS::PathToUTF8String(filename)); | ||
| 83 | return {}; | 88 | return {}; |
| 84 | } | 89 | } |
| 85 | file.ReadBytes(&telemetry_id, sizeof(u64)); | 90 | |
| 86 | if (telemetry_id == 0) { | 91 | if (!file.ReadObject(telemetry_id) || telemetry_id == 0) { |
| 87 | LOG_ERROR(Frontend, "telemetry_id is 0. Generating a new one.", telemetry_id); | 92 | LOG_ERROR(Frontend, "telemetry_id is 0. Generating a new one.", telemetry_id); |
| 88 | generate_new_id = true; | 93 | generate_new_id = true; |
| 89 | } | 94 | } |
| 90 | } | 95 | } |
| 91 | 96 | ||
| 92 | if (generate_new_id) { | 97 | if (generate_new_id) { |
| 93 | Common::FS::IOFile file(filename, "wb"); | 98 | Common::FS::IOFile file{filename, Common::FS::FileAccessMode::Write, |
| 99 | Common::FS::FileType::BinaryFile}; | ||
| 100 | |||
| 94 | if (!file.IsOpen()) { | 101 | if (!file.IsOpen()) { |
| 95 | LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); | 102 | LOG_ERROR(Core, "failed to open telemetry_id: {}", |
| 103 | Common::FS::PathToUTF8String(filename)); | ||
| 96 | return {}; | 104 | return {}; |
| 97 | } | 105 | } |
| 106 | |||
| 98 | telemetry_id = GenerateTelemetryId(); | 107 | telemetry_id = GenerateTelemetryId(); |
| 99 | file.WriteBytes(&telemetry_id, sizeof(u64)); | 108 | |
| 109 | if (!file.WriteObject(telemetry_id)) { | ||
| 110 | LOG_ERROR(Core, "Failed to write telemetry_id to file."); | ||
| 111 | } | ||
| 100 | } | 112 | } |
| 101 | 113 | ||
| 102 | return telemetry_id; | 114 | return telemetry_id; |
| @@ -104,15 +116,20 @@ u64 GetTelemetryId() { | |||
| 104 | 116 | ||
| 105 | u64 RegenerateTelemetryId() { | 117 | u64 RegenerateTelemetryId() { |
| 106 | const u64 new_telemetry_id{GenerateTelemetryId()}; | 118 | const u64 new_telemetry_id{GenerateTelemetryId()}; |
| 107 | const std::string filename{Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir) + | 119 | const auto filename = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / "telemetry_id"; |
| 108 | "telemetry_id"}; | 120 | |
| 121 | Common::FS::IOFile file{filename, Common::FS::FileAccessMode::Write, | ||
| 122 | Common::FS::FileType::BinaryFile}; | ||
| 109 | 123 | ||
| 110 | Common::FS::IOFile file(filename, "wb"); | ||
| 111 | if (!file.IsOpen()) { | 124 | if (!file.IsOpen()) { |
| 112 | LOG_ERROR(Core, "failed to open telemetry_id: {}", filename); | 125 | LOG_ERROR(Core, "failed to open telemetry_id: {}", Common::FS::PathToUTF8String(filename)); |
| 113 | return {}; | 126 | return {}; |
| 114 | } | 127 | } |
| 115 | file.WriteBytes(&new_telemetry_id, sizeof(u64)); | 128 | |
| 129 | if (!file.WriteObject(new_telemetry_id)) { | ||
| 130 | LOG_ERROR(Core, "Failed to write telemetry_id to file."); | ||
| 131 | } | ||
| 132 | |||
| 116 | return new_telemetry_id; | 133 | return new_telemetry_id; |
| 117 | } | 134 | } |
| 118 | 135 | ||
diff --git a/src/tests/core/core_timing.cpp b/src/tests/core/core_timing.cpp index b35459152..e0c66fa2e 100644 --- a/src/tests/core/core_timing.cpp +++ b/src/tests/core/core_timing.cpp | |||
| @@ -11,7 +11,6 @@ | |||
| 11 | #include <memory> | 11 | #include <memory> |
| 12 | #include <string> | 12 | #include <string> |
| 13 | 13 | ||
| 14 | #include "common/file_util.h" | ||
| 15 | #include "core/core.h" | 14 | #include "core/core.h" |
| 16 | #include "core/core_timing.h" | 15 | #include "core/core_timing.h" |
| 17 | 16 | ||
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp index dbcb751cb..0deb86517 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp | |||
| @@ -7,9 +7,10 @@ | |||
| 7 | #include <fmt/format.h> | 7 | #include <fmt/format.h> |
| 8 | 8 | ||
| 9 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| 10 | #include "common/common_paths.h" | ||
| 11 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 12 | #include "common/file_util.h" | 11 | #include "common/fs/file.h" |
| 12 | #include "common/fs/fs.h" | ||
| 13 | #include "common/fs/path_util.h" | ||
| 13 | #include "common/logging/log.h" | 14 | #include "common/logging/log.h" |
| 14 | #include "common/scm_rev.h" | 15 | #include "common/scm_rev.h" |
| 15 | #include "common/settings.h" | 16 | #include "common/settings.h" |
| @@ -26,11 +27,7 @@ using Tegra::Engines::ShaderType; | |||
| 26 | using VideoCommon::Shader::BindlessSamplerMap; | 27 | using VideoCommon::Shader::BindlessSamplerMap; |
| 27 | using VideoCommon::Shader::BoundSamplerMap; | 28 | using VideoCommon::Shader::BoundSamplerMap; |
| 28 | using VideoCommon::Shader::KeyMap; | 29 | using VideoCommon::Shader::KeyMap; |
| 29 | |||
| 30 | namespace { | ||
| 31 | |||
| 32 | using VideoCommon::Shader::SeparateSamplerKey; | 30 | using VideoCommon::Shader::SeparateSamplerKey; |
| 33 | |||
| 34 | using ShaderCacheVersionHash = std::array<u8, 64>; | 31 | using ShaderCacheVersionHash = std::array<u8, 64>; |
| 35 | 32 | ||
| 36 | struct ConstBufferKey { | 33 | struct ConstBufferKey { |
| @@ -58,6 +55,8 @@ struct BindlessSamplerEntry { | |||
| 58 | Tegra::Engines::SamplerDescriptor sampler; | 55 | Tegra::Engines::SamplerDescriptor sampler; |
| 59 | }; | 56 | }; |
| 60 | 57 | ||
| 58 | namespace { | ||
| 59 | |||
| 61 | constexpr u32 NativeVersion = 21; | 60 | constexpr u32 NativeVersion = 21; |
| 62 | 61 | ||
| 63 | ShaderCacheVersionHash GetShaderCacheVersionHash() { | 62 | ShaderCacheVersionHash GetShaderCacheVersionHash() { |
| @@ -74,22 +73,20 @@ ShaderDiskCacheEntry::ShaderDiskCacheEntry() = default; | |||
| 74 | ShaderDiskCacheEntry::~ShaderDiskCacheEntry() = default; | 73 | ShaderDiskCacheEntry::~ShaderDiskCacheEntry() = default; |
| 75 | 74 | ||
| 76 | bool ShaderDiskCacheEntry::Load(Common::FS::IOFile& file) { | 75 | bool ShaderDiskCacheEntry::Load(Common::FS::IOFile& file) { |
| 77 | if (file.ReadBytes(&type, sizeof(u32)) != sizeof(u32)) { | 76 | if (!file.ReadObject(type)) { |
| 78 | return false; | 77 | return false; |
| 79 | } | 78 | } |
| 80 | u32 code_size; | 79 | u32 code_size; |
| 81 | u32 code_size_b; | 80 | u32 code_size_b; |
| 82 | if (file.ReadBytes(&code_size, sizeof(u32)) != sizeof(u32) || | 81 | if (!file.ReadObject(code_size) || !file.ReadObject(code_size_b)) { |
| 83 | file.ReadBytes(&code_size_b, sizeof(u32)) != sizeof(u32)) { | ||
| 84 | return false; | 82 | return false; |
| 85 | } | 83 | } |
| 86 | code.resize(code_size); | 84 | code.resize(code_size); |
| 87 | code_b.resize(code_size_b); | 85 | code_b.resize(code_size_b); |
| 88 | 86 | if (file.Read(code) != code_size) { | |
| 89 | if (file.ReadArray(code.data(), code_size) != code_size) { | ||
| 90 | return false; | 87 | return false; |
| 91 | } | 88 | } |
| 92 | if (HasProgramA() && file.ReadArray(code_b.data(), code_size_b) != code_size_b) { | 89 | if (HasProgramA() && file.Read(code_b) != code_size_b) { |
| 93 | return false; | 90 | return false; |
| 94 | } | 91 | } |
| 95 | 92 | ||
| @@ -99,13 +96,12 @@ bool ShaderDiskCacheEntry::Load(Common::FS::IOFile& file) { | |||
| 99 | u32 num_bound_samplers; | 96 | u32 num_bound_samplers; |
| 100 | u32 num_separate_samplers; | 97 | u32 num_separate_samplers; |
| 101 | u32 num_bindless_samplers; | 98 | u32 num_bindless_samplers; |
| 102 | if (file.ReadArray(&unique_identifier, 1) != 1 || file.ReadArray(&bound_buffer, 1) != 1 || | 99 | if (!file.ReadObject(unique_identifier) || !file.ReadObject(bound_buffer) || |
| 103 | file.ReadArray(&is_texture_handler_size_known, 1) != 1 || | 100 | !file.ReadObject(is_texture_handler_size_known) || |
| 104 | file.ReadArray(&texture_handler_size_value, 1) != 1 || | 101 | !file.ReadObject(texture_handler_size_value) || !file.ReadObject(graphics_info) || |
| 105 | file.ReadArray(&graphics_info, 1) != 1 || file.ReadArray(&compute_info, 1) != 1 || | 102 | !file.ReadObject(compute_info) || !file.ReadObject(num_keys) || |
| 106 | file.ReadArray(&num_keys, 1) != 1 || file.ReadArray(&num_bound_samplers, 1) != 1 || | 103 | !file.ReadObject(num_bound_samplers) || !file.ReadObject(num_separate_samplers) || |
| 107 | file.ReadArray(&num_separate_samplers, 1) != 1 || | 104 | !file.ReadObject(num_bindless_samplers)) { |
| 108 | file.ReadArray(&num_bindless_samplers, 1) != 1) { | ||
| 109 | return false; | 105 | return false; |
| 110 | } | 106 | } |
| 111 | if (is_texture_handler_size_known) { | 107 | if (is_texture_handler_size_known) { |
| @@ -116,13 +112,10 @@ bool ShaderDiskCacheEntry::Load(Common::FS::IOFile& file) { | |||
| 116 | std::vector<BoundSamplerEntry> flat_bound_samplers(num_bound_samplers); | 112 | std::vector<BoundSamplerEntry> flat_bound_samplers(num_bound_samplers); |
| 117 | std::vector<SeparateSamplerEntry> flat_separate_samplers(num_separate_samplers); | 113 | std::vector<SeparateSamplerEntry> flat_separate_samplers(num_separate_samplers); |
| 118 | std::vector<BindlessSamplerEntry> flat_bindless_samplers(num_bindless_samplers); | 114 | std::vector<BindlessSamplerEntry> flat_bindless_samplers(num_bindless_samplers); |
| 119 | if (file.ReadArray(flat_keys.data(), flat_keys.size()) != flat_keys.size() || | 115 | if (file.Read(flat_keys) != flat_keys.size() || |
| 120 | file.ReadArray(flat_bound_samplers.data(), flat_bound_samplers.size()) != | 116 | file.Read(flat_bound_samplers) != flat_bound_samplers.size() || |
| 121 | flat_bound_samplers.size() || | 117 | file.Read(flat_separate_samplers) != flat_separate_samplers.size() || |
| 122 | file.ReadArray(flat_separate_samplers.data(), flat_separate_samplers.size()) != | 118 | file.Read(flat_bindless_samplers) != flat_bindless_samplers.size()) { |
| 123 | flat_separate_samplers.size() || | ||
| 124 | file.ReadArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) != | ||
| 125 | flat_bindless_samplers.size()) { | ||
| 126 | return false; | 119 | return false; |
| 127 | } | 120 | } |
| 128 | for (const auto& entry : flat_keys) { | 121 | for (const auto& entry : flat_keys) { |
| @@ -145,26 +138,25 @@ bool ShaderDiskCacheEntry::Load(Common::FS::IOFile& file) { | |||
| 145 | } | 138 | } |
| 146 | 139 | ||
| 147 | bool ShaderDiskCacheEntry::Save(Common::FS::IOFile& file) const { | 140 | bool ShaderDiskCacheEntry::Save(Common::FS::IOFile& file) const { |
| 148 | if (file.WriteObject(static_cast<u32>(type)) != 1 || | 141 | if (!file.WriteObject(static_cast<u32>(type)) || |
| 149 | file.WriteObject(static_cast<u32>(code.size())) != 1 || | 142 | !file.WriteObject(static_cast<u32>(code.size())) || |
| 150 | file.WriteObject(static_cast<u32>(code_b.size())) != 1) { | 143 | !file.WriteObject(static_cast<u32>(code_b.size()))) { |
| 151 | return false; | 144 | return false; |
| 152 | } | 145 | } |
| 153 | if (file.WriteArray(code.data(), code.size()) != code.size()) { | 146 | if (file.Write(code) != code.size()) { |
| 154 | return false; | 147 | return false; |
| 155 | } | 148 | } |
| 156 | if (HasProgramA() && file.WriteArray(code_b.data(), code_b.size()) != code_b.size()) { | 149 | if (HasProgramA() && file.Write(code_b) != code_b.size()) { |
| 157 | return false; | 150 | return false; |
| 158 | } | 151 | } |
| 159 | 152 | ||
| 160 | if (file.WriteObject(unique_identifier) != 1 || file.WriteObject(bound_buffer) != 1 || | 153 | if (!file.WriteObject(unique_identifier) || !file.WriteObject(bound_buffer) || |
| 161 | file.WriteObject(static_cast<u8>(texture_handler_size.has_value())) != 1 || | 154 | !file.WriteObject(static_cast<u8>(texture_handler_size.has_value())) || |
| 162 | file.WriteObject(texture_handler_size.value_or(0)) != 1 || | 155 | !file.WriteObject(texture_handler_size.value_or(0)) || !file.WriteObject(graphics_info) || |
| 163 | file.WriteObject(graphics_info) != 1 || file.WriteObject(compute_info) != 1 || | 156 | !file.WriteObject(compute_info) || !file.WriteObject(static_cast<u32>(keys.size())) || |
| 164 | file.WriteObject(static_cast<u32>(keys.size())) != 1 || | 157 | !file.WriteObject(static_cast<u32>(bound_samplers.size())) || |
| 165 | file.WriteObject(static_cast<u32>(bound_samplers.size())) != 1 || | 158 | !file.WriteObject(static_cast<u32>(separate_samplers.size())) || |
| 166 | file.WriteObject(static_cast<u32>(separate_samplers.size())) != 1 || | 159 | !file.WriteObject(static_cast<u32>(bindless_samplers.size()))) { |
| 167 | file.WriteObject(static_cast<u32>(bindless_samplers.size())) != 1) { | ||
| 168 | return false; | 160 | return false; |
| 169 | } | 161 | } |
| 170 | 162 | ||
| @@ -197,13 +189,10 @@ bool ShaderDiskCacheEntry::Save(Common::FS::IOFile& file) const { | |||
| 197 | BindlessSamplerEntry{address.first, address.second, sampler}); | 189 | BindlessSamplerEntry{address.first, address.second, sampler}); |
| 198 | } | 190 | } |
| 199 | 191 | ||
| 200 | return file.WriteArray(flat_keys.data(), flat_keys.size()) == flat_keys.size() && | 192 | return file.Write(flat_keys) == flat_keys.size() && |
| 201 | file.WriteArray(flat_bound_samplers.data(), flat_bound_samplers.size()) == | 193 | file.Write(flat_bound_samplers) == flat_bound_samplers.size() && |
| 202 | flat_bound_samplers.size() && | 194 | file.Write(flat_separate_samplers) == flat_separate_samplers.size() && |
| 203 | file.WriteArray(flat_separate_samplers.data(), flat_separate_samplers.size()) == | 195 | file.Write(flat_bindless_samplers) == flat_bindless_samplers.size(); |
| 204 | flat_separate_samplers.size() && | ||
| 205 | file.WriteArray(flat_bindless_samplers.data(), flat_bindless_samplers.size()) == | ||
| 206 | flat_bindless_samplers.size(); | ||
| 207 | } | 196 | } |
| 208 | 197 | ||
| 209 | ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL() = default; | 198 | ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL() = default; |
| @@ -221,7 +210,8 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran | |||
| 221 | return std::nullopt; | 210 | return std::nullopt; |
| 222 | } | 211 | } |
| 223 | 212 | ||
| 224 | Common::FS::IOFile file(GetTransferablePath(), "rb"); | 213 | Common::FS::IOFile file{GetTransferablePath(), Common::FS::FileAccessMode::Read, |
| 214 | Common::FS::FileType::BinaryFile}; | ||
| 225 | if (!file.IsOpen()) { | 215 | if (!file.IsOpen()) { |
| 226 | LOG_INFO(Render_OpenGL, "No transferable shader cache found"); | 216 | LOG_INFO(Render_OpenGL, "No transferable shader cache found"); |
| 227 | is_usable = true; | 217 | is_usable = true; |
| @@ -229,7 +219,7 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran | |||
| 229 | } | 219 | } |
| 230 | 220 | ||
| 231 | u32 version{}; | 221 | u32 version{}; |
| 232 | if (file.ReadBytes(&version, sizeof(version)) != sizeof(version)) { | 222 | if (!file.ReadObject(version)) { |
| 233 | LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it"); | 223 | LOG_ERROR(Render_OpenGL, "Failed to get transferable cache version, skipping it"); |
| 234 | return std::nullopt; | 224 | return std::nullopt; |
| 235 | } | 225 | } |
| @@ -249,7 +239,7 @@ std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTran | |||
| 249 | 239 | ||
| 250 | // Version is valid, load the shaders | 240 | // Version is valid, load the shaders |
| 251 | std::vector<ShaderDiskCacheEntry> entries; | 241 | std::vector<ShaderDiskCacheEntry> entries; |
| 252 | while (file.Tell() < file.GetSize()) { | 242 | while (static_cast<u64>(file.Tell()) < file.GetSize()) { |
| 253 | ShaderDiskCacheEntry& entry = entries.emplace_back(); | 243 | ShaderDiskCacheEntry& entry = entries.emplace_back(); |
| 254 | if (!entry.Load(file)) { | 244 | if (!entry.Load(file)) { |
| 255 | LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping"); | 245 | LOG_ERROR(Render_OpenGL, "Failed to load transferable raw entry, skipping"); |
| @@ -266,7 +256,8 @@ std::vector<ShaderDiskCachePrecompiled> ShaderDiskCacheOpenGL::LoadPrecompiled() | |||
| 266 | return {}; | 256 | return {}; |
| 267 | } | 257 | } |
| 268 | 258 | ||
| 269 | Common::FS::IOFile file(GetPrecompiledPath(), "rb"); | 259 | Common::FS::IOFile file{GetPrecompiledPath(), Common::FS::FileAccessMode::Read, |
| 260 | Common::FS::FileType::BinaryFile}; | ||
| 270 | if (!file.IsOpen()) { | 261 | if (!file.IsOpen()) { |
| 271 | LOG_INFO(Render_OpenGL, "No precompiled shader cache found"); | 262 | LOG_INFO(Render_OpenGL, "No precompiled shader cache found"); |
| 272 | return {}; | 263 | return {}; |
| @@ -286,7 +277,9 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo | |||
| 286 | Common::FS::IOFile& file) { | 277 | Common::FS::IOFile& file) { |
| 287 | // Read compressed file from disk and decompress to virtual precompiled cache file | 278 | // Read compressed file from disk and decompress to virtual precompiled cache file |
| 288 | std::vector<u8> compressed(file.GetSize()); | 279 | std::vector<u8> compressed(file.GetSize()); |
| 289 | file.ReadBytes(compressed.data(), compressed.size()); | 280 | if (file.Read(compressed) != file.GetSize()) { |
| 281 | return std::nullopt; | ||
| 282 | } | ||
| 290 | const std::vector<u8> decompressed = Common::Compression::DecompressDataZSTD(compressed); | 283 | const std::vector<u8> decompressed = Common::Compression::DecompressDataZSTD(compressed); |
| 291 | SaveArrayToPrecompiled(decompressed.data(), decompressed.size()); | 284 | SaveArrayToPrecompiled(decompressed.data(), decompressed.size()); |
| 292 | precompiled_cache_virtual_file_offset = 0; | 285 | precompiled_cache_virtual_file_offset = 0; |
| @@ -321,9 +314,9 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo | |||
| 321 | } | 314 | } |
| 322 | 315 | ||
| 323 | void ShaderDiskCacheOpenGL::InvalidateTransferable() { | 316 | void ShaderDiskCacheOpenGL::InvalidateTransferable() { |
| 324 | if (!Common::FS::Delete(GetTransferablePath())) { | 317 | if (!Common::FS::RemoveFile(GetTransferablePath())) { |
| 325 | LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", | 318 | LOG_ERROR(Render_OpenGL, "Failed to invalidate transferable file={}", |
| 326 | GetTransferablePath()); | 319 | Common::FS::PathToUTF8String(GetTransferablePath())); |
| 327 | } | 320 | } |
| 328 | InvalidatePrecompiled(); | 321 | InvalidatePrecompiled(); |
| 329 | } | 322 | } |
| @@ -332,8 +325,9 @@ void ShaderDiskCacheOpenGL::InvalidatePrecompiled() { | |||
| 332 | // Clear virtaul precompiled cache file | 325 | // Clear virtaul precompiled cache file |
| 333 | precompiled_cache_virtual_file.Resize(0); | 326 | precompiled_cache_virtual_file.Resize(0); |
| 334 | 327 | ||
| 335 | if (!Common::FS::Delete(GetPrecompiledPath())) { | 328 | if (!Common::FS::RemoveFile(GetPrecompiledPath())) { |
| 336 | LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", GetPrecompiledPath()); | 329 | LOG_ERROR(Render_OpenGL, "Failed to invalidate precompiled file={}", |
| 330 | Common::FS::PathToUTF8String(GetPrecompiledPath())); | ||
| 337 | } | 331 | } |
| 338 | } | 332 | } |
| 339 | 333 | ||
| @@ -398,16 +392,18 @@ Common::FS::IOFile ShaderDiskCacheOpenGL::AppendTransferableFile() const { | |||
| 398 | const auto transferable_path{GetTransferablePath()}; | 392 | const auto transferable_path{GetTransferablePath()}; |
| 399 | const bool existed = Common::FS::Exists(transferable_path); | 393 | const bool existed = Common::FS::Exists(transferable_path); |
| 400 | 394 | ||
| 401 | Common::FS::IOFile file(transferable_path, "ab"); | 395 | Common::FS::IOFile file{transferable_path, Common::FS::FileAccessMode::Append, |
| 396 | Common::FS::FileType::BinaryFile}; | ||
| 402 | if (!file.IsOpen()) { | 397 | if (!file.IsOpen()) { |
| 403 | LOG_ERROR(Render_OpenGL, "Failed to open transferable cache in path={}", transferable_path); | 398 | LOG_ERROR(Render_OpenGL, "Failed to open transferable cache in path={}", |
| 399 | Common::FS::PathToUTF8String(transferable_path)); | ||
| 404 | return {}; | 400 | return {}; |
| 405 | } | 401 | } |
| 406 | if (!existed || file.GetSize() == 0) { | 402 | if (!existed || file.GetSize() == 0) { |
| 407 | // If the file didn't exist, write its version | 403 | // If the file didn't exist, write its version |
| 408 | if (file.WriteObject(NativeVersion) != 1) { | 404 | if (!file.WriteObject(NativeVersion)) { |
| 409 | LOG_ERROR(Render_OpenGL, "Failed to write transferable cache version in path={}", | 405 | LOG_ERROR(Render_OpenGL, "Failed to write transferable cache version in path={}", |
| 410 | transferable_path); | 406 | Common::FS::PathToUTF8String(transferable_path)); |
| 411 | return {}; | 407 | return {}; |
| 412 | } | 408 | } |
| 413 | } | 409 | } |
| @@ -429,51 +425,54 @@ void ShaderDiskCacheOpenGL::SaveVirtualPrecompiledFile() { | |||
| 429 | const std::vector<u8> compressed = | 425 | const std::vector<u8> compressed = |
| 430 | Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size()); | 426 | Common::Compression::CompressDataZSTDDefault(uncompressed.data(), uncompressed.size()); |
| 431 | 427 | ||
| 432 | const auto precompiled_path{GetPrecompiledPath()}; | 428 | const auto precompiled_path = GetPrecompiledPath(); |
| 433 | Common::FS::IOFile file(precompiled_path, "wb"); | 429 | Common::FS::IOFile file{precompiled_path, Common::FS::FileAccessMode::Write, |
| 430 | Common::FS::FileType::BinaryFile}; | ||
| 434 | 431 | ||
| 435 | if (!file.IsOpen()) { | 432 | if (!file.IsOpen()) { |
| 436 | LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", precompiled_path); | 433 | LOG_ERROR(Render_OpenGL, "Failed to open precompiled cache in path={}", |
| 434 | Common::FS::PathToUTF8String(precompiled_path)); | ||
| 437 | return; | 435 | return; |
| 438 | } | 436 | } |
| 439 | if (file.WriteBytes(compressed.data(), compressed.size()) != compressed.size()) { | 437 | if (file.Write(compressed) != compressed.size()) { |
| 440 | LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version in path={}", | 438 | LOG_ERROR(Render_OpenGL, "Failed to write precompiled cache version in path={}", |
| 441 | precompiled_path); | 439 | Common::FS::PathToUTF8String(precompiled_path)); |
| 442 | } | 440 | } |
| 443 | } | 441 | } |
| 444 | 442 | ||
| 445 | bool ShaderDiskCacheOpenGL::EnsureDirectories() const { | 443 | bool ShaderDiskCacheOpenGL::EnsureDirectories() const { |
| 446 | const auto CreateDir = [](const std::string& dir) { | 444 | const auto CreateDir = [](const std::filesystem::path& dir) { |
| 447 | if (!Common::FS::CreateDir(dir)) { | 445 | if (!Common::FS::CreateDir(dir)) { |
| 448 | LOG_ERROR(Render_OpenGL, "Failed to create directory={}", dir); | 446 | LOG_ERROR(Render_OpenGL, "Failed to create directory={}", |
| 447 | Common::FS::PathToUTF8String(dir)); | ||
| 449 | return false; | 448 | return false; |
| 450 | } | 449 | } |
| 451 | return true; | 450 | return true; |
| 452 | }; | 451 | }; |
| 453 | 452 | ||
| 454 | return CreateDir(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)) && | 453 | return CreateDir(Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir)) && |
| 455 | CreateDir(GetBaseDir()) && CreateDir(GetTransferableDir()) && | 454 | CreateDir(GetBaseDir()) && CreateDir(GetTransferableDir()) && |
| 456 | CreateDir(GetPrecompiledDir()); | 455 | CreateDir(GetPrecompiledDir()); |
| 457 | } | 456 | } |
| 458 | 457 | ||
| 459 | std::string ShaderDiskCacheOpenGL::GetTransferablePath() const { | 458 | std::filesystem::path ShaderDiskCacheOpenGL::GetTransferablePath() const { |
| 460 | return Common::FS::SanitizePath(GetTransferableDir() + DIR_SEP_CHR + GetTitleID() + ".bin"); | 459 | return GetTransferableDir() / fmt::format("{}.bin", GetTitleID()); |
| 461 | } | 460 | } |
| 462 | 461 | ||
| 463 | std::string ShaderDiskCacheOpenGL::GetPrecompiledPath() const { | 462 | std::filesystem::path ShaderDiskCacheOpenGL::GetPrecompiledPath() const { |
| 464 | return Common::FS::SanitizePath(GetPrecompiledDir() + DIR_SEP_CHR + GetTitleID() + ".bin"); | 463 | return GetPrecompiledDir() / fmt::format("{}.bin", GetTitleID()); |
| 465 | } | 464 | } |
| 466 | 465 | ||
| 467 | std::string ShaderDiskCacheOpenGL::GetTransferableDir() const { | 466 | std::filesystem::path ShaderDiskCacheOpenGL::GetTransferableDir() const { |
| 468 | return GetBaseDir() + DIR_SEP "transferable"; | 467 | return GetBaseDir() / "transferable"; |
| 469 | } | 468 | } |
| 470 | 469 | ||
| 471 | std::string ShaderDiskCacheOpenGL::GetPrecompiledDir() const { | 470 | std::filesystem::path ShaderDiskCacheOpenGL::GetPrecompiledDir() const { |
| 472 | return GetBaseDir() + DIR_SEP "precompiled"; | 471 | return GetBaseDir() / "precompiled"; |
| 473 | } | 472 | } |
| 474 | 473 | ||
| 475 | std::string ShaderDiskCacheOpenGL::GetBaseDir() const { | 474 | std::filesystem::path ShaderDiskCacheOpenGL::GetBaseDir() const { |
| 476 | return Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir) + DIR_SEP "opengl"; | 475 | return Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir) / "opengl"; |
| 477 | } | 476 | } |
| 478 | 477 | ||
| 479 | std::string ShaderDiskCacheOpenGL::GetTitleID() const { | 478 | std::string ShaderDiskCacheOpenGL::GetTitleID() const { |
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h index aef841c1d..f8bc23868 100644 --- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h +++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <filesystem> | ||
| 7 | #include <optional> | 8 | #include <optional> |
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <tuple> | 10 | #include <tuple> |
| @@ -108,19 +109,19 @@ private: | |||
| 108 | bool EnsureDirectories() const; | 109 | bool EnsureDirectories() const; |
| 109 | 110 | ||
| 110 | /// Gets current game's transferable file path | 111 | /// Gets current game's transferable file path |
| 111 | std::string GetTransferablePath() const; | 112 | std::filesystem::path GetTransferablePath() const; |
| 112 | 113 | ||
| 113 | /// Gets current game's precompiled file path | 114 | /// Gets current game's precompiled file path |
| 114 | std::string GetPrecompiledPath() const; | 115 | std::filesystem::path GetPrecompiledPath() const; |
| 115 | 116 | ||
| 116 | /// Get user's transferable directory path | 117 | /// Get user's transferable directory path |
| 117 | std::string GetTransferableDir() const; | 118 | std::filesystem::path GetTransferableDir() const; |
| 118 | 119 | ||
| 119 | /// Get user's precompiled directory path | 120 | /// Get user's precompiled directory path |
| 120 | std::string GetPrecompiledDir() const; | 121 | std::filesystem::path GetPrecompiledDir() const; |
| 121 | 122 | ||
| 122 | /// Get user's shader directory path | 123 | /// Get user's shader directory path |
| 123 | std::string GetBaseDir() const; | 124 | std::filesystem::path GetBaseDir() const; |
| 124 | 125 | ||
| 125 | /// Get current game's title id | 126 | /// Get current game's title id |
| 126 | std::string GetTitleID() const; | 127 | std::string GetTitleID() const; |
diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp index 7a9d00d4f..f0ee76519 100644 --- a/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp +++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #ifdef HAS_NSIGHT_AFTERMATH | 5 | #ifdef HAS_NSIGHT_AFTERMATH |
| 6 | 6 | ||
| 7 | #include <mutex> | 7 | #include <mutex> |
| 8 | #include <span> | ||
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <string_view> | 10 | #include <string_view> |
| 10 | #include <utility> | 11 | #include <utility> |
| @@ -12,9 +13,10 @@ | |||
| 12 | 13 | ||
| 13 | #include <fmt/format.h> | 14 | #include <fmt/format.h> |
| 14 | 15 | ||
| 15 | #include "common/common_paths.h" | ||
| 16 | #include "common/common_types.h" | 16 | #include "common/common_types.h" |
| 17 | #include "common/file_util.h" | 17 | #include "common/fs/file.h" |
| 18 | #include "common/fs/fs.h" | ||
| 19 | #include "common/fs/path_util.h" | ||
| 18 | #include "common/logging/log.h" | 20 | #include "common/logging/log.h" |
| 19 | #include "common/scope_exit.h" | 21 | #include "common/scope_exit.h" |
| 20 | #include "video_core/vulkan_common/nsight_aftermath_tracker.h" | 22 | #include "video_core/vulkan_common/nsight_aftermath_tracker.h" |
| @@ -46,9 +48,9 @@ NsightAftermathTracker::NsightAftermathTracker() { | |||
| 46 | LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers"); | 48 | LOG_ERROR(Render_Vulkan, "Failed to load Nsight Aftermath function pointers"); |
| 47 | return; | 49 | return; |
| 48 | } | 50 | } |
| 49 | dump_dir = Common::FS::GetUserPath(Common::FS::UserPath::LogDir) + "gpucrash"; | 51 | dump_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::LogDir) / "gpucrash"; |
| 50 | 52 | ||
| 51 | void(Common::FS::DeleteDirRecursively(dump_dir)); | 53 | void(Common::FS::RemoveDirRecursively(dump_dir)); |
| 52 | if (!Common::FS::CreateDir(dump_dir)) { | 54 | if (!Common::FS::CreateDir(dump_dir)) { |
| 53 | LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory"); | 55 | LOG_ERROR(Render_Vulkan, "Failed to create Nsight Aftermath dump directory"); |
| 54 | return; | 56 | return; |
| @@ -60,7 +62,8 @@ NsightAftermathTracker::NsightAftermathTracker() { | |||
| 60 | LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed"); | 62 | LOG_ERROR(Render_Vulkan, "GFSDK_Aftermath_EnableGpuCrashDumps failed"); |
| 61 | return; | 63 | return; |
| 62 | } | 64 | } |
| 63 | LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"", dump_dir); | 65 | LOG_INFO(Render_Vulkan, "Nsight Aftermath dump directory is \"{}\"", |
| 66 | Common::FS::PathToUTF8String(dump_dir)); | ||
| 64 | initialized = true; | 67 | initialized = true; |
| 65 | } | 68 | } |
| 66 | 69 | ||
| @@ -89,12 +92,15 @@ void NsightAftermathTracker::SaveShader(const std::vector<u32>& spirv) const { | |||
| 89 | return; | 92 | return; |
| 90 | } | 93 | } |
| 91 | 94 | ||
| 92 | Common::FS::IOFile file(fmt::format("{}/source_{:016x}.spv", dump_dir, hash.hash), "wb"); | 95 | const auto shader_file = dump_dir / fmt::format("source_{:016x}.spv", hash.hash); |
| 96 | |||
| 97 | Common::FS::IOFile file{shader_file, Common::FS::FileAccessMode::Write, | ||
| 98 | Common::FS::FileType::BinaryFile}; | ||
| 93 | if (!file.IsOpen()) { | 99 | if (!file.IsOpen()) { |
| 94 | LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash); | 100 | LOG_ERROR(Render_Vulkan, "Failed to dump SPIR-V module with hash={:016x}", hash.hash); |
| 95 | return; | 101 | return; |
| 96 | } | 102 | } |
| 97 | if (file.WriteArray(spirv.data(), spirv.size()) != spirv.size()) { | 103 | if (file.Write(spirv) != spirv.size()) { |
| 98 | LOG_ERROR(Render_Vulkan, "Failed to write SPIR-V module with hash={:016x}", hash.hash); | 104 | LOG_ERROR(Render_Vulkan, "Failed to write SPIR-V module with hash={:016x}", hash.hash); |
| 99 | return; | 105 | return; |
| 100 | } | 106 | } |
| @@ -129,22 +135,24 @@ void NsightAftermathTracker::OnGpuCrashDumpCallback(const void* gpu_crash_dump, | |||
| 129 | return; | 135 | return; |
| 130 | } | 136 | } |
| 131 | 137 | ||
| 132 | const std::string base_name = [this] { | 138 | std::filesystem::path base_name = [this] { |
| 133 | const int id = dump_id++; | 139 | const int id = dump_id++; |
| 134 | if (id == 0) { | 140 | if (id == 0) { |
| 135 | return fmt::format("{}/crash.nv-gpudmp", dump_dir); | 141 | return dump_dir / "crash.nv-gpudmp"; |
| 136 | } else { | 142 | } else { |
| 137 | return fmt::format("{}/crash_{}.nv-gpudmp", dump_dir, id); | 143 | return dump_dir / fmt::format("crash_{}.nv-gpudmp", id); |
| 138 | } | 144 | } |
| 139 | }(); | 145 | }(); |
| 140 | 146 | ||
| 141 | std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size); | 147 | std::string_view dump_view(static_cast<const char*>(gpu_crash_dump), gpu_crash_dump_size); |
| 142 | if (Common::FS::WriteStringToFile(false, base_name, dump_view) != gpu_crash_dump_size) { | 148 | if (Common::FS::WriteStringToFile(base_name, Common::FS::FileType::BinaryFile, dump_view) != |
| 149 | gpu_crash_dump_size) { | ||
| 143 | LOG_ERROR(Render_Vulkan, "Failed to write dump file"); | 150 | LOG_ERROR(Render_Vulkan, "Failed to write dump file"); |
| 144 | return; | 151 | return; |
| 145 | } | 152 | } |
| 146 | const std::string_view json_view(json.data(), json.size()); | 153 | const std::string_view json_view(json.data(), json.size()); |
| 147 | if (Common::FS::WriteStringToFile(true, base_name + ".json", json_view) != json.size()) { | 154 | if (Common::FS::WriteStringToFile(base_name.concat(".json"), Common::FS::FileType::TextFile, |
| 155 | json_view) != json.size()) { | ||
| 148 | LOG_ERROR(Render_Vulkan, "Failed to write JSON"); | 156 | LOG_ERROR(Render_Vulkan, "Failed to write JSON"); |
| 149 | return; | 157 | return; |
| 150 | } | 158 | } |
| @@ -161,16 +169,17 @@ void NsightAftermathTracker::OnShaderDebugInfoCallback(const void* shader_debug_ | |||
| 161 | return; | 169 | return; |
| 162 | } | 170 | } |
| 163 | 171 | ||
| 164 | const std::string path = | 172 | const auto path = |
| 165 | fmt::format("{}/shader_{:016x}{:016x}.nvdbg", dump_dir, identifier.id[0], identifier.id[1]); | 173 | dump_dir / fmt::format("shader_{:016x}{:016x}.nvdbg", identifier.id[0], identifier.id[1]); |
| 166 | Common::FS::IOFile file(path, "wb"); | 174 | Common::FS::IOFile file{path, Common::FS::FileAccessMode::Write, |
| 175 | Common::FS::FileType::BinaryFile}; | ||
| 167 | if (!file.IsOpen()) { | 176 | if (!file.IsOpen()) { |
| 168 | LOG_ERROR(Render_Vulkan, "Failed to create file {}", path); | 177 | LOG_ERROR(Render_Vulkan, "Failed to create file {}", Common::FS::PathToUTF8String(path)); |
| 169 | return; | 178 | return; |
| 170 | } | 179 | } |
| 171 | if (file.WriteBytes(static_cast<const u8*>(shader_debug_info), shader_debug_info_size) != | 180 | if (file.WriteSpan(std::span(static_cast<const u8*>(shader_debug_info), |
| 172 | shader_debug_info_size) { | 181 | shader_debug_info_size)) != shader_debug_info_size) { |
| 173 | LOG_ERROR(Render_Vulkan, "Failed to write file {}", path); | 182 | LOG_ERROR(Render_Vulkan, "Failed to write file {}", Common::FS::PathToUTF8String(path)); |
| 174 | return; | 183 | return; |
| 175 | } | 184 | } |
| 176 | } | 185 | } |
diff --git a/src/video_core/vulkan_common/nsight_aftermath_tracker.h b/src/video_core/vulkan_common/nsight_aftermath_tracker.h index 1ce8d4e8e..4fe2b14d9 100644 --- a/src/video_core/vulkan_common/nsight_aftermath_tracker.h +++ b/src/video_core/vulkan_common/nsight_aftermath_tracker.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <filesystem> | ||
| 7 | #include <mutex> | 8 | #include <mutex> |
| 8 | #include <string> | 9 | #include <string> |
| 9 | #include <vector> | 10 | #include <vector> |
| @@ -54,7 +55,7 @@ private: | |||
| 54 | 55 | ||
| 55 | mutable std::mutex mutex; | 56 | mutable std::mutex mutex; |
| 56 | 57 | ||
| 57 | std::string dump_dir; | 58 | std::filesystem::path dump_dir; |
| 58 | int dump_id = 0; | 59 | int dump_id = 0; |
| 59 | 60 | ||
| 60 | bool initialized = false; | 61 | bool initialized = false; |
diff --git a/src/video_core/vulkan_common/vulkan_library.cpp b/src/video_core/vulkan_common/vulkan_library.cpp index 557871d81..22833fa56 100644 --- a/src/video_core/vulkan_common/vulkan_library.cpp +++ b/src/video_core/vulkan_common/vulkan_library.cpp | |||
| @@ -6,7 +6,7 @@ | |||
| 6 | #include <string> | 6 | #include <string> |
| 7 | 7 | ||
| 8 | #include "common/dynamic_library.h" | 8 | #include "common/dynamic_library.h" |
| 9 | #include "common/file_util.h" | 9 | #include "common/fs/path_util.h" |
| 10 | #include "video_core/vulkan_common/vulkan_library.h" | 10 | #include "video_core/vulkan_common/vulkan_library.h" |
| 11 | 11 | ||
| 12 | namespace Vulkan { | 12 | namespace Vulkan { |
| @@ -18,9 +18,9 @@ Common::DynamicLibrary OpenLibrary() { | |||
| 18 | char* const libvulkan_env = std::getenv("LIBVULKAN_PATH"); | 18 | char* const libvulkan_env = std::getenv("LIBVULKAN_PATH"); |
| 19 | if (!libvulkan_env || !library.Open(libvulkan_env)) { | 19 | if (!libvulkan_env || !library.Open(libvulkan_env)) { |
| 20 | // Use the libvulkan.dylib from the application bundle. | 20 | // Use the libvulkan.dylib from the application bundle. |
| 21 | const std::string filename = | 21 | const auto filename = |
| 22 | Common::FS::GetBundleDirectory() + "/Contents/Frameworks/libvulkan.dylib"; | 22 | Common::FS::GetBundleDirectory() / "Contents/Frameworks/libvulkan.dylib"; |
| 23 | void(library.Open(filename.c_str())); | 23 | void(library.Open(Common::FS::PathToUTF8String(filename).c_str())); |
| 24 | } | 24 | } |
| 25 | #else | 25 | #else |
| 26 | std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); | 26 | std::string filename = Common::DynamicLibrary::GetVersionedFilename("vulkan", 1); |
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp index 0a4c48b3d..62fd1141c 100644 --- a/src/yuzu/applets/profile_select.cpp +++ b/src/yuzu/applets/profile_select.cpp | |||
| @@ -10,7 +10,7 @@ | |||
| 10 | #include <QScrollArea> | 10 | #include <QScrollArea> |
| 11 | #include <QStandardItemModel> | 11 | #include <QStandardItemModel> |
| 12 | #include <QVBoxLayout> | 12 | #include <QVBoxLayout> |
| 13 | #include "common/file_util.h" | 13 | #include "common/fs/path_util.h" |
| 14 | #include "common/string_util.h" | 14 | #include "common/string_util.h" |
| 15 | #include "core/constants.h" | 15 | #include "core/constants.h" |
| 16 | #include "core/hle/lock.h" | 16 | #include "core/hle/lock.h" |
| @@ -26,9 +26,10 @@ QString FormatUserEntryText(const QString& username, Common::UUID uuid) { | |||
| 26 | } | 26 | } |
| 27 | 27 | ||
| 28 | QString GetImagePath(Common::UUID uuid) { | 28 | QString GetImagePath(Common::UUID uuid) { |
| 29 | const auto path = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + | 29 | const auto path = |
| 30 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | 30 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / |
| 31 | return QString::fromStdString(path); | 31 | fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch()); |
| 32 | return QString::fromStdString(Common::FS::PathToUTF8String(path)); | ||
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | QPixmap GetIcon(Common::UUID uuid) { | 35 | QPixmap GetIcon(Common::UUID uuid) { |
diff --git a/src/yuzu/applets/web_browser.cpp b/src/yuzu/applets/web_browser.cpp index 93e3a4f6f..34d3feb55 100644 --- a/src/yuzu/applets/web_browser.cpp +++ b/src/yuzu/applets/web_browser.cpp | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | #include <QWebEngineUrlScheme> | 12 | #include <QWebEngineUrlScheme> |
| 13 | #endif | 13 | #endif |
| 14 | 14 | ||
| 15 | #include "common/file_util.h" | 15 | #include "common/fs/path_util.h" |
| 16 | #include "core/core.h" | 16 | #include "core/core.h" |
| 17 | #include "core/frontend/input_interpreter.h" | 17 | #include "core/frontend/input_interpreter.h" |
| 18 | #include "input_common/keyboard.h" | 18 | #include "input_common/keyboard.h" |
| @@ -322,21 +322,25 @@ void QtNXWebEngineView::LoadExtractedFonts() { | |||
| 322 | QWebEngineScript nx_font_css; | 322 | QWebEngineScript nx_font_css; |
| 323 | QWebEngineScript load_nx_font; | 323 | QWebEngineScript load_nx_font; |
| 324 | 324 | ||
| 325 | const QString fonts_dir = QString::fromStdString(Common::FS::SanitizePath( | 325 | auto fonts_dir_str = Common::FS::PathToUTF8String( |
| 326 | fmt::format("{}/fonts", Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)))); | 326 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "fonts/"); |
| 327 | |||
| 328 | std::replace(fonts_dir_str.begin(), fonts_dir_str.end(), '\\', '/'); | ||
| 329 | |||
| 330 | const auto fonts_dir = QString::fromStdString(fonts_dir_str); | ||
| 327 | 331 | ||
| 328 | nx_font_css.setName(QStringLiteral("nx_font_css.js")); | 332 | nx_font_css.setName(QStringLiteral("nx_font_css.js")); |
| 329 | load_nx_font.setName(QStringLiteral("load_nx_font.js")); | 333 | load_nx_font.setName(QStringLiteral("load_nx_font.js")); |
| 330 | 334 | ||
| 331 | nx_font_css.setSourceCode( | 335 | nx_font_css.setSourceCode( |
| 332 | QString::fromStdString(NX_FONT_CSS) | 336 | QString::fromStdString(NX_FONT_CSS) |
| 333 | .arg(fonts_dir + QStringLiteral("/FontStandard.ttf")) | 337 | .arg(fonts_dir + QStringLiteral("FontStandard.ttf")) |
| 334 | .arg(fonts_dir + QStringLiteral("/FontChineseSimplified.ttf")) | 338 | .arg(fonts_dir + QStringLiteral("FontChineseSimplified.ttf")) |
| 335 | .arg(fonts_dir + QStringLiteral("/FontExtendedChineseSimplified.ttf")) | 339 | .arg(fonts_dir + QStringLiteral("FontExtendedChineseSimplified.ttf")) |
| 336 | .arg(fonts_dir + QStringLiteral("/FontChineseTraditional.ttf")) | 340 | .arg(fonts_dir + QStringLiteral("FontChineseTraditional.ttf")) |
| 337 | .arg(fonts_dir + QStringLiteral("/FontKorean.ttf")) | 341 | .arg(fonts_dir + QStringLiteral("FontKorean.ttf")) |
| 338 | .arg(fonts_dir + QStringLiteral("/FontNintendoExtended.ttf")) | 342 | .arg(fonts_dir + QStringLiteral("FontNintendoExtended.ttf")) |
| 339 | .arg(fonts_dir + QStringLiteral("/FontNintendoExtended2.ttf"))); | 343 | .arg(fonts_dir + QStringLiteral("FontNintendoExtended2.ttf"))); |
| 340 | load_nx_font.setSourceCode(QString::fromStdString(LOAD_NX_FONT)); | 344 | load_nx_font.setSourceCode(QString::fromStdString(LOAD_NX_FONT)); |
| 341 | 345 | ||
| 342 | nx_font_css.setInjectionPoint(QWebEngineScript::DocumentReady); | 346 | nx_font_css.setInjectionPoint(QWebEngineScript::DocumentReady); |
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp index 21d1dc174..eb58bfa5b 100644 --- a/src/yuzu/configuration/config.cpp +++ b/src/yuzu/configuration/config.cpp | |||
| @@ -5,8 +5,8 @@ | |||
| 5 | #include <array> | 5 | #include <array> |
| 6 | #include <QKeySequence> | 6 | #include <QKeySequence> |
| 7 | #include <QSettings> | 7 | #include <QSettings> |
| 8 | #include "common/common_paths.h" | 8 | #include "common/fs/fs.h" |
| 9 | #include "common/file_util.h" | 9 | #include "common/fs/path_util.h" |
| 10 | #include "core/core.h" | 10 | #include "core/core.h" |
| 11 | #include "core/hle/service/acc/profile_manager.h" | 11 | #include "core/hle/service/acc/profile_manager.h" |
| 12 | #include "core/hle/service/hid/controllers/npad.h" | 12 | #include "core/hle/service/hid/controllers/npad.h" |
| @@ -243,27 +243,27 @@ const std::array<UISettings::Shortcut, 17> Config::default_hotkeys{{ | |||
| 243 | // clang-format on | 243 | // clang-format on |
| 244 | 244 | ||
| 245 | void Config::Initialize(const std::string& config_name) { | 245 | void Config::Initialize(const std::string& config_name) { |
| 246 | const auto fs_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir); | ||
| 247 | const auto config_file = fmt::format("{}.ini", config_name); | ||
| 248 | |||
| 246 | switch (type) { | 249 | switch (type) { |
| 247 | case ConfigType::GlobalConfig: | 250 | case ConfigType::GlobalConfig: |
| 248 | qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", FS::GetUserPath(FS::UserPath::ConfigDir), | 251 | qt_config_loc = FS::PathToUTF8String(fs_config_loc / config_file); |
| 249 | config_name); | 252 | void(FS::CreateParentDir(qt_config_loc)); |
| 250 | FS::CreateFullPath(qt_config_loc); | ||
| 251 | qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), | 253 | qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), |
| 252 | QSettings::IniFormat); | 254 | QSettings::IniFormat); |
| 253 | Reload(); | 255 | Reload(); |
| 254 | break; | 256 | break; |
| 255 | case ConfigType::PerGameConfig: | 257 | case ConfigType::PerGameConfig: |
| 256 | qt_config_loc = fmt::format("{}custom" DIR_SEP "{}.ini", | 258 | qt_config_loc = FS::PathToUTF8String(fs_config_loc / "custom" / config_file); |
| 257 | FS::GetUserPath(FS::UserPath::ConfigDir), config_name); | 259 | void(FS::CreateParentDir(qt_config_loc)); |
| 258 | FS::CreateFullPath(qt_config_loc); | ||
| 259 | qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), | 260 | qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), |
| 260 | QSettings::IniFormat); | 261 | QSettings::IniFormat); |
| 261 | Reload(); | 262 | Reload(); |
| 262 | break; | 263 | break; |
| 263 | case ConfigType::InputProfile: | 264 | case ConfigType::InputProfile: |
| 264 | qt_config_loc = fmt::format("{}input" DIR_SEP "{}.ini", | 265 | qt_config_loc = FS::PathToUTF8String(fs_config_loc / "input" / config_file); |
| 265 | FS::GetUserPath(FS::UserPath::ConfigDir), config_name); | 266 | void(FS::CreateParentDir(qt_config_loc)); |
| 266 | FS::CreateFullPath(qt_config_loc); | ||
| 267 | qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), | 267 | qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), |
| 268 | QSettings::IniFormat); | 268 | QSettings::IniFormat); |
| 269 | break; | 269 | break; |
| @@ -598,30 +598,34 @@ void Config::ReadDataStorageValues() { | |||
| 598 | qt_config->beginGroup(QStringLiteral("Data Storage")); | 598 | qt_config->beginGroup(QStringLiteral("Data Storage")); |
| 599 | 599 | ||
| 600 | Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool(); | 600 | Settings::values.use_virtual_sd = ReadSetting(QStringLiteral("use_virtual_sd"), true).toBool(); |
| 601 | FS::GetUserPath(FS::UserPath::NANDDir, | 601 | FS::SetYuzuPath( |
| 602 | qt_config | 602 | FS::YuzuPath::NANDDir, |
| 603 | ->value(QStringLiteral("nand_directory"), | 603 | qt_config |
| 604 | QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir))) | 604 | ->value(QStringLiteral("nand_directory"), |
| 605 | .toString() | 605 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))) |
| 606 | .toStdString()); | 606 | .toString() |
| 607 | FS::GetUserPath(FS::UserPath::SDMCDir, | 607 | .toStdString()); |
| 608 | qt_config | 608 | FS::SetYuzuPath( |
| 609 | ->value(QStringLiteral("sdmc_directory"), | 609 | FS::YuzuPath::SDMCDir, |
| 610 | QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir))) | 610 | qt_config |
| 611 | .toString() | 611 | ->value(QStringLiteral("sdmc_directory"), |
| 612 | .toStdString()); | 612 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))) |
| 613 | FS::GetUserPath(FS::UserPath::LoadDir, | 613 | .toString() |
| 614 | qt_config | 614 | .toStdString()); |
| 615 | ->value(QStringLiteral("load_directory"), | 615 | FS::SetYuzuPath( |
| 616 | QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir))) | 616 | FS::YuzuPath::LoadDir, |
| 617 | .toString() | 617 | qt_config |
| 618 | .toStdString()); | 618 | ->value(QStringLiteral("load_directory"), |
| 619 | FS::GetUserPath(FS::UserPath::DumpDir, | 619 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))) |
| 620 | qt_config | 620 | .toString() |
| 621 | ->value(QStringLiteral("dump_directory"), | 621 | .toStdString()); |
| 622 | QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir))) | 622 | FS::SetYuzuPath( |
| 623 | .toString() | 623 | FS::YuzuPath::DumpDir, |
| 624 | .toStdString()); | 624 | qt_config |
| 625 | ->value(QStringLiteral("dump_directory"), | ||
| 626 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))) | ||
| 627 | .toString() | ||
| 628 | .toStdString()); | ||
| 625 | Settings::values.gamecard_inserted = | 629 | Settings::values.gamecard_inserted = |
| 626 | ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool(); | 630 | ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool(); |
| 627 | Settings::values.gamecard_current_game = | 631 | Settings::values.gamecard_current_game = |
| @@ -817,11 +821,11 @@ void Config::ReadScreenshotValues() { | |||
| 817 | 821 | ||
| 818 | UISettings::values.enable_screenshot_save_as = | 822 | UISettings::values.enable_screenshot_save_as = |
| 819 | ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool(); | 823 | ReadSetting(QStringLiteral("enable_screenshot_save_as"), true).toBool(); |
| 820 | FS::GetUserPath( | 824 | FS::SetYuzuPath( |
| 821 | FS::UserPath::ScreenshotsDir, | 825 | FS::YuzuPath::ScreenshotsDir, |
| 822 | qt_config | 826 | qt_config |
| 823 | ->value(QStringLiteral("screenshot_path"), | 827 | ->value(QStringLiteral("screenshot_path"), |
| 824 | QString::fromStdString(FS::GetUserPath(FS::UserPath::ScreenshotsDir))) | 828 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))) |
| 825 | .toString() | 829 | .toString() |
| 826 | .toStdString()); | 830 | .toStdString()); |
| 827 | 831 | ||
| @@ -1220,17 +1224,17 @@ void Config::SaveDataStorageValues() { | |||
| 1220 | 1224 | ||
| 1221 | WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true); | 1225 | WriteSetting(QStringLiteral("use_virtual_sd"), Settings::values.use_virtual_sd, true); |
| 1222 | WriteSetting(QStringLiteral("nand_directory"), | 1226 | WriteSetting(QStringLiteral("nand_directory"), |
| 1223 | QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir)), | 1227 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir)), |
| 1224 | QString::fromStdString(FS::GetUserPath(FS::UserPath::NANDDir))); | 1228 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); |
| 1225 | WriteSetting(QStringLiteral("sdmc_directory"), | 1229 | WriteSetting(QStringLiteral("sdmc_directory"), |
| 1226 | QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir)), | 1230 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir)), |
| 1227 | QString::fromStdString(FS::GetUserPath(FS::UserPath::SDMCDir))); | 1231 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); |
| 1228 | WriteSetting(QStringLiteral("load_directory"), | 1232 | WriteSetting(QStringLiteral("load_directory"), |
| 1229 | QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir)), | 1233 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir)), |
| 1230 | QString::fromStdString(FS::GetUserPath(FS::UserPath::LoadDir))); | 1234 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); |
| 1231 | WriteSetting(QStringLiteral("dump_directory"), | 1235 | WriteSetting(QStringLiteral("dump_directory"), |
| 1232 | QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir)), | 1236 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir)), |
| 1233 | QString::fromStdString(FS::GetUserPath(FS::UserPath::DumpDir))); | 1237 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); |
| 1234 | WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false); | 1238 | WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false); |
| 1235 | WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, | 1239 | WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, |
| 1236 | false); | 1240 | false); |
| @@ -1397,7 +1401,7 @@ void Config::SaveScreenshotValues() { | |||
| 1397 | WriteSetting(QStringLiteral("enable_screenshot_save_as"), | 1401 | WriteSetting(QStringLiteral("enable_screenshot_save_as"), |
| 1398 | UISettings::values.enable_screenshot_save_as); | 1402 | UISettings::values.enable_screenshot_save_as); |
| 1399 | WriteSetting(QStringLiteral("screenshot_path"), | 1403 | WriteSetting(QStringLiteral("screenshot_path"), |
| 1400 | QString::fromStdString(FS::GetUserPath(FS::UserPath::ScreenshotsDir))); | 1404 | QString::fromStdString(FS::GetYuzuPathString(FS::YuzuPath::ScreenshotsDir))); |
| 1401 | 1405 | ||
| 1402 | qt_config->endGroup(); | 1406 | qt_config->endGroup(); |
| 1403 | } | 1407 | } |
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp index 6730eb356..b207e07cb 100644 --- a/src/yuzu/configuration/configure_debug.cpp +++ b/src/yuzu/configuration/configure_debug.cpp | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #include <QDesktopServices> | 5 | #include <QDesktopServices> |
| 6 | #include <QUrl> | 6 | #include <QUrl> |
| 7 | #include "common/file_util.h" | 7 | #include "common/fs/path_util.h" |
| 8 | #include "common/logging/backend.h" | 8 | #include "common/logging/backend.h" |
| 9 | #include "common/logging/filter.h" | 9 | #include "common/logging/filter.h" |
| 10 | #include "common/settings.h" | 10 | #include "common/settings.h" |
| @@ -20,7 +20,7 @@ ConfigureDebug::ConfigureDebug(QWidget* parent) : QWidget(parent), ui(new Ui::Co | |||
| 20 | 20 | ||
| 21 | connect(ui->open_log_button, &QPushButton::clicked, []() { | 21 | connect(ui->open_log_button, &QPushButton::clicked, []() { |
| 22 | const auto path = | 22 | const auto path = |
| 23 | QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LogDir)); | 23 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LogDir)); |
| 24 | QDesktopServices::openUrl(QUrl::fromLocalFile(path)); | 24 | QDesktopServices::openUrl(QUrl::fromLocalFile(path)); |
| 25 | }); | 25 | }); |
| 26 | } | 26 | } |
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp index 006eda4b0..d223c40ea 100644 --- a/src/yuzu/configuration/configure_filesystem.cpp +++ b/src/yuzu/configuration/configure_filesystem.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <QFileDialog> | 5 | #include <QFileDialog> |
| 6 | #include <QMessageBox> | 6 | #include <QMessageBox> |
| 7 | #include "common/common_paths.h" | 7 | #include "common/fs/fs.h" |
| 8 | #include "common/file_util.h" | 8 | #include "common/fs/path_util.h" |
| 9 | #include "common/settings.h" | 9 | #include "common/settings.h" |
| 10 | #include "ui_configure_filesystem.h" | 10 | #include "ui_configure_filesystem.h" |
| 11 | #include "yuzu/configuration/configure_filesystem.h" | 11 | #include "yuzu/configuration/configure_filesystem.h" |
| @@ -40,14 +40,14 @@ ConfigureFilesystem::~ConfigureFilesystem() = default; | |||
| 40 | 40 | ||
| 41 | void ConfigureFilesystem::setConfiguration() { | 41 | void ConfigureFilesystem::setConfiguration() { |
| 42 | ui->nand_directory_edit->setText( | 42 | ui->nand_directory_edit->setText( |
| 43 | QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir))); | 43 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::NANDDir))); |
| 44 | ui->sdmc_directory_edit->setText( | 44 | ui->sdmc_directory_edit->setText( |
| 45 | QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir))); | 45 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::SDMCDir))); |
| 46 | ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path)); | 46 | ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path)); |
| 47 | ui->dump_path_edit->setText( | 47 | ui->dump_path_edit->setText( |
| 48 | QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::DumpDir))); | 48 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::DumpDir))); |
| 49 | ui->load_path_edit->setText( | 49 | ui->load_path_edit->setText( |
| 50 | QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::LoadDir))); | 50 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::LoadDir))); |
| 51 | 51 | ||
| 52 | ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted); | 52 | ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted); |
| 53 | ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game); | 53 | ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game); |
| @@ -60,13 +60,13 @@ void ConfigureFilesystem::setConfiguration() { | |||
| 60 | } | 60 | } |
| 61 | 61 | ||
| 62 | void ConfigureFilesystem::applyConfiguration() { | 62 | void ConfigureFilesystem::applyConfiguration() { |
| 63 | Common::FS::GetUserPath(Common::FS::UserPath::NANDDir, | 63 | Common::FS::SetYuzuPath(Common::FS::YuzuPath::NANDDir, |
| 64 | ui->nand_directory_edit->text().toStdString()); | 64 | ui->nand_directory_edit->text().toStdString()); |
| 65 | Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir, | 65 | Common::FS::SetYuzuPath(Common::FS::YuzuPath::SDMCDir, |
| 66 | ui->sdmc_directory_edit->text().toStdString()); | 66 | ui->sdmc_directory_edit->text().toStdString()); |
| 67 | Common::FS::GetUserPath(Common::FS::UserPath::DumpDir, | 67 | Common::FS::SetYuzuPath(Common::FS::YuzuPath::DumpDir, |
| 68 | ui->dump_path_edit->text().toStdString()); | 68 | ui->dump_path_edit->text().toStdString()); |
| 69 | Common::FS::GetUserPath(Common::FS::UserPath::LoadDir, | 69 | Common::FS::SetYuzuPath(Common::FS::YuzuPath::LoadDir, |
| 70 | ui->load_path_edit->text().toStdString()); | 70 | ui->load_path_edit->text().toStdString()); |
| 71 | 71 | ||
| 72 | Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked(); | 72 | Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked(); |
| @@ -104,25 +104,26 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) | |||
| 104 | QStringLiteral("NX Gamecard;*.xci")); | 104 | QStringLiteral("NX Gamecard;*.xci")); |
| 105 | } else { | 105 | } else { |
| 106 | str = QFileDialog::getExistingDirectory(this, caption, edit->text()); | 106 | str = QFileDialog::getExistingDirectory(this, caption, edit->text()); |
| 107 | if (!str.isNull() && str.back() != QDir::separator()) { | ||
| 108 | str.append(QDir::separator()); | ||
| 109 | } | ||
| 110 | } | 107 | } |
| 111 | 108 | ||
| 112 | if (str.isEmpty()) | 109 | if (str.isNull() || str.isEmpty()) { |
| 113 | return; | 110 | return; |
| 111 | } | ||
| 112 | |||
| 113 | if (str.back() != QChar::fromLatin1('/')) { | ||
| 114 | str.append(QChar::fromLatin1('/')); | ||
| 115 | } | ||
| 114 | 116 | ||
| 115 | edit->setText(str); | 117 | edit->setText(str); |
| 116 | } | 118 | } |
| 117 | 119 | ||
| 118 | void ConfigureFilesystem::ResetMetadata() { | 120 | void ConfigureFilesystem::ResetMetadata() { |
| 119 | if (!Common::FS::Exists(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP + | 121 | if (!Common::FS::Exists(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / |
| 120 | "game_list")) { | 122 | "game_list/")) { |
| 121 | QMessageBox::information(this, tr("Reset Metadata Cache"), | 123 | QMessageBox::information(this, tr("Reset Metadata Cache"), |
| 122 | tr("The metadata cache is already empty.")); | 124 | tr("The metadata cache is already empty.")); |
| 123 | } else if (Common::FS::DeleteDirRecursively( | 125 | } else if (Common::FS::RemoveDirRecursively( |
| 124 | Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP + | 126 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "game_list")) { |
| 125 | "game_list")) { | ||
| 126 | QMessageBox::information(this, tr("Reset Metadata Cache"), | 127 | QMessageBox::information(this, tr("Reset Metadata Cache"), |
| 127 | tr("The operation completed successfully.")); | 128 | tr("The operation completed successfully.")); |
| 128 | UISettings::values.is_game_list_reload_pending.exchange(true); | 129 | UISettings::values.is_game_list_reload_pending.exchange(true); |
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp index f550567e2..3e13bd438 100644 --- a/src/yuzu/configuration/configure_per_game.cpp +++ b/src/yuzu/configuration/configure_per_game.cpp | |||
| @@ -14,8 +14,6 @@ | |||
| 14 | #include <QTimer> | 14 | #include <QTimer> |
| 15 | #include <QTreeView> | 15 | #include <QTreeView> |
| 16 | 16 | ||
| 17 | #include "common/common_paths.h" | ||
| 18 | #include "common/file_util.h" | ||
| 19 | #include "core/core.h" | 17 | #include "core/core.h" |
| 20 | #include "core/file_sys/control_metadata.h" | 18 | #include "core/file_sys/control_metadata.h" |
| 21 | #include "core/file_sys/patch_manager.h" | 19 | #include "core/file_sys/patch_manager.h" |
diff --git a/src/yuzu/configuration/configure_per_game_addons.cpp b/src/yuzu/configuration/configure_per_game_addons.cpp index cdeeec01c..9b709d405 100644 --- a/src/yuzu/configuration/configure_per_game_addons.cpp +++ b/src/yuzu/configuration/configure_per_game_addons.cpp | |||
| @@ -13,8 +13,8 @@ | |||
| 13 | #include <QTimer> | 13 | #include <QTimer> |
| 14 | #include <QTreeView> | 14 | #include <QTreeView> |
| 15 | 15 | ||
| 16 | #include "common/common_paths.h" | 16 | #include "common/fs/fs.h" |
| 17 | #include "common/file_util.h" | 17 | #include "common/fs/path_util.h" |
| 18 | #include "core/core.h" | 18 | #include "core/core.h" |
| 19 | #include "core/file_sys/patch_manager.h" | 19 | #include "core/file_sys/patch_manager.h" |
| 20 | #include "core/file_sys/xts_archive.h" | 20 | #include "core/file_sys/xts_archive.h" |
| @@ -79,8 +79,8 @@ void ConfigurePerGameAddons::ApplyConfiguration() { | |||
| 79 | std::sort(disabled_addons.begin(), disabled_addons.end()); | 79 | std::sort(disabled_addons.begin(), disabled_addons.end()); |
| 80 | std::sort(current.begin(), current.end()); | 80 | std::sort(current.begin(), current.end()); |
| 81 | if (disabled_addons != current) { | 81 | if (disabled_addons != current) { |
| 82 | Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP + | 82 | void(Common::FS::RemoveFile(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / |
| 83 | "game_list" + DIR_SEP + fmt::format("{:016X}.pv.txt", title_id)); | 83 | "game_list" / fmt::format("{:016X}.pv.txt", title_id))); |
| 84 | } | 84 | } |
| 85 | 85 | ||
| 86 | Settings::values.disabled_addons[title_id] = disabled_addons; | 86 | Settings::values.disabled_addons[title_id] = disabled_addons; |
diff --git a/src/yuzu/configuration/configure_profile_manager.cpp b/src/yuzu/configuration/configure_profile_manager.cpp index d61b5e29b..f5881e58d 100644 --- a/src/yuzu/configuration/configure_profile_manager.cpp +++ b/src/yuzu/configuration/configure_profile_manager.cpp | |||
| @@ -12,7 +12,7 @@ | |||
| 12 | #include <QTreeView> | 12 | #include <QTreeView> |
| 13 | #include <QVBoxLayout> | 13 | #include <QVBoxLayout> |
| 14 | #include "common/assert.h" | 14 | #include "common/assert.h" |
| 15 | #include "common/file_util.h" | 15 | #include "common/fs/path_util.h" |
| 16 | #include "common/settings.h" | 16 | #include "common/settings.h" |
| 17 | #include "common/string_util.h" | 17 | #include "common/string_util.h" |
| 18 | #include "core/core.h" | 18 | #include "core/core.h" |
| @@ -34,9 +34,10 @@ constexpr std::array<u8, 107> backup_jpeg{ | |||
| 34 | }; | 34 | }; |
| 35 | 35 | ||
| 36 | QString GetImagePath(Common::UUID uuid) { | 36 | QString GetImagePath(Common::UUID uuid) { |
| 37 | const auto path = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + | 37 | const auto path = |
| 38 | "/system/save/8000000000000010/su/avators/" + uuid.FormatSwitch() + ".jpg"; | 38 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / |
| 39 | return QString::fromStdString(path); | 39 | fmt::format("system/save/8000000000000010/su/avators/{}.jpg", uuid.FormatSwitch()); |
| 40 | return QString::fromStdString(Common::FS::PathToUTF8String(path)); | ||
| 40 | } | 41 | } |
| 41 | 42 | ||
| 42 | QString GetAccountUsername(const Service::Account::ProfileManager& manager, Common::UUID uuid) { | 43 | QString GetAccountUsername(const Service::Account::ProfileManager& manager, Common::UUID uuid) { |
| @@ -281,8 +282,8 @@ void ConfigureProfileManager::SetUserImage() { | |||
| 281 | return; | 282 | return; |
| 282 | } | 283 | } |
| 283 | 284 | ||
| 284 | const auto raw_path = QString::fromStdString( | 285 | const auto raw_path = QString::fromStdString(Common::FS::PathToUTF8String( |
| 285 | Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + "/system/save/8000000000000010"); | 286 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/save/8000000000000010")); |
| 286 | const QFileInfo raw_info{raw_path}; | 287 | const QFileInfo raw_info{raw_path}; |
| 287 | if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) { | 288 | if (raw_info.exists() && !raw_info.isDir() && !QFile::remove(raw_path)) { |
| 288 | QMessageBox::warning(this, tr("Error deleting file"), | 289 | QMessageBox::warning(this, tr("Error deleting file"), |
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp index 85418f969..99a5df241 100644 --- a/src/yuzu/configuration/configure_system.cpp +++ b/src/yuzu/configuration/configure_system.cpp | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | #include <QGraphicsItem> | 10 | #include <QGraphicsItem> |
| 11 | #include <QMessageBox> | 11 | #include <QMessageBox> |
| 12 | #include "common/assert.h" | 12 | #include "common/assert.h" |
| 13 | #include "common/file_util.h" | ||
| 14 | #include "common/settings.h" | 13 | #include "common/settings.h" |
| 15 | #include "core/core.h" | 14 | #include "core/core.h" |
| 16 | #include "core/hle/service/time/time.h" | 15 | #include "core/hle/service/time/time.h" |
diff --git a/src/yuzu/configuration/configure_ui.cpp b/src/yuzu/configuration/configure_ui.cpp index 0cdaea8a4..0a28c87c0 100644 --- a/src/yuzu/configuration/configure_ui.cpp +++ b/src/yuzu/configuration/configure_ui.cpp | |||
| @@ -8,7 +8,7 @@ | |||
| 8 | 8 | ||
| 9 | #include <QDirIterator> | 9 | #include <QDirIterator> |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "common/file_util.h" | 11 | #include "common/fs/path_util.h" |
| 12 | #include "common/settings.h" | 12 | #include "common/settings.h" |
| 13 | #include "core/core.h" | 13 | #include "core/core.h" |
| 14 | #include "ui_configure_ui.h" | 14 | #include "ui_configure_ui.h" |
| @@ -62,13 +62,16 @@ ConfigureUi::ConfigureUi(QWidget* parent) : QWidget(parent), ui(new Ui::Configur | |||
| 62 | 62 | ||
| 63 | // Set screenshot path to user specification. | 63 | // Set screenshot path to user specification. |
| 64 | connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] { | 64 | connect(ui->screenshot_path_button, &QToolButton::pressed, this, [this] { |
| 65 | const QString& filename = | 65 | auto dir = |
| 66 | QFileDialog::getExistingDirectory(this, tr("Select Screenshots Path..."), | 66 | QFileDialog::getExistingDirectory(this, tr("Select Screenshots Path..."), |
| 67 | QString::fromStdString(Common::FS::GetUserPath( | 67 | QString::fromStdString(Common::FS::GetYuzuPathString( |
| 68 | Common::FS::UserPath::ScreenshotsDir))) + | 68 | Common::FS::YuzuPath::ScreenshotsDir))); |
| 69 | QDir::separator(); | 69 | if (!dir.isEmpty()) { |
| 70 | if (!filename.isEmpty()) { | 70 | if (dir.back() != QChar::fromLatin1('/')) { |
| 71 | ui->screenshot_path_edit->setText(filename); | 71 | dir.append(QChar::fromLatin1('/')); |
| 72 | } | ||
| 73 | |||
| 74 | ui->screenshot_path_edit->setText(dir); | ||
| 72 | } | 75 | } |
| 73 | }); | 76 | }); |
| 74 | } | 77 | } |
| @@ -84,7 +87,7 @@ void ConfigureUi::ApplyConfiguration() { | |||
| 84 | UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); | 87 | UISettings::values.row_2_text_id = ui->row_2_text_combobox->currentData().toUInt(); |
| 85 | 88 | ||
| 86 | UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked(); | 89 | UISettings::values.enable_screenshot_save_as = ui->enable_screenshot_save_as->isChecked(); |
| 87 | Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir, | 90 | Common::FS::SetYuzuPath(Common::FS::YuzuPath::ScreenshotsDir, |
| 88 | ui->screenshot_path_edit->text().toStdString()); | 91 | ui->screenshot_path_edit->text().toStdString()); |
| 89 | Core::System::GetInstance().ApplySettings(); | 92 | Core::System::GetInstance().ApplySettings(); |
| 90 | } | 93 | } |
| @@ -102,8 +105,8 @@ void ConfigureUi::SetConfiguration() { | |||
| 102 | ui->icon_size_combobox->findData(UISettings::values.icon_size)); | 105 | ui->icon_size_combobox->findData(UISettings::values.icon_size)); |
| 103 | 106 | ||
| 104 | ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as); | 107 | ui->enable_screenshot_save_as->setChecked(UISettings::values.enable_screenshot_save_as); |
| 105 | ui->screenshot_path_edit->setText( | 108 | ui->screenshot_path_edit->setText(QString::fromStdString( |
| 106 | QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir))); | 109 | Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir))); |
| 107 | } | 110 | } |
| 108 | 111 | ||
| 109 | void ConfigureUi::changeEvent(QEvent* event) { | 112 | void ConfigureUi::changeEvent(QEvent* event) { |
diff --git a/src/yuzu/configuration/input_profiles.cpp b/src/yuzu/configuration/input_profiles.cpp index e87aededb..333eeb84e 100644 --- a/src/yuzu/configuration/input_profiles.cpp +++ b/src/yuzu/configuration/input_profiles.cpp | |||
| @@ -4,8 +4,8 @@ | |||
| 4 | 4 | ||
| 5 | #include <fmt/format.h> | 5 | #include <fmt/format.h> |
| 6 | 6 | ||
| 7 | #include "common/common_paths.h" | 7 | #include "common/fs/fs.h" |
| 8 | #include "common/file_util.h" | 8 | #include "common/fs/path_util.h" |
| 9 | #include "yuzu/configuration/config.h" | 9 | #include "yuzu/configuration/config.h" |
| 10 | #include "yuzu/configuration/input_profiles.h" | 10 | #include "yuzu/configuration/input_profiles.h" |
| 11 | 11 | ||
| @@ -14,47 +14,43 @@ namespace FS = Common::FS; | |||
| 14 | namespace { | 14 | namespace { |
| 15 | 15 | ||
| 16 | bool ProfileExistsInFilesystem(std::string_view profile_name) { | 16 | bool ProfileExistsInFilesystem(std::string_view profile_name) { |
| 17 | return FS::Exists(fmt::format("{}input" DIR_SEP "{}.ini", | 17 | return FS::Exists(FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "input" / |
| 18 | FS::GetUserPath(FS::UserPath::ConfigDir), profile_name)); | 18 | fmt::format("{}.ini", profile_name)); |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | bool IsINI(std::string_view filename) { | 21 | bool IsINI(const std::filesystem::path& filename) { |
| 22 | const std::size_t index = filename.rfind('.'); | 22 | return filename.extension() == ".ini"; |
| 23 | |||
| 24 | if (index == std::string::npos) { | ||
| 25 | return false; | ||
| 26 | } | ||
| 27 | |||
| 28 | return filename.substr(index) == ".ini"; | ||
| 29 | } | 23 | } |
| 30 | 24 | ||
| 31 | std::string GetNameWithoutExtension(const std::string& filename) { | 25 | std::filesystem::path GetNameWithoutExtension(std::filesystem::path filename) { |
| 32 | const std::size_t index = filename.rfind('.'); | 26 | return filename.replace_extension(); |
| 33 | |||
| 34 | if (index == std::string::npos) { | ||
| 35 | return filename; | ||
| 36 | } | ||
| 37 | |||
| 38 | return filename.substr(0, index); | ||
| 39 | } | 27 | } |
| 40 | 28 | ||
| 41 | } // namespace | 29 | } // namespace |
| 42 | 30 | ||
| 43 | InputProfiles::InputProfiles() { | 31 | InputProfiles::InputProfiles() { |
| 44 | const std::string input_profile_loc = | 32 | const auto input_profile_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "input"; |
| 45 | fmt::format("{}input", FS::GetUserPath(FS::UserPath::ConfigDir)); | 33 | |
| 34 | if (!FS::IsDir(input_profile_loc)) { | ||
| 35 | return; | ||
| 36 | } | ||
| 46 | 37 | ||
| 47 | FS::ForeachDirectoryEntry( | 38 | FS::IterateDirEntries( |
| 48 | nullptr, input_profile_loc, | 39 | input_profile_loc, |
| 49 | [this](u64* entries_out, const std::string& directory, const std::string& filename) { | 40 | [this](const std::filesystem::path& full_path) { |
| 50 | if (IsINI(filename) && IsProfileNameValid(GetNameWithoutExtension(filename))) { | 41 | const auto filename = full_path.filename(); |
| 42 | const auto name_without_ext = | ||
| 43 | Common::FS::PathToUTF8String(GetNameWithoutExtension(filename)); | ||
| 44 | |||
| 45 | if (IsINI(filename) && IsProfileNameValid(name_without_ext)) { | ||
| 51 | map_profiles.insert_or_assign( | 46 | map_profiles.insert_or_assign( |
| 52 | GetNameWithoutExtension(filename), | 47 | name_without_ext, |
| 53 | std::make_unique<Config>(GetNameWithoutExtension(filename), | 48 | std::make_unique<Config>(name_without_ext, Config::ConfigType::InputProfile)); |
| 54 | Config::ConfigType::InputProfile)); | ||
| 55 | } | 49 | } |
| 50 | |||
| 56 | return true; | 51 | return true; |
| 57 | }); | 52 | }, |
| 53 | FS::DirEntryFilter::File); | ||
| 58 | } | 54 | } |
| 59 | 55 | ||
| 60 | InputProfiles::~InputProfiles() = default; | 56 | InputProfiles::~InputProfiles() = default; |
| @@ -96,7 +92,7 @@ bool InputProfiles::DeleteProfile(const std::string& profile_name) { | |||
| 96 | } | 92 | } |
| 97 | 93 | ||
| 98 | if (!ProfileExistsInFilesystem(profile_name) || | 94 | if (!ProfileExistsInFilesystem(profile_name) || |
| 99 | FS::Delete(map_profiles[profile_name]->GetConfigFilePath())) { | 95 | FS::RemoveFile(map_profiles[profile_name]->GetConfigFilePath())) { |
| 100 | map_profiles.erase(profile_name); | 96 | map_profiles.erase(profile_name); |
| 101 | } | 97 | } |
| 102 | 98 | ||
diff --git a/src/yuzu/game_list_worker.cpp b/src/yuzu/game_list_worker.cpp index 23643aea2..485045334 100644 --- a/src/yuzu/game_list_worker.cpp +++ b/src/yuzu/game_list_worker.cpp | |||
| @@ -12,8 +12,8 @@ | |||
| 12 | #include <QFileInfo> | 12 | #include <QFileInfo> |
| 13 | #include <QSettings> | 13 | #include <QSettings> |
| 14 | 14 | ||
| 15 | #include "common/common_paths.h" | 15 | #include "common/fs/fs.h" |
| 16 | #include "common/file_util.h" | 16 | #include "common/fs/path_util.h" |
| 17 | #include "core/core.h" | 17 | #include "core/core.h" |
| 18 | #include "core/file_sys/card_image.h" | 18 | #include "core/file_sys/card_image.h" |
| 19 | #include "core/file_sys/content_archive.h" | 19 | #include "core/file_sys/content_archive.h" |
| @@ -39,10 +39,11 @@ QString GetGameListCachedObject(const std::string& filename, const std::string& | |||
| 39 | return generator(); | 39 | return generator(); |
| 40 | } | 40 | } |
| 41 | 41 | ||
| 42 | const auto path = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP + | 42 | const auto path = |
| 43 | "game_list" + DIR_SEP + filename + '.' + ext; | 43 | Common::FS::PathToUTF8String(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / |
| 44 | "game_list" / fmt::format("{}.{}", filename, ext)); | ||
| 44 | 45 | ||
| 45 | Common::FS::CreateFullPath(path); | 46 | void(Common::FS::CreateParentDirs(path)); |
| 46 | 47 | ||
| 47 | if (!Common::FS::Exists(path)) { | 48 | if (!Common::FS::Exists(path)) { |
| 48 | const auto str = generator(); | 49 | const auto str = generator(); |
| @@ -70,12 +71,15 @@ std::pair<std::vector<u8>, std::string> GetGameListCachedObject( | |||
| 70 | return generator(); | 71 | return generator(); |
| 71 | } | 72 | } |
| 72 | 73 | ||
| 73 | const auto path1 = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP + | 74 | const auto game_list_dir = |
| 74 | "game_list" + DIR_SEP + filename + ".jpeg"; | 75 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / "game_list"; |
| 75 | const auto path2 = Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + DIR_SEP + | 76 | const auto jpeg_name = fmt::format("{}.jpeg", filename); |
| 76 | "game_list" + DIR_SEP + filename + ".appname.txt"; | 77 | const auto app_name = fmt::format("{}.appname.txt", filename); |
| 77 | 78 | ||
| 78 | Common::FS::CreateFullPath(path1); | 79 | const auto path1 = Common::FS::PathToUTF8String(game_list_dir / jpeg_name); |
| 80 | const auto path2 = Common::FS::PathToUTF8String(game_list_dir / app_name); | ||
| 81 | |||
| 82 | void(Common::FS::CreateParentDirs(path1)); | ||
| 79 | 83 | ||
| 80 | if (!Common::FS::Exists(path1) || !Common::FS::Exists(path2)) { | 84 | if (!Common::FS::Exists(path1) || !Common::FS::Exists(path2)) { |
| 81 | const auto [icon, nacp] = generator(); | 85 | const auto [icon, nacp] = generator(); |
| @@ -281,23 +285,27 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) { | |||
| 281 | } | 285 | } |
| 282 | } | 286 | } |
| 283 | 287 | ||
| 284 | void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, | 288 | void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan, |
| 285 | unsigned int recursion, GameListDir* parent_dir) { | 289 | GameListDir* parent_dir) { |
| 286 | auto& system = Core::System::GetInstance(); | 290 | auto& system = Core::System::GetInstance(); |
| 287 | 291 | ||
| 288 | const auto callback = [this, target, recursion, parent_dir, | 292 | const auto callback = [this, target, parent_dir, |
| 289 | &system](u64* num_entries_out, const std::string& directory, | 293 | &system](const std::filesystem::path& path) -> bool { |
| 290 | const std::string& virtual_name) -> bool { | ||
| 291 | if (stop_processing) { | 294 | if (stop_processing) { |
| 292 | // Breaks the callback loop. | 295 | // Breaks the callback loop. |
| 293 | return false; | 296 | return false; |
| 294 | } | 297 | } |
| 295 | 298 | ||
| 296 | const std::string physical_name = directory + DIR_SEP + virtual_name; | 299 | const auto physical_name = Common::FS::PathToUTF8String(path); |
| 297 | const bool is_dir = Common::FS::IsDirectory(physical_name); | 300 | const auto is_dir = Common::FS::IsDir(path); |
| 301 | |||
| 298 | if (!is_dir && | 302 | if (!is_dir && |
| 299 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { | 303 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { |
| 300 | const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); | 304 | const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read); |
| 305 | if (!file) { | ||
| 306 | return true; | ||
| 307 | } | ||
| 308 | |||
| 301 | auto loader = Loader::GetLoader(system, file); | 309 | auto loader = Loader::GetLoader(system, file); |
| 302 | if (!loader) { | 310 | if (!loader) { |
| 303 | return true; | 311 | return true; |
| @@ -343,15 +351,19 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa | |||
| 343 | compatibility_list, patch), | 351 | compatibility_list, patch), |
| 344 | parent_dir); | 352 | parent_dir); |
| 345 | } | 353 | } |
| 346 | } else if (is_dir && recursion > 0) { | 354 | } else if (is_dir) { |
| 347 | watch_list.append(QString::fromStdString(physical_name)); | 355 | watch_list.append(QString::fromStdString(physical_name)); |
| 348 | ScanFileSystem(target, physical_name, recursion - 1, parent_dir); | ||
| 349 | } | 356 | } |
| 350 | 357 | ||
| 351 | return true; | 358 | return true; |
| 352 | }; | 359 | }; |
| 353 | 360 | ||
| 354 | Common::FS::ForeachDirectoryEntry(nullptr, dir_path, callback); | 361 | if (deep_scan) { |
| 362 | Common::FS::IterateDirEntriesRecursively(dir_path, callback, | ||
| 363 | Common::FS::DirEntryFilter::All); | ||
| 364 | } else { | ||
| 365 | Common::FS::IterateDirEntries(dir_path, callback, Common::FS::DirEntryFilter::File); | ||
| 366 | } | ||
| 355 | } | 367 | } |
| 356 | 368 | ||
| 357 | void GameListWorker::run() { | 369 | void GameListWorker::run() { |
| @@ -376,9 +388,9 @@ void GameListWorker::run() { | |||
| 376 | auto* const game_list_dir = new GameListDir(game_dir); | 388 | auto* const game_list_dir = new GameListDir(game_dir); |
| 377 | emit DirEntryReady(game_list_dir); | 389 | emit DirEntryReady(game_list_dir); |
| 378 | ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), | 390 | ScanFileSystem(ScanTarget::FillManualContentProvider, game_dir.path.toStdString(), |
| 379 | game_dir.deep_scan ? 256 : 0, game_list_dir); | 391 | game_dir.deep_scan, game_list_dir); |
| 380 | ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), | 392 | ScanFileSystem(ScanTarget::PopulateGameList, game_dir.path.toStdString(), |
| 381 | game_dir.deep_scan ? 256 : 0, game_list_dir); | 393 | game_dir.deep_scan, game_list_dir); |
| 382 | } | 394 | } |
| 383 | } | 395 | } |
| 384 | 396 | ||
diff --git a/src/yuzu/game_list_worker.h b/src/yuzu/game_list_worker.h index 84e4e1b42..396bb2623 100644 --- a/src/yuzu/game_list_worker.h +++ b/src/yuzu/game_list_worker.h | |||
| @@ -70,7 +70,7 @@ private: | |||
| 70 | PopulateGameList, | 70 | PopulateGameList, |
| 71 | }; | 71 | }; |
| 72 | 72 | ||
| 73 | void ScanFileSystem(ScanTarget target, const std::string& dir_path, unsigned int recursion, | 73 | void ScanFileSystem(ScanTarget target, const std::string& dir_path, bool deep_scan, |
| 74 | GameListDir* parent_dir); | 74 | GameListDir* parent_dir); |
| 75 | 75 | ||
| 76 | std::shared_ptr<FileSys::VfsFilesystem> vfs; | 76 | std::shared_ptr<FileSys::VfsFilesystem> vfs; |
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index bc97f9d53..37ef62967 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -66,9 +66,10 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual | |||
| 66 | #include <QtConcurrent/QtConcurrent> | 66 | #include <QtConcurrent/QtConcurrent> |
| 67 | 67 | ||
| 68 | #include <fmt/format.h> | 68 | #include <fmt/format.h> |
| 69 | #include "common/common_paths.h" | ||
| 70 | #include "common/detached_tasks.h" | 69 | #include "common/detached_tasks.h" |
| 71 | #include "common/file_util.h" | 70 | #include "common/fs/fs.h" |
| 71 | #include "common/fs/fs_paths.h" | ||
| 72 | #include "common/fs/path_util.h" | ||
| 72 | #include "common/logging/backend.h" | 73 | #include "common/logging/backend.h" |
| 73 | #include "common/logging/filter.h" | 74 | #include "common/logging/filter.h" |
| 74 | #include "common/logging/log.h" | 75 | #include "common/logging/log.h" |
| @@ -178,36 +179,25 @@ static void InitializeLogging() { | |||
| 178 | log_filter.ParseFilterString(Settings::values.log_filter); | 179 | log_filter.ParseFilterString(Settings::values.log_filter); |
| 179 | Log::SetGlobalFilter(log_filter); | 180 | Log::SetGlobalFilter(log_filter); |
| 180 | 181 | ||
| 181 | const std::string& log_dir = FS::GetUserPath(FS::UserPath::LogDir); | 182 | const auto log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir); |
| 182 | FS::CreateFullPath(log_dir); | 183 | void(FS::CreateDir(log_dir)); |
| 183 | Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); | 184 | Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE)); |
| 184 | #ifdef _WIN32 | 185 | #ifdef _WIN32 |
| 185 | Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); | 186 | Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); |
| 186 | #endif | 187 | #endif |
| 187 | } | 188 | } |
| 188 | 189 | ||
| 189 | static void RemoveCachedContents() { | 190 | static void RemoveCachedContents() { |
| 190 | const auto offline_fonts = Common::FS::SanitizePath( | 191 | const auto cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir); |
| 191 | fmt::format("{}/fonts", Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)), | 192 | const auto offline_fonts = cache_dir / "fonts"; |
| 192 | Common::FS::DirectorySeparator::PlatformDefault); | 193 | const auto offline_manual = cache_dir / "offline_web_applet_manual"; |
| 193 | 194 | const auto offline_legal_information = cache_dir / "offline_web_applet_legal_information"; | |
| 194 | const auto offline_manual = Common::FS::SanitizePath( | 195 | const auto offline_system_data = cache_dir / "offline_web_applet_system_data"; |
| 195 | fmt::format("{}/offline_web_applet_manual", | 196 | |
| 196 | Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)), | 197 | void(Common::FS::RemoveDirRecursively(offline_fonts)); |
| 197 | Common::FS::DirectorySeparator::PlatformDefault); | 198 | void(Common::FS::RemoveDirRecursively(offline_manual)); |
| 198 | const auto offline_legal_information = Common::FS::SanitizePath( | 199 | void(Common::FS::RemoveDirRecursively(offline_legal_information)); |
| 199 | fmt::format("{}/offline_web_applet_legal_information", | 200 | void(Common::FS::RemoveDirRecursively(offline_system_data)); |
| 200 | Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)), | ||
| 201 | Common::FS::DirectorySeparator::PlatformDefault); | ||
| 202 | const auto offline_system_data = Common::FS::SanitizePath( | ||
| 203 | fmt::format("{}/offline_web_applet_system_data", | ||
| 204 | Common::FS::GetUserPath(Common::FS::UserPath::CacheDir)), | ||
| 205 | Common::FS::DirectorySeparator::PlatformDefault); | ||
| 206 | |||
| 207 | Common::FS::DeleteDirRecursively(offline_fonts); | ||
| 208 | Common::FS::DeleteDirRecursively(offline_manual); | ||
| 209 | Common::FS::DeleteDirRecursively(offline_legal_information); | ||
| 210 | Common::FS::DeleteDirRecursively(offline_system_data); | ||
| 211 | } | 201 | } |
| 212 | 202 | ||
| 213 | GMainWindow::GMainWindow() | 203 | GMainWindow::GMainWindow() |
| @@ -1418,7 +1408,8 @@ void GMainWindow::BootGame(const QString& filename, std::size_t program_index) { | |||
| 1418 | title_name = metadata.first->GetApplicationName(); | 1408 | title_name = metadata.first->GetApplicationName(); |
| 1419 | } | 1409 | } |
| 1420 | if (res != Loader::ResultStatus::Success || title_name.empty()) { | 1410 | if (res != Loader::ResultStatus::Success || title_name.empty()) { |
| 1421 | title_name = Common::FS::GetFilename(filename.toStdString()); | 1411 | title_name = Common::FS::PathToUTF8String( |
| 1412 | std::filesystem::path{filename.toStdU16String()}.filename()); | ||
| 1422 | } | 1413 | } |
| 1423 | LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); | 1414 | LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version); |
| 1424 | UpdateWindowTitle(title_name, title_version); | 1415 | UpdateWindowTitle(title_name, title_version); |
| @@ -1538,7 +1529,7 @@ void GMainWindow::OnGameListLoadFile(QString game_path) { | |||
| 1538 | 1529 | ||
| 1539 | void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, | 1530 | void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target, |
| 1540 | const std::string& game_path) { | 1531 | const std::string& game_path) { |
| 1541 | std::string path; | 1532 | std::filesystem::path path; |
| 1542 | QString open_target; | 1533 | QString open_target; |
| 1543 | auto& system = Core::System::GetInstance(); | 1534 | auto& system = Core::System::GetInstance(); |
| 1544 | 1535 | ||
| @@ -1567,7 +1558,7 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 1567 | switch (target) { | 1558 | switch (target) { |
| 1568 | case GameListOpenTarget::SaveData: { | 1559 | case GameListOpenTarget::SaveData: { |
| 1569 | open_target = tr("Save Data"); | 1560 | open_target = tr("Save Data"); |
| 1570 | const std::string nand_dir = Common::FS::GetUserPath(Common::FS::UserPath::NANDDir); | 1561 | const auto nand_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir); |
| 1571 | 1562 | ||
| 1572 | if (has_user_save) { | 1563 | if (has_user_save) { |
| 1573 | // User save data | 1564 | // User save data |
| @@ -1592,34 +1583,38 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 1592 | Service::Account::ProfileManager manager; | 1583 | Service::Account::ProfileManager manager; |
| 1593 | const auto user_id = manager.GetUser(static_cast<std::size_t>(index)); | 1584 | const auto user_id = manager.GetUser(static_cast<std::size_t>(index)); |
| 1594 | ASSERT(user_id); | 1585 | ASSERT(user_id); |
| 1595 | path = nand_dir + FileSys::SaveDataFactory::GetFullPath( | 1586 | |
| 1596 | system, FileSys::SaveDataSpaceId::NandUser, | 1587 | const auto user_save_data_path = FileSys::SaveDataFactory::GetFullPath( |
| 1597 | FileSys::SaveDataType::SaveData, program_id, user_id->uuid, 0); | 1588 | system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, |
| 1589 | program_id, user_id->uuid, 0); | ||
| 1590 | |||
| 1591 | path = Common::FS::ConcatPathSafe(nand_dir, user_save_data_path); | ||
| 1598 | } else { | 1592 | } else { |
| 1599 | // Device save data | 1593 | // Device save data |
| 1600 | path = nand_dir + FileSys::SaveDataFactory::GetFullPath( | 1594 | const auto device_save_data_path = FileSys::SaveDataFactory::GetFullPath( |
| 1601 | system, FileSys::SaveDataSpaceId::NandUser, | 1595 | system, FileSys::SaveDataSpaceId::NandUser, FileSys::SaveDataType::SaveData, |
| 1602 | FileSys::SaveDataType::SaveData, program_id, {}, 0); | 1596 | program_id, {}, 0); |
| 1597 | |||
| 1598 | path = Common::FS::ConcatPathSafe(nand_dir, device_save_data_path); | ||
| 1603 | } | 1599 | } |
| 1604 | 1600 | ||
| 1605 | if (!Common::FS::Exists(path)) { | 1601 | if (!Common::FS::CreateDirs(path)) { |
| 1606 | Common::FS::CreateFullPath(path); | 1602 | LOG_ERROR(Frontend, "Unable to create the directories for save data"); |
| 1607 | Common::FS::CreateDir(path); | ||
| 1608 | } | 1603 | } |
| 1609 | 1604 | ||
| 1610 | break; | 1605 | break; |
| 1611 | } | 1606 | } |
| 1612 | case GameListOpenTarget::ModData: { | 1607 | case GameListOpenTarget::ModData: { |
| 1613 | open_target = tr("Mod Data"); | 1608 | open_target = tr("Mod Data"); |
| 1614 | const auto load_dir = Common::FS::GetUserPath(Common::FS::UserPath::LoadDir); | 1609 | path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::LoadDir) / |
| 1615 | path = fmt::format("{}{:016X}", load_dir, program_id); | 1610 | fmt::format("{:016X}", program_id); |
| 1616 | break; | 1611 | break; |
| 1617 | } | 1612 | } |
| 1618 | default: | 1613 | default: |
| 1619 | UNIMPLEMENTED(); | 1614 | UNIMPLEMENTED(); |
| 1620 | } | 1615 | } |
| 1621 | 1616 | ||
| 1622 | const QString qpath = QString::fromStdString(path); | 1617 | const QString qpath = QString::fromStdString(Common::FS::PathToUTF8String(path)); |
| 1623 | const QDir dir(qpath); | 1618 | const QDir dir(qpath); |
| 1624 | if (!dir.exists()) { | 1619 | if (!dir.exists()) { |
| 1625 | QMessageBox::warning(this, tr("Error Opening %1 Folder").arg(open_target), | 1620 | QMessageBox::warning(this, tr("Error Opening %1 Folder").arg(open_target), |
| @@ -1632,33 +1627,35 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target | |||
| 1632 | } | 1627 | } |
| 1633 | 1628 | ||
| 1634 | void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { | 1629 | void GMainWindow::OnTransferableShaderCacheOpenFile(u64 program_id) { |
| 1635 | const QString shader_dir = | 1630 | const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir); |
| 1636 | QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)); | 1631 | const auto transferable_shader_cache_folder_path = shader_cache_dir / "opengl" / "transferable"; |
| 1637 | const QString transferable_shader_cache_folder_path = | 1632 | const auto transferable_shader_cache_file_path = |
| 1638 | shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); | 1633 | transferable_shader_cache_folder_path / fmt::format("{:016X}.bin", program_id); |
| 1639 | const QString transferable_shader_cache_file_path = | 1634 | |
| 1640 | transferable_shader_cache_folder_path + QDir::separator() + | 1635 | if (!Common::FS::Exists(transferable_shader_cache_file_path)) { |
| 1641 | QString::fromStdString(fmt::format("{:016X}.bin", program_id)); | ||
| 1642 | |||
| 1643 | if (!QFile::exists(transferable_shader_cache_file_path)) { | ||
| 1644 | QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"), | 1636 | QMessageBox::warning(this, tr("Error Opening Transferable Shader Cache"), |
| 1645 | tr("A shader cache for this title does not exist.")); | 1637 | tr("A shader cache for this title does not exist.")); |
| 1646 | return; | 1638 | return; |
| 1647 | } | 1639 | } |
| 1648 | 1640 | ||
| 1641 | const auto qt_shader_cache_folder_path = | ||
| 1642 | QString::fromStdString(Common::FS::PathToUTF8String(transferable_shader_cache_folder_path)); | ||
| 1643 | const auto qt_shader_cache_file_path = | ||
| 1644 | QString::fromStdString(Common::FS::PathToUTF8String(transferable_shader_cache_file_path)); | ||
| 1645 | |||
| 1649 | // Windows supports opening a folder with selecting a specified file in explorer. On every other | 1646 | // Windows supports opening a folder with selecting a specified file in explorer. On every other |
| 1650 | // OS we just open the transferable shader cache folder without preselecting the transferable | 1647 | // OS we just open the transferable shader cache folder without preselecting the transferable |
| 1651 | // shader cache file for the selected game. | 1648 | // shader cache file for the selected game. |
| 1652 | #if defined(Q_OS_WIN) | 1649 | #if defined(Q_OS_WIN) |
| 1653 | const QString explorer = QStringLiteral("explorer"); | 1650 | const QString explorer = QStringLiteral("explorer"); |
| 1654 | QStringList param; | 1651 | QStringList param; |
| 1655 | if (!QFileInfo(transferable_shader_cache_file_path).isDir()) { | 1652 | if (!QFileInfo(qt_shader_cache_file_path).isDir()) { |
| 1656 | param << QStringLiteral("/select,"); | 1653 | param << QStringLiteral("/select,"); |
| 1657 | } | 1654 | } |
| 1658 | param << QDir::toNativeSeparators(transferable_shader_cache_file_path); | 1655 | param << QDir::toNativeSeparators(qt_shader_cache_file_path); |
| 1659 | QProcess::startDetached(explorer, param); | 1656 | QProcess::startDetached(explorer, param); |
| 1660 | #else | 1657 | #else |
| 1661 | QDesktopServices::openUrl(QUrl::fromLocalFile(transferable_shader_cache_folder_path)); | 1658 | QDesktopServices::openUrl(QUrl::fromLocalFile(qt_shader_cache_folder_path)); |
| 1662 | #endif | 1659 | #endif |
| 1663 | } | 1660 | } |
| 1664 | 1661 | ||
| @@ -1736,8 +1733,8 @@ void GMainWindow::OnGameListRemoveInstalledEntry(u64 program_id, InstalledEntryT | |||
| 1736 | RemoveAddOnContent(program_id, entry_type); | 1733 | RemoveAddOnContent(program_id, entry_type); |
| 1737 | break; | 1734 | break; |
| 1738 | } | 1735 | } |
| 1739 | Common::FS::DeleteDirRecursively(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + | 1736 | void(Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / |
| 1740 | DIR_SEP + "game_list"); | 1737 | "game_list")); |
| 1741 | game_list->PopulateAsync(UISettings::values.game_dirs); | 1738 | game_list->PopulateAsync(UISettings::values.game_dirs); |
| 1742 | } | 1739 | } |
| 1743 | 1740 | ||
| @@ -1826,21 +1823,17 @@ void GMainWindow::OnGameListRemoveFile(u64 program_id, GameListRemoveTarget targ | |||
| 1826 | } | 1823 | } |
| 1827 | 1824 | ||
| 1828 | void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { | 1825 | void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { |
| 1829 | const QString shader_dir = | 1826 | const auto shader_cache_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ShaderDir); |
| 1830 | QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ShaderDir)); | 1827 | const auto transferable_shader_cache_file_path = |
| 1831 | const QString transferable_shader_cache_folder_path = | 1828 | shader_cache_dir / "opengl" / "transferable" / fmt::format("{:016X}.bin", program_id); |
| 1832 | shader_dir + QStringLiteral("opengl") + QDir::separator() + QStringLiteral("transferable"); | 1829 | |
| 1833 | const QString transferable_shader_cache_file_path = | 1830 | if (!Common::FS::Exists(transferable_shader_cache_file_path)) { |
| 1834 | transferable_shader_cache_folder_path + QDir::separator() + | ||
| 1835 | QString::fromStdString(fmt::format("{:016X}.bin", program_id)); | ||
| 1836 | |||
| 1837 | if (!QFile::exists(transferable_shader_cache_file_path)) { | ||
| 1838 | QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"), | 1831 | QMessageBox::warning(this, tr("Error Removing Transferable Shader Cache"), |
| 1839 | tr("A shader cache for this title does not exist.")); | 1832 | tr("A shader cache for this title does not exist.")); |
| 1840 | return; | 1833 | return; |
| 1841 | } | 1834 | } |
| 1842 | 1835 | ||
| 1843 | if (QFile::remove(transferable_shader_cache_file_path)) { | 1836 | if (Common::FS::RemoveFile(transferable_shader_cache_file_path)) { |
| 1844 | QMessageBox::information(this, tr("Successfully Removed"), | 1837 | QMessageBox::information(this, tr("Successfully Removed"), |
| 1845 | tr("Successfully removed the transferable shader cache.")); | 1838 | tr("Successfully removed the transferable shader cache.")); |
| 1846 | } else { | 1839 | } else { |
| @@ -1850,19 +1843,16 @@ void GMainWindow::RemoveTransferableShaderCache(u64 program_id) { | |||
| 1850 | } | 1843 | } |
| 1851 | 1844 | ||
| 1852 | void GMainWindow::RemoveCustomConfiguration(u64 program_id) { | 1845 | void GMainWindow::RemoveCustomConfiguration(u64 program_id) { |
| 1853 | const QString config_dir = | 1846 | const auto custom_config_file_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir) / |
| 1854 | QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir)); | 1847 | "custom" / fmt::format("{:016X}.ini", program_id); |
| 1855 | const QString custom_config_file_path = | ||
| 1856 | config_dir + QStringLiteral("custom") + QDir::separator() + | ||
| 1857 | QString::fromStdString(fmt::format("{:016X}.ini", program_id)); | ||
| 1858 | 1848 | ||
| 1859 | if (!QFile::exists(custom_config_file_path)) { | 1849 | if (!Common::FS::Exists(custom_config_file_path)) { |
| 1860 | QMessageBox::warning(this, tr("Error Removing Custom Configuration"), | 1850 | QMessageBox::warning(this, tr("Error Removing Custom Configuration"), |
| 1861 | tr("A custom configuration for this title does not exist.")); | 1851 | tr("A custom configuration for this title does not exist.")); |
| 1862 | return; | 1852 | return; |
| 1863 | } | 1853 | } |
| 1864 | 1854 | ||
| 1865 | if (QFile::remove(custom_config_file_path)) { | 1855 | if (Common::FS::RemoveFile(custom_config_file_path)) { |
| 1866 | QMessageBox::information(this, tr("Successfully Removed"), | 1856 | QMessageBox::information(this, tr("Successfully Removed"), |
| 1867 | tr("Successfully removed the custom game configuration.")); | 1857 | tr("Successfully removed the custom game configuration.")); |
| 1868 | } else { | 1858 | } else { |
| @@ -1899,8 +1889,10 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa | |||
| 1899 | return; | 1889 | return; |
| 1900 | } | 1890 | } |
| 1901 | 1891 | ||
| 1902 | const auto path = fmt::format( | 1892 | const auto dump_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::DumpDir); |
| 1903 | "{}{:016X}/romfs", Common::FS::GetUserPath(Common::FS::UserPath::DumpDir), *romfs_title_id); | 1893 | const auto romfs_dir = fmt::format("{:016X}/romfs", *romfs_title_id); |
| 1894 | |||
| 1895 | const auto path = Common::FS::PathToUTF8String(dump_dir / romfs_dir); | ||
| 1904 | 1896 | ||
| 1905 | FileSys::VirtualFile romfs; | 1897 | FileSys::VirtualFile romfs; |
| 1906 | 1898 | ||
| @@ -1978,24 +1970,29 @@ void GMainWindow::OnGameListNavigateToGamedbEntry(u64 program_id, | |||
| 1978 | } | 1970 | } |
| 1979 | 1971 | ||
| 1980 | void GMainWindow::OnGameListOpenDirectory(const QString& directory) { | 1972 | void GMainWindow::OnGameListOpenDirectory(const QString& directory) { |
| 1981 | QString path; | 1973 | std::filesystem::path fs_path; |
| 1982 | if (directory == QStringLiteral("SDMC")) { | 1974 | if (directory == QStringLiteral("SDMC")) { |
| 1983 | path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::SDMCDir) + | 1975 | fs_path = |
| 1984 | "Nintendo/Contents/registered"); | 1976 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::SDMCDir) / "Nintendo/Contents/registered"; |
| 1985 | } else if (directory == QStringLiteral("UserNAND")) { | 1977 | } else if (directory == QStringLiteral("UserNAND")) { |
| 1986 | path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + | 1978 | fs_path = |
| 1987 | "user/Contents/registered"); | 1979 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "user/Contents/registered"; |
| 1988 | } else if (directory == QStringLiteral("SysNAND")) { | 1980 | } else if (directory == QStringLiteral("SysNAND")) { |
| 1989 | path = QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::NANDDir) + | 1981 | fs_path = |
| 1990 | "system/Contents/registered"); | 1982 | Common::FS::GetYuzuPath(Common::FS::YuzuPath::NANDDir) / "system/Contents/registered"; |
| 1991 | } else { | 1983 | } else { |
| 1992 | path = directory; | 1984 | fs_path = directory.toStdString(); |
| 1993 | } | 1985 | } |
| 1994 | if (!QFileInfo::exists(path)) { | 1986 | |
| 1995 | QMessageBox::critical(this, tr("Error Opening %1").arg(path), tr("Folder does not exist!")); | 1987 | const auto qt_path = QString::fromStdString(Common::FS::PathToUTF8String(fs_path)); |
| 1988 | |||
| 1989 | if (!Common::FS::IsDir(fs_path)) { | ||
| 1990 | QMessageBox::critical(this, tr("Error Opening %1").arg(qt_path), | ||
| 1991 | tr("Folder does not exist!")); | ||
| 1996 | return; | 1992 | return; |
| 1997 | } | 1993 | } |
| 1998 | QDesktopServices::openUrl(QUrl::fromLocalFile(path)); | 1994 | |
| 1995 | QDesktopServices::openUrl(QUrl::fromLocalFile(qt_path)); | ||
| 1999 | } | 1996 | } |
| 2000 | 1997 | ||
| 2001 | void GMainWindow::OnGameListAddDirectory() { | 1998 | void GMainWindow::OnGameListAddDirectory() { |
| @@ -2189,8 +2186,8 @@ void GMainWindow::OnMenuInstallToNAND() { | |||
| 2189 | : tr("%n file(s) failed to install\n", "", failed_files.size())); | 2186 | : tr("%n file(s) failed to install\n", "", failed_files.size())); |
| 2190 | 2187 | ||
| 2191 | QMessageBox::information(this, tr("Install Results"), install_results); | 2188 | QMessageBox::information(this, tr("Install Results"), install_results); |
| 2192 | Common::FS::DeleteDirRecursively(Common::FS::GetUserPath(Common::FS::UserPath::CacheDir) + | 2189 | void(Common::FS::RemoveDirRecursively(Common::FS::GetYuzuPath(Common::FS::YuzuPath::CacheDir) / |
| 2193 | DIR_SEP + "game_list"); | 2190 | "game_list")); |
| 2194 | game_list->PopulateAsync(UISettings::values.game_dirs); | 2191 | game_list->PopulateAsync(UISettings::values.game_dirs); |
| 2195 | ui.action_Install_File_NAND->setEnabled(true); | 2192 | ui.action_Install_File_NAND->setEnabled(true); |
| 2196 | } | 2193 | } |
| @@ -2706,7 +2703,7 @@ void GMainWindow::LoadAmiibo(const QString& filename) { | |||
| 2706 | 2703 | ||
| 2707 | void GMainWindow::OnOpenYuzuFolder() { | 2704 | void GMainWindow::OnOpenYuzuFolder() { |
| 2708 | QDesktopServices::openUrl(QUrl::fromLocalFile( | 2705 | QDesktopServices::openUrl(QUrl::fromLocalFile( |
| 2709 | QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::UserDir)))); | 2706 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::YuzuDir)))); |
| 2710 | } | 2707 | } |
| 2711 | 2708 | ||
| 2712 | void GMainWindow::OnAbout() { | 2709 | void GMainWindow::OnAbout() { |
| @@ -2728,7 +2725,7 @@ void GMainWindow::OnCaptureScreenshot() { | |||
| 2728 | 2725 | ||
| 2729 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); | 2726 | const u64 title_id = Core::System::GetInstance().CurrentProcess()->GetTitleID(); |
| 2730 | const auto screenshot_path = | 2727 | const auto screenshot_path = |
| 2731 | QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ScreenshotsDir)); | 2728 | QString::fromStdString(Common::FS::GetYuzuPathString(Common::FS::YuzuPath::ScreenshotsDir)); |
| 2732 | const auto date = | 2729 | const auto date = |
| 2733 | QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz")); | 2730 | QDateTime::currentDateTime().toString(QStringLiteral("yyyy-MM-dd_hh-mm-ss-zzz")); |
| 2734 | QString filename = QStringLiteral("%1%2_%3.png") | 2731 | QString filename = QStringLiteral("%1%2_%3.png") |
| @@ -2757,23 +2754,26 @@ void GMainWindow::OnCaptureScreenshot() { | |||
| 2757 | 2754 | ||
| 2758 | // TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant | 2755 | // TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant |
| 2759 | void GMainWindow::MigrateConfigFiles() { | 2756 | void GMainWindow::MigrateConfigFiles() { |
| 2760 | const std::string& config_dir_str = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir); | 2757 | const auto config_dir_fs_path = Common::FS::GetYuzuPath(Common::FS::YuzuPath::ConfigDir); |
| 2761 | const QDir config_dir = QDir(QString::fromStdString(config_dir_str)); | 2758 | const QDir config_dir = |
| 2759 | QDir(QString::fromStdString(Common::FS::PathToUTF8String(config_dir_fs_path))); | ||
| 2762 | const QStringList config_dir_list = config_dir.entryList(QStringList(QStringLiteral("*.ini"))); | 2760 | const QStringList config_dir_list = config_dir.entryList(QStringList(QStringLiteral("*.ini"))); |
| 2763 | 2761 | ||
| 2764 | Common::FS::CreateFullPath(fmt::format("{}custom" DIR_SEP, config_dir_str)); | 2762 | if (!Common::FS::CreateDirs(config_dir_fs_path / "custom")) { |
| 2765 | for (QStringList::const_iterator it = config_dir_list.constBegin(); | 2763 | LOG_ERROR(Frontend, "Failed to create new config file directory"); |
| 2766 | it != config_dir_list.constEnd(); ++it) { | 2764 | } |
| 2765 | |||
| 2766 | for (auto it = config_dir_list.constBegin(); it != config_dir_list.constEnd(); ++it) { | ||
| 2767 | const auto filename = it->toStdString(); | 2767 | const auto filename = it->toStdString(); |
| 2768 | if (filename.find_first_not_of("0123456789abcdefACBDEF", 0) < 16) { | 2768 | if (filename.find_first_not_of("0123456789abcdefACBDEF", 0) < 16) { |
| 2769 | continue; | 2769 | continue; |
| 2770 | } | 2770 | } |
| 2771 | const auto origin = fmt::format("{}{}", config_dir_str, filename); | 2771 | const auto origin = config_dir_fs_path / filename; |
| 2772 | const auto destination = fmt::format("{}custom" DIR_SEP "{}", config_dir_str, filename); | 2772 | const auto destination = config_dir_fs_path / "custom" / filename; |
| 2773 | LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination); | 2773 | LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination); |
| 2774 | if (!Common::FS::Rename(origin, destination)) { | 2774 | if (!Common::FS::RenameFile(origin, destination)) { |
| 2775 | // Delete the old config file if one already exists in the new location. | 2775 | // Delete the old config file if one already exists in the new location. |
| 2776 | Common::FS::Delete(origin); | 2776 | void(Common::FS::RemoveFile(origin)); |
| 2777 | } | 2777 | } |
| 2778 | } | 2778 | } |
| 2779 | } | 2779 | } |
| @@ -2965,18 +2965,16 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) { | |||
| 2965 | if (res == QMessageBox::Cancel) | 2965 | if (res == QMessageBox::Cancel) |
| 2966 | return; | 2966 | return; |
| 2967 | 2967 | ||
| 2968 | Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) + | 2968 | const auto keys_dir = Common::FS::GetYuzuPath(Common::FS::YuzuPath::KeysDir); |
| 2969 | "prod.keys_autogenerated"); | 2969 | |
| 2970 | Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) + | 2970 | void(Common::FS::RemoveFile(keys_dir / "prod.keys_autogenerated")); |
| 2971 | "console.keys_autogenerated"); | 2971 | void(Common::FS::RemoveFile(keys_dir / "console.keys_autogenerated")); |
| 2972 | Common::FS::Delete(Common::FS::GetUserPath(Common::FS::UserPath::KeysDir) + | 2972 | void(Common::FS::RemoveFile(keys_dir / "title.keys_autogenerated")); |
| 2973 | "title.keys_autogenerated"); | ||
| 2974 | } | 2973 | } |
| 2975 | 2974 | ||
| 2976 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); | 2975 | Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance(); |
| 2977 | if (keys.BaseDeriveNecessary()) { | 2976 | if (keys.BaseDeriveNecessary()) { |
| 2978 | Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory( | 2977 | Core::Crypto::PartitionDataManager pdm{vfs->OpenDirectory("", FileSys::Mode::Read)}; |
| 2979 | Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), FileSys::Mode::Read)}; | ||
| 2980 | 2978 | ||
| 2981 | const auto function = [this, &keys, &pdm] { | 2979 | const auto function = [this, &keys, &pdm] { |
| 2982 | keys.PopulateFromPartitionData(pdm); | 2980 | keys.PopulateFromPartitionData(pdm); |
| @@ -3289,12 +3287,17 @@ int main(int argc, char* argv[]) { | |||
| 3289 | QCoreApplication::setOrganizationName(QStringLiteral("yuzu team")); | 3287 | QCoreApplication::setOrganizationName(QStringLiteral("yuzu team")); |
| 3290 | QCoreApplication::setApplicationName(QStringLiteral("yuzu")); | 3288 | QCoreApplication::setApplicationName(QStringLiteral("yuzu")); |
| 3291 | 3289 | ||
| 3290 | #ifdef _WIN32 | ||
| 3291 | // Increases the maximum open file limit to 4096 | ||
| 3292 | _setmaxstdio(4096); | ||
| 3293 | #endif | ||
| 3294 | |||
| 3292 | #ifdef __APPLE__ | 3295 | #ifdef __APPLE__ |
| 3293 | // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/". | 3296 | // If you start a bundle (binary) on OSX without the Terminal, the working directory is "/". |
| 3294 | // But since we require the working directory to be the executable path for the location of | 3297 | // But since we require the working directory to be the executable path for the location of |
| 3295 | // the user folder in the Qt Frontend, we need to cd into that working directory | 3298 | // the user folder in the Qt Frontend, we need to cd into that working directory |
| 3296 | const std::string bin_path = Common::FS::GetBundleDirectory() + DIR_SEP + ".."; | 3299 | const auto bin_path = Common::FS::GetBundleDirectory() / ".."; |
| 3297 | chdir(bin_path.c_str()); | 3300 | chdir(Common::FS::PathToUTF8String(bin_path).c_str()); |
| 3298 | #endif | 3301 | #endif |
| 3299 | 3302 | ||
| 3300 | #ifdef __linux__ | 3303 | #ifdef __linux__ |
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp index 38d896d65..a2ab69cdd 100644 --- a/src/yuzu_cmd/config.cpp +++ b/src/yuzu_cmd/config.cpp | |||
| @@ -16,7 +16,9 @@ | |||
| 16 | #endif | 16 | #endif |
| 17 | 17 | ||
| 18 | #include <inih/cpp/INIReader.h> | 18 | #include <inih/cpp/INIReader.h> |
| 19 | #include "common/file_util.h" | 19 | #include "common/fs/file.h" |
| 20 | #include "common/fs/fs.h" | ||
| 21 | #include "common/fs/path_util.h" | ||
| 20 | #include "common/logging/log.h" | 22 | #include "common/logging/log.h" |
| 21 | #include "common/param_package.h" | 23 | #include "common/param_package.h" |
| 22 | #include "common/settings.h" | 24 | #include "common/settings.h" |
| @@ -30,8 +32,8 @@ namespace FS = Common::FS; | |||
| 30 | 32 | ||
| 31 | Config::Config() { | 33 | Config::Config() { |
| 32 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. | 34 | // TODO: Don't hardcode the path; let the frontend decide where to put the config files. |
| 33 | sdl2_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + "sdl2-config.ini"; | 35 | sdl2_config_loc = FS::GetYuzuPath(FS::YuzuPath::ConfigDir) / "sdl2-config.ini"; |
| 34 | sdl2_config = std::make_unique<INIReader>(sdl2_config_loc); | 36 | sdl2_config = std::make_unique<INIReader>(FS::PathToUTF8String(sdl2_config_loc)); |
| 35 | 37 | ||
| 36 | Reload(); | 38 | Reload(); |
| 37 | } | 39 | } |
| @@ -39,20 +41,23 @@ Config::Config() { | |||
| 39 | Config::~Config() = default; | 41 | Config::~Config() = default; |
| 40 | 42 | ||
| 41 | bool Config::LoadINI(const std::string& default_contents, bool retry) { | 43 | bool Config::LoadINI(const std::string& default_contents, bool retry) { |
| 42 | const std::string& location = this->sdl2_config_loc; | 44 | const auto config_loc_str = FS::PathToUTF8String(sdl2_config_loc); |
| 43 | if (sdl2_config->ParseError() < 0) { | 45 | if (sdl2_config->ParseError() < 0) { |
| 44 | if (retry) { | 46 | if (retry) { |
| 45 | LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", location); | 47 | LOG_WARNING(Config, "Failed to load {}. Creating file from defaults...", |
| 46 | FS::CreateFullPath(location); | 48 | config_loc_str); |
| 47 | FS::WriteStringToFile(true, location, default_contents); | 49 | |
| 48 | sdl2_config = std::make_unique<INIReader>(location); // Reopen file | 50 | void(FS::CreateParentDir(sdl2_config_loc)); |
| 51 | void(FS::WriteStringToFile(sdl2_config_loc, FS::FileType::TextFile, default_contents)); | ||
| 52 | |||
| 53 | sdl2_config = std::make_unique<INIReader>(config_loc_str); | ||
| 49 | 54 | ||
| 50 | return LoadINI(default_contents, false); | 55 | return LoadINI(default_contents, false); |
| 51 | } | 56 | } |
| 52 | LOG_ERROR(Config, "Failed."); | 57 | LOG_ERROR(Config, "Failed."); |
| 53 | return false; | 58 | return false; |
| 54 | } | 59 | } |
| 55 | LOG_INFO(Config, "Successfully loaded {}", location); | 60 | LOG_INFO(Config, "Successfully loaded {}", config_loc_str); |
| 56 | return true; | 61 | return true; |
| 57 | } | 62 | } |
| 58 | 63 | ||
| @@ -327,18 +332,18 @@ void Config::ReadValues() { | |||
| 327 | // Data Storage | 332 | // Data Storage |
| 328 | Settings::values.use_virtual_sd = | 333 | Settings::values.use_virtual_sd = |
| 329 | sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); | 334 | sdl2_config->GetBoolean("Data Storage", "use_virtual_sd", true); |
| 330 | FS::GetUserPath( | 335 | FS::SetYuzuPath(FS::YuzuPath::NANDDir, |
| 331 | FS::UserPath::NANDDir, | 336 | sdl2_config->Get("Data Storage", "nand_directory", |
| 332 | sdl2_config->Get("Data Storage", "nand_directory", FS::GetUserPath(FS::UserPath::NANDDir))); | 337 | FS::GetYuzuPathString(FS::YuzuPath::NANDDir))); |
| 333 | FS::GetUserPath( | 338 | FS::SetYuzuPath(FS::YuzuPath::SDMCDir, |
| 334 | FS::UserPath::SDMCDir, | 339 | sdl2_config->Get("Data Storage", "sdmc_directory", |
| 335 | sdl2_config->Get("Data Storage", "sdmc_directory", FS::GetUserPath(FS::UserPath::SDMCDir))); | 340 | FS::GetYuzuPathString(FS::YuzuPath::SDMCDir))); |
| 336 | FS::GetUserPath( | 341 | FS::SetYuzuPath(FS::YuzuPath::LoadDir, |
| 337 | FS::UserPath::LoadDir, | 342 | sdl2_config->Get("Data Storage", "load_directory", |
| 338 | sdl2_config->Get("Data Storage", "load_directory", FS::GetUserPath(FS::UserPath::LoadDir))); | 343 | FS::GetYuzuPathString(FS::YuzuPath::LoadDir))); |
| 339 | FS::GetUserPath( | 344 | FS::SetYuzuPath(FS::YuzuPath::DumpDir, |
| 340 | FS::UserPath::DumpDir, | 345 | sdl2_config->Get("Data Storage", "dump_directory", |
| 341 | sdl2_config->Get("Data Storage", "dump_directory", FS::GetUserPath(FS::UserPath::DumpDir))); | 346 | FS::GetYuzuPathString(FS::YuzuPath::DumpDir))); |
| 342 | Settings::values.gamecard_inserted = | 347 | Settings::values.gamecard_inserted = |
| 343 | sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false); | 348 | sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false); |
| 344 | Settings::values.gamecard_current_game = | 349 | Settings::values.gamecard_current_game = |
diff --git a/src/yuzu_cmd/config.h b/src/yuzu_cmd/config.h index abc90f642..807199278 100644 --- a/src/yuzu_cmd/config.h +++ b/src/yuzu_cmd/config.h | |||
| @@ -4,6 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <filesystem> | ||
| 7 | #include <memory> | 8 | #include <memory> |
| 8 | #include <string> | 9 | #include <string> |
| 9 | 10 | ||
| @@ -11,7 +12,7 @@ class INIReader; | |||
| 11 | 12 | ||
| 12 | class Config { | 13 | class Config { |
| 13 | std::unique_ptr<INIReader> sdl2_config; | 14 | std::unique_ptr<INIReader> sdl2_config; |
| 14 | std::string sdl2_config_loc; | 15 | std::filesystem::path sdl2_config_loc; |
| 15 | 16 | ||
| 16 | bool LoadINI(const std::string& default_contents = "", bool retry = true); | 17 | bool LoadINI(const std::string& default_contents = "", bool retry = true); |
| 17 | void ReadValues(); | 18 | void ReadValues(); |
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index e2812ca61..584967f5c 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -10,9 +10,10 @@ | |||
| 10 | 10 | ||
| 11 | #include <fmt/ostream.h> | 11 | #include <fmt/ostream.h> |
| 12 | 12 | ||
| 13 | #include "common/common_paths.h" | ||
| 14 | #include "common/detached_tasks.h" | 13 | #include "common/detached_tasks.h" |
| 15 | #include "common/file_util.h" | 14 | #include "common/fs/fs.h" |
| 15 | #include "common/fs/fs_paths.h" | ||
| 16 | #include "common/fs/path_util.h" | ||
| 16 | #include "common/logging/backend.h" | 17 | #include "common/logging/backend.h" |
| 17 | #include "common/logging/filter.h" | 18 | #include "common/logging/filter.h" |
| 18 | #include "common/logging/log.h" | 19 | #include "common/logging/log.h" |
| @@ -82,9 +83,9 @@ static void InitializeLogging() { | |||
| 82 | 83 | ||
| 83 | Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); | 84 | Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); |
| 84 | 85 | ||
| 85 | const std::string& log_dir = FS::GetUserPath(FS::UserPath::LogDir); | 86 | const auto& log_dir = FS::GetYuzuPath(FS::YuzuPath::LogDir); |
| 86 | FS::CreateFullPath(log_dir); | 87 | void(FS::CreateDir(log_dir)); |
| 87 | Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir + LOG_FILE)); | 88 | Log::AddBackend(std::make_unique<Log::FileBackend>(log_dir / LOG_FILE)); |
| 88 | #ifdef _WIN32 | 89 | #ifdef _WIN32 |
| 89 | Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); | 90 | Log::AddBackend(std::make_unique<Log::DebuggerBackend>()); |
| 90 | #endif | 91 | #endif |