summaryrefslogtreecommitdiff
path: root/src/common/file_util.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/file_util.cpp')
-rw-r--r--src/common/file_util.cpp133
1 files changed, 91 insertions, 42 deletions
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 35eee0096..18fbfa25b 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -74,7 +74,7 @@
74// This namespace has various generic functions related to files and paths. 74// This namespace has various generic functions related to files and paths.
75// The code still needs a ton of cleanup. 75// The code still needs a ton of cleanup.
76// REMEMBER: strdup considered harmful! 76// REMEMBER: strdup considered harmful!
77namespace FileUtil { 77namespace Common::FS {
78 78
79// Remove any ending forward slashes from directory paths 79// Remove any ending forward slashes from directory paths
80// Modifies argument. 80// Modifies argument.
@@ -196,7 +196,7 @@ bool CreateFullPath(const std::string& fullPath) {
196 int panicCounter = 100; 196 int panicCounter = 100;
197 LOG_TRACE(Common_Filesystem, "path {}", fullPath); 197 LOG_TRACE(Common_Filesystem, "path {}", fullPath);
198 198
199 if (FileUtil::Exists(fullPath)) { 199 if (Exists(fullPath)) {
200 LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath); 200 LOG_DEBUG(Common_Filesystem, "path exists {}", fullPath);
201 return true; 201 return true;
202 } 202 }
@@ -212,7 +212,7 @@ bool CreateFullPath(const std::string& fullPath) {
212 212
213 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("") 213 // Include the '/' so the first call is CreateDir("/") rather than CreateDir("")
214 std::string const subPath(fullPath.substr(0, position + 1)); 214 std::string const subPath(fullPath.substr(0, position + 1));
215 if (!FileUtil::IsDirectory(subPath) && !FileUtil::CreateDir(subPath)) { 215 if (!IsDirectory(subPath) && !CreateDir(subPath)) {
216 LOG_ERROR(Common, "CreateFullPath: directory creation failed"); 216 LOG_ERROR(Common, "CreateFullPath: directory creation failed");
217 return false; 217 return false;
218 } 218 }
@@ -231,7 +231,7 @@ bool DeleteDir(const std::string& filename) {
231 LOG_TRACE(Common_Filesystem, "directory {}", filename); 231 LOG_TRACE(Common_Filesystem, "directory {}", filename);
232 232
233 // check if a directory 233 // check if a directory
234 if (!FileUtil::IsDirectory(filename)) { 234 if (!IsDirectory(filename)) {
235 LOG_ERROR(Common_Filesystem, "Not a directory {}", filename); 235 LOG_ERROR(Common_Filesystem, "Not a directory {}", filename);
236 return false; 236 return false;
237 } 237 }
@@ -371,7 +371,7 @@ u64 GetSize(FILE* f) {
371bool CreateEmptyFile(const std::string& filename) { 371bool CreateEmptyFile(const std::string& filename) {
372 LOG_TRACE(Common_Filesystem, "{}", filename); 372 LOG_TRACE(Common_Filesystem, "{}", filename);
373 373
374 if (!FileUtil::IOFile(filename, "wb").IsOpen()) { 374 if (!IOFile(filename, "wb").IsOpen()) {
375 LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); 375 LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg());
376 return false; 376 return false;
377 } 377 }
@@ -472,13 +472,14 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
472} 472}
473 473
474bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) { 474bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
475 const auto callback = [recursion](u64* num_entries_out, const std::string& directory, 475 const auto callback = [recursion](u64*, const std::string& directory,
476 const std::string& virtual_name) -> bool { 476 const std::string& virtual_name) {
477 std::string new_path = directory + DIR_SEP_CHR + virtual_name; 477 const std::string new_path = directory + DIR_SEP_CHR + virtual_name;
478 478
479 if (IsDirectory(new_path)) { 479 if (IsDirectory(new_path)) {
480 if (recursion == 0) 480 if (recursion == 0) {
481 return false; 481 return false;
482 }
482 return DeleteDirRecursively(new_path, recursion - 1); 483 return DeleteDirRecursively(new_path, recursion - 1);
483 } 484 }
484 return Delete(new_path); 485 return Delete(new_path);
@@ -488,29 +489,35 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
488 return false; 489 return false;
489 490
490 // Delete the outermost directory 491 // Delete the outermost directory
491 FileUtil::DeleteDir(directory); 492 DeleteDir(directory);
492 return true; 493 return true;
493} 494}
494 495
495void CopyDir(const std::string& source_path, const std::string& dest_path) { 496void CopyDir([[maybe_unused]] const std::string& source_path,
497 [[maybe_unused]] const std::string& dest_path) {
496#ifndef _WIN32 498#ifndef _WIN32
497 if (source_path == dest_path) 499 if (source_path == dest_path) {
498 return; 500 return;
499 if (!FileUtil::Exists(source_path)) 501 }
502 if (!Exists(source_path)) {
500 return; 503 return;
501 if (!FileUtil::Exists(dest_path)) 504 }
502 FileUtil::CreateFullPath(dest_path); 505 if (!Exists(dest_path)) {
506 CreateFullPath(dest_path);
507 }
503 508
504 DIR* dirp = opendir(source_path.c_str()); 509 DIR* dirp = opendir(source_path.c_str());
505 if (!dirp) 510 if (!dirp) {
506 return; 511 return;
512 }
507 513
508 while (struct dirent* result = readdir(dirp)) { 514 while (struct dirent* result = readdir(dirp)) {
509 const std::string virtualName(result->d_name); 515 const std::string virtualName(result->d_name);
510 // check for "." and ".." 516 // check for "." and ".."
511 if (((virtualName[0] == '.') && (virtualName[1] == '\0')) || 517 if (((virtualName[0] == '.') && (virtualName[1] == '\0')) ||
512 ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) 518 ((virtualName[0] == '.') && (virtualName[1] == '.') && (virtualName[2] == '\0'))) {
513 continue; 519 continue;
520 }
514 521
515 std::string source, dest; 522 std::string source, dest;
516 source = source_path + virtualName; 523 source = source_path + virtualName;
@@ -518,11 +525,13 @@ void CopyDir(const std::string& source_path, const std::string& dest_path) {
518 if (IsDirectory(source)) { 525 if (IsDirectory(source)) {
519 source += '/'; 526 source += '/';
520 dest += '/'; 527 dest += '/';
521 if (!FileUtil::Exists(dest)) 528 if (!Exists(dest)) {
522 FileUtil::CreateFullPath(dest); 529 CreateFullPath(dest);
530 }
523 CopyDir(source, dest); 531 CopyDir(source, dest);
524 } else if (!FileUtil::Exists(dest)) 532 } else if (!Exists(dest)) {
525 FileUtil::Copy(source, dest); 533 Copy(source, dest);
534 }
526 } 535 }
527 closedir(dirp); 536 closedir(dirp);
528#endif 537#endif
@@ -538,7 +547,7 @@ std::optional<std::string> GetCurrentDir() {
538 if (!dir) { 547 if (!dir) {
539#endif 548#endif
540 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg()); 549 LOG_ERROR(Common_Filesystem, "GetCurrentDirectory failed: {}", GetLastErrorMsg());
541 return {}; 550 return std::nullopt;
542 } 551 }
543#ifdef _WIN32 552#ifdef _WIN32
544 std::string strDir = Common::UTF16ToUTF8(dir); 553 std::string strDir = Common::UTF16ToUTF8(dir);
@@ -668,7 +677,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
668 if (user_path.empty()) { 677 if (user_path.empty()) {
669#ifdef _WIN32 678#ifdef _WIN32
670 user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP; 679 user_path = GetExeDirectory() + DIR_SEP USERDATA_DIR DIR_SEP;
671 if (!FileUtil::IsDirectory(user_path)) { 680 if (!IsDirectory(user_path)) {
672 user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP; 681 user_path = AppDataRoamingDirectory() + DIR_SEP EMU_DATA_DIR DIR_SEP;
673 } else { 682 } else {
674 LOG_INFO(Common_Filesystem, "Using the local user directory"); 683 LOG_INFO(Common_Filesystem, "Using the local user directory");
@@ -677,7 +686,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
677 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); 686 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
678 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); 687 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
679#else 688#else
680 if (FileUtil::Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) { 689 if (Exists(ROOT_DIR DIR_SEP USERDATA_DIR)) {
681 user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP; 690 user_path = ROOT_DIR DIR_SEP USERDATA_DIR DIR_SEP;
682 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP); 691 paths.emplace(UserPath::ConfigDir, user_path + CONFIG_DIR DIR_SEP);
683 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP); 692 paths.emplace(UserPath::CacheDir, user_path + CACHE_DIR DIR_SEP);
@@ -695,6 +704,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
695 paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP); 704 paths.emplace(UserPath::NANDDir, user_path + NAND_DIR DIR_SEP);
696 paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP); 705 paths.emplace(UserPath::LoadDir, user_path + LOAD_DIR DIR_SEP);
697 paths.emplace(UserPath::DumpDir, user_path + DUMP_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);
698 paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP); 708 paths.emplace(UserPath::ShaderDir, user_path + SHADER_DIR DIR_SEP);
699 paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP); 709 paths.emplace(UserPath::SysDataDir, user_path + SYSDATA_DIR DIR_SEP);
700 paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP); 710 paths.emplace(UserPath::KeysDir, user_path + KEYS_DIR DIR_SEP);
@@ -703,7 +713,7 @@ const std::string& GetUserPath(UserPath path, const std::string& new_path) {
703 } 713 }
704 714
705 if (!new_path.empty()) { 715 if (!new_path.empty()) {
706 if (!FileUtil::IsDirectory(new_path)) { 716 if (!IsDirectory(new_path)) {
707 LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path); 717 LOG_ERROR(Common_Filesystem, "Invalid path specified {}", new_path);
708 return paths[path]; 718 return paths[path];
709 } else { 719 } else {
@@ -764,21 +774,23 @@ std::size_t ReadFileToString(bool text_file, const std::string& filename, std::s
764 774
765void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, 775void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
766 std::array<char, 4>& extension) { 776 std::array<char, 4>& extension) {
767 const std::string forbidden_characters = ".\"/\\[]:;=, "; 777 static constexpr std::string_view forbidden_characters = ".\"/\\[]:;=, ";
768 778
769 // On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces. 779 // On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces.
770 short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}}; 780 short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}};
771 extension = {{' ', ' ', ' ', '\0'}}; 781 extension = {{' ', ' ', ' ', '\0'}};
772 782
773 std::string::size_type point = filename.rfind('.'); 783 auto point = filename.rfind('.');
774 if (point == filename.size() - 1) 784 if (point == filename.size() - 1) {
775 point = filename.rfind('.', point); 785 point = filename.rfind('.', point);
786 }
776 787
777 // Get short name. 788 // Get short name.
778 int j = 0; 789 int j = 0;
779 for (char letter : filename.substr(0, point)) { 790 for (char letter : filename.substr(0, point)) {
780 if (forbidden_characters.find(letter, 0) != std::string::npos) 791 if (forbidden_characters.find(letter, 0) != std::string::npos) {
781 continue; 792 continue;
793 }
782 if (j == 8) { 794 if (j == 8) {
783 // TODO(Link Mauve): also do that for filenames containing a space. 795 // TODO(Link Mauve): also do that for filenames containing a space.
784 // TODO(Link Mauve): handle multiple files having the same short name. 796 // TODO(Link Mauve): handle multiple files having the same short name.
@@ -786,14 +798,15 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
786 short_name[7] = '1'; 798 short_name[7] = '1';
787 break; 799 break;
788 } 800 }
789 short_name[j++] = toupper(letter); 801 short_name[j++] = static_cast<char>(std::toupper(letter));
790 } 802 }
791 803
792 // Get extension. 804 // Get extension.
793 if (point != std::string::npos) { 805 if (point != std::string::npos) {
794 j = 0; 806 j = 0;
795 for (char letter : filename.substr(point + 1, 3)) 807 for (char letter : filename.substr(point + 1, 3)) {
796 extension[j++] = toupper(letter); 808 extension[j++] = static_cast<char>(std::toupper(letter));
809 }
797 } 810 }
798} 811}
799 812
@@ -888,16 +901,23 @@ std::string SanitizePath(std::string_view path_, DirectorySeparator directory_se
888 } 901 }
889 902
890 std::replace(path.begin(), path.end(), type1, type2); 903 std::replace(path.begin(), path.end(), type1, type2);
891 path.erase(std::unique(path.begin(), path.end(), 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(),
892 [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }), 912 [type2](char c1, char c2) { return c1 == type2 && c2 == type2; }),
893 path.end()); 913 path.end());
894 return std::string(RemoveTrailingSlash(path)); 914 return std::string(RemoveTrailingSlash(path));
895} 915}
896 916
897IOFile::IOFile() {} 917IOFile::IOFile() = default;
898 918
899IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { 919IOFile::IOFile(const std::string& filename, const char openmode[], int flags) {
900 Open(filename, openmode, flags); 920 void(Open(filename, openmode, flags));
901} 921}
902 922
903IOFile::~IOFile() { 923IOFile::~IOFile() {
@@ -938,17 +958,18 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags)
938} 958}
939 959
940bool IOFile::Close() { 960bool IOFile::Close() {
941 if (!IsOpen() || 0 != std::fclose(m_file)) 961 if (!IsOpen() || 0 != std::fclose(m_file)) {
942 return false; 962 return false;
963 }
943 964
944 m_file = nullptr; 965 m_file = nullptr;
945 return true; 966 return true;
946} 967}
947 968
948u64 IOFile::GetSize() const { 969u64 IOFile::GetSize() const {
949 if (IsOpen()) 970 if (IsOpen()) {
950 return FileUtil::GetSize(m_file); 971 return FS::GetSize(m_file);
951 972 }
952 return 0; 973 return 0;
953} 974}
954 975
@@ -957,9 +978,9 @@ bool IOFile::Seek(s64 off, int origin) const {
957} 978}
958 979
959u64 IOFile::Tell() const { 980u64 IOFile::Tell() const {
960 if (IsOpen()) 981 if (IsOpen()) {
961 return ftello(m_file); 982 return ftello(m_file);
962 983 }
963 return std::numeric_limits<u64>::max(); 984 return std::numeric_limits<u64>::max();
964} 985}
965 986
@@ -967,6 +988,34 @@ bool IOFile::Flush() {
967 return IsOpen() && 0 == std::fflush(m_file); 988 return IsOpen() && 0 == std::fflush(m_file);
968} 989}
969 990
991std::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
1005std::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
970bool IOFile::Resize(u64 size) { 1019bool IOFile::Resize(u64 size) {
971 return IsOpen() && 0 == 1020 return IsOpen() && 0 ==
972#ifdef _WIN32 1021#ifdef _WIN32
@@ -980,4 +1029,4 @@ bool IOFile::Resize(u64 size) {
980 ; 1029 ;
981} 1030}
982 1031
983} // namespace FileUtil 1032} // namespace Common::FS