diff options
Diffstat (limited to 'src')
47 files changed, 1787 insertions, 1679 deletions
diff --git a/src/common/common_paths.h b/src/common/common_paths.h index d03fca314..9bf3efaf2 100644 --- a/src/common/common_paths.h +++ b/src/common/common_paths.h | |||
| @@ -40,7 +40,7 @@ | |||
| 40 | #define DEBUGGER_CONFIG "debugger.ini" | 40 | #define DEBUGGER_CONFIG "debugger.ini" |
| 41 | #define LOGGER_CONFIG "logger.ini" | 41 | #define LOGGER_CONFIG "logger.ini" |
| 42 | // Files in the directory returned by GetUserPath(D_LOGS_IDX) | 42 | // Files in the directory returned by GetUserPath(D_LOGS_IDX) |
| 43 | #define LOG_FILE "citra_log.txt" | 43 | #define LOG_FILE "yuzu_log.txt" |
| 44 | 44 | ||
| 45 | // Sys files | 45 | // Sys files |
| 46 | #define SHARED_FONT "shared_font.bin" | 46 | #define SHARED_FONT "shared_font.bin" |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index b9e1fd1f6..493a81e01 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -2,6 +2,7 @@ | |||
| 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 <sstream> | ||
| 5 | #include "common/assert.h" | 6 | #include "common/assert.h" |
| 6 | #include "common/common_funcs.h" | 7 | #include "common/common_funcs.h" |
| 7 | #include "common/common_paths.h" | 8 | #include "common/common_paths.h" |
| @@ -386,7 +387,7 @@ u64 GetSize(FILE* f) { | |||
| 386 | bool CreateEmptyFile(const std::string& filename) { | 387 | bool CreateEmptyFile(const std::string& filename) { |
| 387 | LOG_TRACE(Common_Filesystem, "{}", filename); | 388 | LOG_TRACE(Common_Filesystem, "{}", filename); |
| 388 | 389 | ||
| 389 | if (!FileUtil::IOFile(filename, "wb")) { | 390 | if (!FileUtil::IOFile(filename, "wb").IsOpen()) { |
| 390 | LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); | 391 | LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); |
| 391 | return false; | 392 | return false; |
| 392 | } | 393 | } |
| @@ -678,7 +679,7 @@ std::string GetSysDirectory() { | |||
| 678 | return sysDir; | 679 | return sysDir; |
| 679 | } | 680 | } |
| 680 | 681 | ||
| 681 | // Returns a string with a Citra data dir or file in the user's home | 682 | // Returns a string with a yuzu data dir or file in the user's home |
| 682 | // directory. To be used in "multi-user" mode (that is, installed). | 683 | // directory. To be used in "multi-user" mode (that is, installed). |
| 683 | const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath) { | 684 | const std::string& GetUserPath(const unsigned int DirIDX, const std::string& newPath) { |
| 684 | static std::string paths[NUM_PATH_INDICES]; | 685 | static std::string paths[NUM_PATH_INDICES]; |
| @@ -750,7 +751,7 @@ size_t WriteStringToFile(bool text_file, const std::string& str, const char* fil | |||
| 750 | size_t ReadFileToString(bool text_file, const char* filename, std::string& str) { | 751 | size_t ReadFileToString(bool text_file, const char* filename, std::string& str) { |
| 751 | IOFile file(filename, text_file ? "r" : "rb"); | 752 | IOFile file(filename, text_file ? "r" : "rb"); |
| 752 | 753 | ||
| 753 | if (!file) | 754 | if (!file.IsOpen()) |
| 754 | return false; | 755 | return false; |
| 755 | 756 | ||
| 756 | str.resize(static_cast<u32>(file.GetSize())); | 757 | str.resize(static_cast<u32>(file.GetSize())); |
| @@ -799,6 +800,57 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam | |||
| 799 | } | 800 | } |
| 800 | } | 801 | } |
| 801 | 802 | ||
| 803 | std::vector<std::string> SplitPathComponents(const std::string& filename) { | ||
| 804 | auto copy(filename); | ||
| 805 | std::replace(copy.begin(), copy.end(), '\\', '/'); | ||
| 806 | std::vector<std::string> out; | ||
| 807 | |||
| 808 | std::stringstream stream(filename); | ||
| 809 | std::string item; | ||
| 810 | while (std::getline(stream, item, '/')) | ||
| 811 | out.push_back(std::move(item)); | ||
| 812 | |||
| 813 | return out; | ||
| 814 | } | ||
| 815 | |||
| 816 | std::string GetParentPath(const std::string& path) { | ||
| 817 | auto out = path; | ||
| 818 | const auto name_bck_index = out.find_last_of('\\'); | ||
| 819 | const auto name_fwd_index = out.find_last_of('/'); | ||
| 820 | size_t name_index; | ||
| 821 | if (name_bck_index == std::string::npos || name_fwd_index == std::string::npos) | ||
| 822 | name_index = std::min<size_t>(name_bck_index, name_fwd_index); | ||
| 823 | else | ||
| 824 | name_index = std::max<size_t>(name_bck_index, name_fwd_index); | ||
| 825 | |||
| 826 | return out.erase(name_index); | ||
| 827 | } | ||
| 828 | |||
| 829 | std::string GetFilename(std::string path) { | ||
| 830 | std::replace(path.begin(), path.end(), '\\', '/'); | ||
| 831 | auto name_index = path.find_last_of('/'); | ||
| 832 | if (name_index == std::string::npos) | ||
| 833 | return ""; | ||
| 834 | return path.substr(name_index + 1); | ||
| 835 | } | ||
| 836 | |||
| 837 | std::string GetExtensionFromFilename(const std::string& name) { | ||
| 838 | size_t index = name.find_last_of('.'); | ||
| 839 | if (index == std::string::npos) | ||
| 840 | return ""; | ||
| 841 | |||
| 842 | return name.substr(index + 1); | ||
| 843 | } | ||
| 844 | |||
| 845 | std::string RemoveTrailingSlash(const std::string& path) { | ||
| 846 | if (path.empty()) | ||
| 847 | return path; | ||
| 848 | if (path.back() == '\\' || path.back() == '/') | ||
| 849 | return path.substr(0, path.size() - 1); | ||
| 850 | |||
| 851 | return path; | ||
| 852 | } | ||
| 853 | |||
| 802 | IOFile::IOFile() {} | 854 | IOFile::IOFile() {} |
| 803 | 855 | ||
| 804 | IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { | 856 | IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { |
| @@ -820,7 +872,6 @@ IOFile& IOFile::operator=(IOFile&& other) noexcept { | |||
| 820 | 872 | ||
| 821 | void IOFile::Swap(IOFile& other) noexcept { | 873 | void IOFile::Swap(IOFile& other) noexcept { |
| 822 | std::swap(m_file, other.m_file); | 874 | std::swap(m_file, other.m_file); |
| 823 | std::swap(m_good, other.m_good); | ||
| 824 | } | 875 | } |
| 825 | 876 | ||
| 826 | bool IOFile::Open(const std::string& filename, const char openmode[], int flags) { | 877 | bool IOFile::Open(const std::string& filename, const char openmode[], int flags) { |
| @@ -837,16 +888,15 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags) | |||
| 837 | m_file = fopen(filename.c_str(), openmode); | 888 | m_file = fopen(filename.c_str(), openmode); |
| 838 | #endif | 889 | #endif |
| 839 | 890 | ||
| 840 | m_good = IsOpen(); | 891 | return IsOpen(); |
| 841 | return m_good; | ||
| 842 | } | 892 | } |
| 843 | 893 | ||
| 844 | bool IOFile::Close() { | 894 | bool IOFile::Close() { |
| 845 | if (!IsOpen() || 0 != std::fclose(m_file)) | 895 | if (!IsOpen() || 0 != std::fclose(m_file)) |
| 846 | m_good = false; | 896 | return false; |
| 847 | 897 | ||
| 848 | m_file = nullptr; | 898 | m_file = nullptr; |
| 849 | return m_good; | 899 | return true; |
| 850 | } | 900 | } |
| 851 | 901 | ||
| 852 | u64 IOFile::GetSize() const { | 902 | u64 IOFile::GetSize() const { |
| @@ -856,11 +906,8 @@ u64 IOFile::GetSize() const { | |||
| 856 | return 0; | 906 | return 0; |
| 857 | } | 907 | } |
| 858 | 908 | ||
| 859 | bool IOFile::Seek(s64 off, int origin) { | 909 | bool IOFile::Seek(s64 off, int origin) const { |
| 860 | if (!IsOpen() || 0 != fseeko(m_file, off, origin)) | 910 | return IsOpen() && 0 == fseeko(m_file, off, origin); |
| 861 | m_good = false; | ||
| 862 | |||
| 863 | return m_good; | ||
| 864 | } | 911 | } |
| 865 | 912 | ||
| 866 | u64 IOFile::Tell() const { | 913 | u64 IOFile::Tell() const { |
| @@ -871,26 +918,20 @@ u64 IOFile::Tell() const { | |||
| 871 | } | 918 | } |
| 872 | 919 | ||
| 873 | bool IOFile::Flush() { | 920 | bool IOFile::Flush() { |
| 874 | if (!IsOpen() || 0 != std::fflush(m_file)) | 921 | return IsOpen() && 0 == std::fflush(m_file); |
| 875 | m_good = false; | ||
| 876 | |||
| 877 | return m_good; | ||
| 878 | } | 922 | } |
| 879 | 923 | ||
| 880 | bool IOFile::Resize(u64 size) { | 924 | bool IOFile::Resize(u64 size) { |
| 881 | if (!IsOpen() || 0 != | 925 | return IsOpen() && 0 == |
| 882 | #ifdef _WIN32 | 926 | #ifdef _WIN32 |
| 883 | // ector: _chsize sucks, not 64-bit safe | 927 | // ector: _chsize sucks, not 64-bit safe |
| 884 | // F|RES: changed to _chsize_s. i think it is 64-bit safe | 928 | // F|RES: changed to _chsize_s. i think it is 64-bit safe |
| 885 | _chsize_s(_fileno(m_file), size) | 929 | _chsize_s(_fileno(m_file), size) |
| 886 | #else | 930 | #else |
| 887 | // TODO: handle 64bit and growing | 931 | // TODO: handle 64bit and growing |
| 888 | ftruncate(fileno(m_file), size) | 932 | ftruncate(fileno(m_file), size) |
| 889 | #endif | 933 | #endif |
| 890 | ) | 934 | ; |
| 891 | m_good = false; | ||
| 892 | |||
| 893 | return m_good; | ||
| 894 | } | 935 | } |
| 895 | 936 | ||
| 896 | } // namespace FileUtil | 937 | } // namespace FileUtil |
diff --git a/src/common/file_util.h b/src/common/file_util.h index 5bc7fbf7c..9bb3c4109 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -150,6 +150,31 @@ size_t ReadFileToString(bool text_file, const char* filename, std::string& str); | |||
| 150 | void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, | 150 | void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, |
| 151 | std::array<char, 4>& extension); | 151 | std::array<char, 4>& extension); |
| 152 | 152 | ||
| 153 | // Splits the path on '/' or '\' and put the components into a vector | ||
| 154 | // i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } | ||
| 155 | std::vector<std::string> SplitPathComponents(const std::string& filename); | ||
| 156 | |||
| 157 | // Gets all of the text prior to the last '/' or '\' in the path. | ||
| 158 | std::string GetParentPath(const std::string& path); | ||
| 159 | |||
| 160 | // Gets the filename of the path | ||
| 161 | std::string GetFilename(std::string path); | ||
| 162 | |||
| 163 | // Gets the extension of the filename | ||
| 164 | std::string GetExtensionFromFilename(const std::string& name); | ||
| 165 | |||
| 166 | // Removes the final '/' or '\' if one exists | ||
| 167 | std::string RemoveTrailingSlash(const std::string& path); | ||
| 168 | |||
| 169 | // Creates a new vector containing indices [first, last) from the original. | ||
| 170 | template <typename T> | ||
| 171 | std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) { | ||
| 172 | if (first >= last) | ||
| 173 | return {}; | ||
| 174 | last = std::min<size_t>(last, vector.size()); | ||
| 175 | return std::vector<T>(vector.begin() + first, vector.begin() + first + last); | ||
| 176 | } | ||
| 177 | |||
| 153 | // simple wrapper for cstdlib file functions to | 178 | // simple wrapper for cstdlib file functions to |
| 154 | // hopefully will make error checking easier | 179 | // hopefully will make error checking easier |
| 155 | // and make forgetting an fclose() harder | 180 | // and make forgetting an fclose() harder |
| @@ -172,41 +197,27 @@ public: | |||
| 172 | bool Close(); | 197 | bool Close(); |
| 173 | 198 | ||
| 174 | template <typename T> | 199 | template <typename T> |
| 175 | size_t ReadArray(T* data, size_t length) { | 200 | size_t ReadArray(T* data, size_t length) const { |
| 176 | static_assert(std::is_trivially_copyable<T>(), | 201 | static_assert(std::is_trivially_copyable<T>(), |
| 177 | "Given array does not consist of trivially copyable objects"); | 202 | "Given array does not consist of trivially copyable objects"); |
| 178 | 203 | ||
| 179 | if (!IsOpen()) { | 204 | if (!IsOpen()) |
| 180 | m_good = false; | ||
| 181 | return -1; | 205 | return -1; |
| 182 | } | ||
| 183 | 206 | ||
| 184 | size_t items_read = std::fread(data, sizeof(T), length, m_file); | 207 | return std::fread(data, sizeof(T), length, m_file); |
| 185 | if (items_read != length) | ||
| 186 | m_good = false; | ||
| 187 | |||
| 188 | return items_read; | ||
| 189 | } | 208 | } |
| 190 | 209 | ||
| 191 | template <typename T> | 210 | template <typename T> |
| 192 | size_t WriteArray(const T* data, size_t length) { | 211 | size_t WriteArray(const T* data, size_t length) { |
| 193 | static_assert(std::is_trivially_copyable<T>(), | 212 | static_assert(std::is_trivially_copyable<T>(), |
| 194 | "Given array does not consist of trivially copyable objects"); | 213 | "Given array does not consist of trivially copyable objects"); |
| 195 | 214 | if (!IsOpen()) | |
| 196 | if (!IsOpen()) { | ||
| 197 | m_good = false; | ||
| 198 | return -1; | 215 | return -1; |
| 199 | } | 216 | return std::fwrite(data, sizeof(T), length, m_file); |
| 200 | |||
| 201 | size_t items_written = std::fwrite(data, sizeof(T), length, m_file); | ||
| 202 | if (items_written != length) | ||
| 203 | m_good = false; | ||
| 204 | |||
| 205 | return items_written; | ||
| 206 | } | 217 | } |
| 207 | 218 | ||
| 208 | template <typename T> | 219 | template <typename T> |
| 209 | size_t ReadBytes(T* data, size_t length) { | 220 | size_t ReadBytes(T* data, size_t length) const { |
| 210 | static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable"); | 221 | static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable"); |
| 211 | return ReadArray(reinterpret_cast<char*>(data), length); | 222 | return ReadArray(reinterpret_cast<char*>(data), length); |
| 212 | } | 223 | } |
| @@ -231,15 +242,7 @@ public: | |||
| 231 | return nullptr != m_file; | 242 | return nullptr != m_file; |
| 232 | } | 243 | } |
| 233 | 244 | ||
| 234 | // m_good is set to false when a read, write or other function fails | 245 | bool Seek(s64 off, int origin) const; |
| 235 | bool IsGood() const { | ||
| 236 | return m_good; | ||
| 237 | } | ||
| 238 | explicit operator bool() const { | ||
| 239 | return IsGood(); | ||
| 240 | } | ||
| 241 | |||
| 242 | bool Seek(s64 off, int origin); | ||
| 243 | u64 Tell() const; | 246 | u64 Tell() const; |
| 244 | u64 GetSize() const; | 247 | u64 GetSize() const; |
| 245 | bool Resize(u64 size); | 248 | bool Resize(u64 size); |
| @@ -247,13 +250,11 @@ public: | |||
| 247 | 250 | ||
| 248 | // clear error state | 251 | // clear error state |
| 249 | void Clear() { | 252 | void Clear() { |
| 250 | m_good = true; | ||
| 251 | std::clearerr(m_file); | 253 | std::clearerr(m_file); |
| 252 | } | 254 | } |
| 253 | 255 | ||
| 254 | private: | 256 | private: |
| 255 | std::FILE* m_file = nullptr; | 257 | std::FILE* m_file = nullptr; |
| 256 | bool m_good = true; | ||
| 257 | }; | 258 | }; |
| 258 | 259 | ||
| 259 | } // namespace FileUtil | 260 | } // namespace FileUtil |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3dff068df..582294a57 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -8,9 +8,9 @@ add_library(core STATIC | |||
| 8 | core_cpu.h | 8 | core_cpu.h |
| 9 | core_timing.cpp | 9 | core_timing.cpp |
| 10 | core_timing.h | 10 | core_timing.h |
| 11 | file_sys/content_archive.cpp | ||
| 12 | file_sys/content_archive.h | ||
| 11 | file_sys/directory.h | 13 | file_sys/directory.h |
| 12 | file_sys/disk_filesystem.cpp | ||
| 13 | file_sys/disk_filesystem.h | ||
| 14 | file_sys/errors.h | 14 | file_sys/errors.h |
| 15 | file_sys/filesystem.cpp | 15 | file_sys/filesystem.cpp |
| 16 | file_sys/filesystem.h | 16 | file_sys/filesystem.h |
| @@ -20,15 +20,13 @@ add_library(core STATIC | |||
| 20 | file_sys/path_parser.h | 20 | file_sys/path_parser.h |
| 21 | file_sys/program_metadata.cpp | 21 | file_sys/program_metadata.cpp |
| 22 | file_sys/program_metadata.h | 22 | file_sys/program_metadata.h |
| 23 | file_sys/romfs_factory.cpp | ||
| 24 | file_sys/romfs_factory.h | ||
| 25 | file_sys/romfs_filesystem.cpp | ||
| 26 | file_sys/romfs_filesystem.h | ||
| 27 | file_sys/savedata_factory.cpp | ||
| 28 | file_sys/savedata_factory.h | ||
| 29 | file_sys/sdmc_factory.cpp | ||
| 30 | file_sys/sdmc_factory.h | ||
| 31 | file_sys/storage.h | 23 | file_sys/storage.h |
| 24 | file_sys/vfs.cpp | ||
| 25 | file_sys/vfs.h | ||
| 26 | file_sys/vfs_offset.cpp | ||
| 27 | file_sys/vfs_offset.h | ||
| 28 | file_sys/vfs_real.cpp | ||
| 29 | file_sys/vfs_real.h | ||
| 32 | frontend/emu_window.cpp | 30 | frontend/emu_window.cpp |
| 33 | frontend/emu_window.h | 31 | frontend/emu_window.h |
| 34 | frontend/framebuffer_layout.cpp | 32 | frontend/framebuffer_layout.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 8335d502e..82db5cccf 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -19,6 +19,7 @@ | |||
| 19 | #include "core/loader/loader.h" | 19 | #include "core/loader/loader.h" |
| 20 | #include "core/memory_setup.h" | 20 | #include "core/memory_setup.h" |
| 21 | #include "core/settings.h" | 21 | #include "core/settings.h" |
| 22 | #include "file_sys/vfs_real.h" | ||
| 22 | #include "video_core/video_core.h" | 23 | #include "video_core/video_core.h" |
| 23 | 24 | ||
| 24 | namespace Core { | 25 | namespace Core { |
| @@ -84,7 +85,7 @@ System::ResultStatus System::SingleStep() { | |||
| 84 | } | 85 | } |
| 85 | 86 | ||
| 86 | System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) { | 87 | System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) { |
| 87 | app_loader = Loader::GetLoader(filepath); | 88 | app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath)); |
| 88 | 89 | ||
| 89 | if (!app_loader) { | 90 | if (!app_loader) { |
| 90 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); | 91 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp new file mode 100644 index 000000000..b45b83a26 --- /dev/null +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -0,0 +1,164 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/logging/log.h" | ||
| 6 | #include "core/file_sys/content_archive.h" | ||
| 7 | #include "core/file_sys/vfs_offset.h" | ||
| 8 | #include "core/loader/loader.h" | ||
| 9 | |||
| 10 | namespace FileSys { | ||
| 11 | |||
| 12 | // Media offsets in headers are stored divided by 512. Mult. by this to get real offset. | ||
| 13 | constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200; | ||
| 14 | |||
| 15 | constexpr u64 SECTION_HEADER_SIZE = 0x200; | ||
| 16 | constexpr u64 SECTION_HEADER_OFFSET = 0x400; | ||
| 17 | |||
| 18 | constexpr u32 IVFC_MAX_LEVEL = 6; | ||
| 19 | |||
| 20 | enum class NCASectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 }; | ||
| 21 | |||
| 22 | struct NCASectionHeaderBlock { | ||
| 23 | INSERT_PADDING_BYTES(3); | ||
| 24 | NCASectionFilesystemType filesystem_type; | ||
| 25 | u8 crypto_type; | ||
| 26 | INSERT_PADDING_BYTES(3); | ||
| 27 | }; | ||
| 28 | static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); | ||
| 29 | |||
| 30 | struct PFS0Superblock { | ||
| 31 | NCASectionHeaderBlock header_block; | ||
| 32 | std::array<u8, 0x20> hash; | ||
| 33 | u32_le size; | ||
| 34 | INSERT_PADDING_BYTES(4); | ||
| 35 | u64_le hash_table_offset; | ||
| 36 | u64_le hash_table_size; | ||
| 37 | u64_le pfs0_header_offset; | ||
| 38 | u64_le pfs0_size; | ||
| 39 | INSERT_PADDING_BYTES(432); | ||
| 40 | }; | ||
| 41 | static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); | ||
| 42 | |||
| 43 | struct IVFCLevel { | ||
| 44 | u64_le offset; | ||
| 45 | u64_le size; | ||
| 46 | u32_le block_size; | ||
| 47 | u32_le reserved; | ||
| 48 | }; | ||
| 49 | static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size."); | ||
| 50 | |||
| 51 | struct RomFSSuperblock { | ||
| 52 | NCASectionHeaderBlock header_block; | ||
| 53 | u32_le magic; | ||
| 54 | u32_le magic_number; | ||
| 55 | INSERT_PADDING_BYTES(8); | ||
| 56 | std::array<IVFCLevel, 6> levels; | ||
| 57 | INSERT_PADDING_BYTES(64); | ||
| 58 | }; | ||
| 59 | static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size."); | ||
| 60 | |||
| 61 | NCA::NCA(VirtualFile file_) : file(file_) { | ||
| 62 | if (sizeof(NCAHeader) != file->ReadObject(&header)) | ||
| 63 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 64 | |||
| 65 | if (!IsValidNCA(header)) { | ||
| 66 | status = Loader::ResultStatus::ErrorInvalidFormat; | ||
| 67 | return; | ||
| 68 | } | ||
| 69 | |||
| 70 | std::ptrdiff_t number_sections = | ||
| 71 | std::count_if(std::begin(header.section_tables), std::end(header.section_tables), | ||
| 72 | [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); | ||
| 73 | |||
| 74 | for (std::ptrdiff_t i = 0; i < number_sections; ++i) { | ||
| 75 | // Seek to beginning of this section. | ||
| 76 | NCASectionHeaderBlock block{}; | ||
| 77 | if (sizeof(NCASectionHeaderBlock) != | ||
| 78 | file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) | ||
| 79 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 80 | |||
| 81 | if (block.filesystem_type == NCASectionFilesystemType::ROMFS) { | ||
| 82 | RomFSSuperblock sb{}; | ||
| 83 | if (sizeof(RomFSSuperblock) != | ||
| 84 | file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) | ||
| 85 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 86 | |||
| 87 | const size_t romfs_offset = | ||
| 88 | header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + | ||
| 89 | sb.levels[IVFC_MAX_LEVEL - 1].offset; | ||
| 90 | const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size; | ||
| 91 | files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset)); | ||
| 92 | romfs = files.back(); | ||
| 93 | } else if (block.filesystem_type == NCASectionFilesystemType::PFS0) { | ||
| 94 | PFS0Superblock sb{}; | ||
| 95 | // Seek back to beginning of this section. | ||
| 96 | if (sizeof(PFS0Superblock) != | ||
| 97 | file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) | ||
| 98 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 99 | |||
| 100 | u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * | ||
| 101 | MEDIA_OFFSET_MULTIPLIER) + | ||
| 102 | sb.pfs0_header_offset; | ||
| 103 | u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - | ||
| 104 | header.section_tables[i].media_offset); | ||
| 105 | auto npfs = std::make_shared<PartitionFilesystem>( | ||
| 106 | std::make_shared<OffsetVfsFile>(file, size, offset)); | ||
| 107 | |||
| 108 | if (npfs->GetStatus() == Loader::ResultStatus::Success) { | ||
| 109 | dirs.emplace_back(npfs); | ||
| 110 | if (IsDirectoryExeFS(dirs.back())) | ||
| 111 | exefs = dirs.back(); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | status = Loader::ResultStatus::Success; | ||
| 117 | } | ||
| 118 | |||
| 119 | Loader::ResultStatus NCA::GetStatus() const { | ||
| 120 | return status; | ||
| 121 | } | ||
| 122 | |||
| 123 | std::vector<std::shared_ptr<VfsFile>> NCA::GetFiles() const { | ||
| 124 | if (status != Loader::ResultStatus::Success) | ||
| 125 | return {}; | ||
| 126 | return files; | ||
| 127 | } | ||
| 128 | |||
| 129 | std::vector<std::shared_ptr<VfsDirectory>> NCA::GetSubdirectories() const { | ||
| 130 | if (status != Loader::ResultStatus::Success) | ||
| 131 | return {}; | ||
| 132 | return dirs; | ||
| 133 | } | ||
| 134 | |||
| 135 | std::string NCA::GetName() const { | ||
| 136 | return file->GetName(); | ||
| 137 | } | ||
| 138 | |||
| 139 | std::shared_ptr<VfsDirectory> NCA::GetParentDirectory() const { | ||
| 140 | return file->GetContainingDirectory(); | ||
| 141 | } | ||
| 142 | |||
| 143 | NCAContentType NCA::GetType() const { | ||
| 144 | return header.content_type; | ||
| 145 | } | ||
| 146 | |||
| 147 | u64 NCA::GetTitleId() const { | ||
| 148 | if (status != Loader::ResultStatus::Success) | ||
| 149 | return {}; | ||
| 150 | return header.title_id; | ||
| 151 | } | ||
| 152 | |||
| 153 | VirtualFile NCA::GetRomFS() const { | ||
| 154 | return romfs; | ||
| 155 | } | ||
| 156 | |||
| 157 | VirtualDir NCA::GetExeFS() const { | ||
| 158 | return exefs; | ||
| 159 | } | ||
| 160 | |||
| 161 | bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 162 | return false; | ||
| 163 | } | ||
| 164 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h new file mode 100644 index 000000000..eb4ca1c18 --- /dev/null +++ b/src/core/file_sys/content_archive.h | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/common_funcs.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/swap.h" | ||
| 10 | #include "core/file_sys/partition_filesystem.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | enum class NCAContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 }; | ||
| 15 | |||
| 16 | struct NCASectionTableEntry { | ||
| 17 | u32_le media_offset; | ||
| 18 | u32_le media_end_offset; | ||
| 19 | INSERT_PADDING_BYTES(0x8); | ||
| 20 | }; | ||
| 21 | static_assert(sizeof(NCASectionTableEntry) == 0x10, "NCASectionTableEntry has incorrect size."); | ||
| 22 | |||
| 23 | struct NCAHeader { | ||
| 24 | std::array<u8, 0x100> rsa_signature_1; | ||
| 25 | std::array<u8, 0x100> rsa_signature_2; | ||
| 26 | u32_le magic; | ||
| 27 | u8 is_system; | ||
| 28 | NCAContentType content_type; | ||
| 29 | u8 crypto_type; | ||
| 30 | u8 key_index; | ||
| 31 | u64_le size; | ||
| 32 | u64_le title_id; | ||
| 33 | INSERT_PADDING_BYTES(0x4); | ||
| 34 | u32_le sdk_version; | ||
| 35 | u8 crypto_type_2; | ||
| 36 | INSERT_PADDING_BYTES(15); | ||
| 37 | std::array<u8, 0x10> rights_id; | ||
| 38 | std::array<NCASectionTableEntry, 0x4> section_tables; | ||
| 39 | std::array<std::array<u8, 0x20>, 0x4> hash_tables; | ||
| 40 | std::array<std::array<u8, 0x10>, 0x4> key_area; | ||
| 41 | INSERT_PADDING_BYTES(0xC0); | ||
| 42 | }; | ||
| 43 | static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size."); | ||
| 44 | |||
| 45 | static bool IsDirectoryExeFS(std::shared_ptr<FileSys::VfsDirectory> pfs) { | ||
| 46 | // According to switchbrew, an exefs must only contain these two files: | ||
| 47 | return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; | ||
| 48 | } | ||
| 49 | |||
| 50 | static bool IsValidNCA(const NCAHeader& header) { | ||
| 51 | return header.magic == Common::MakeMagic('N', 'C', 'A', '2') || | ||
| 52 | header.magic == Common::MakeMagic('N', 'C', 'A', '3'); | ||
| 53 | } | ||
| 54 | |||
| 55 | // An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. | ||
| 56 | // After construction, use GetStatus to determine if the file is valid and ready to be used. | ||
| 57 | class NCA : public ReadOnlyVfsDirectory { | ||
| 58 | public: | ||
| 59 | explicit NCA(VirtualFile file); | ||
| 60 | Loader::ResultStatus GetStatus() const; | ||
| 61 | |||
| 62 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | ||
| 63 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | ||
| 64 | std::string GetName() const override; | ||
| 65 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | ||
| 66 | |||
| 67 | NCAContentType GetType() const; | ||
| 68 | u64 GetTitleId() const; | ||
| 69 | |||
| 70 | VirtualFile GetRomFS() const; | ||
| 71 | VirtualDir GetExeFS() const; | ||
| 72 | |||
| 73 | protected: | ||
| 74 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 75 | |||
| 76 | private: | ||
| 77 | std::vector<VirtualDir> dirs; | ||
| 78 | std::vector<VirtualFile> files; | ||
| 79 | |||
| 80 | VirtualFile romfs = nullptr; | ||
| 81 | VirtualDir exefs = nullptr; | ||
| 82 | VirtualFile file; | ||
| 83 | |||
| 84 | NCAHeader header{}; | ||
| 85 | |||
| 86 | Loader::ResultStatus status{}; | ||
| 87 | }; | ||
| 88 | |||
| 89 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/disk_filesystem.cpp b/src/core/file_sys/disk_filesystem.cpp deleted file mode 100644 index 8c6f15bb5..000000000 --- a/src/core/file_sys/disk_filesystem.cpp +++ /dev/null | |||
| @@ -1,237 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include <memory> | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/file_sys/disk_filesystem.h" | ||
| 10 | #include "core/file_sys/errors.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | static std::string ModeFlagsToString(Mode mode) { | ||
| 15 | std::string mode_str; | ||
| 16 | u32 mode_flags = static_cast<u32>(mode); | ||
| 17 | |||
| 18 | // Calculate the correct open mode for the file. | ||
| 19 | if ((mode_flags & static_cast<u32>(Mode::Read)) && | ||
| 20 | (mode_flags & static_cast<u32>(Mode::Write))) { | ||
| 21 | if (mode_flags & static_cast<u32>(Mode::Append)) | ||
| 22 | mode_str = "a+"; | ||
| 23 | else | ||
| 24 | mode_str = "r+"; | ||
| 25 | } else { | ||
| 26 | if (mode_flags & static_cast<u32>(Mode::Read)) | ||
| 27 | mode_str = "r"; | ||
| 28 | else if (mode_flags & static_cast<u32>(Mode::Append)) | ||
| 29 | mode_str = "a"; | ||
| 30 | else if (mode_flags & static_cast<u32>(Mode::Write)) | ||
| 31 | mode_str = "w"; | ||
| 32 | } | ||
| 33 | |||
| 34 | mode_str += "b"; | ||
| 35 | |||
| 36 | return mode_str; | ||
| 37 | } | ||
| 38 | |||
| 39 | std::string Disk_FileSystem::GetName() const { | ||
| 40 | return "Disk"; | ||
| 41 | } | ||
| 42 | |||
| 43 | ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path, | ||
| 44 | Mode mode) const { | ||
| 45 | |||
| 46 | // Calculate the correct open mode for the file. | ||
| 47 | std::string mode_str = ModeFlagsToString(mode); | ||
| 48 | |||
| 49 | std::string full_path = base_directory + path; | ||
| 50 | auto file = std::make_shared<FileUtil::IOFile>(full_path, mode_str.c_str()); | ||
| 51 | |||
| 52 | if (!file->IsOpen()) { | ||
| 53 | return ERROR_PATH_NOT_FOUND; | ||
| 54 | } | ||
| 55 | |||
| 56 | return MakeResult<std::unique_ptr<StorageBackend>>( | ||
| 57 | std::make_unique<Disk_Storage>(std::move(file))); | ||
| 58 | } | ||
| 59 | |||
| 60 | ResultCode Disk_FileSystem::DeleteFile(const std::string& path) const { | ||
| 61 | if (!FileUtil::Exists(path)) { | ||
| 62 | return ERROR_PATH_NOT_FOUND; | ||
| 63 | } | ||
| 64 | |||
| 65 | FileUtil::Delete(path); | ||
| 66 | |||
| 67 | return RESULT_SUCCESS; | ||
| 68 | } | ||
| 69 | |||
| 70 | ResultCode Disk_FileSystem::RenameFile(const std::string& src_path, | ||
| 71 | const std::string& dest_path) const { | ||
| 72 | const std::string full_src_path = base_directory + src_path; | ||
| 73 | const std::string full_dest_path = base_directory + dest_path; | ||
| 74 | |||
| 75 | if (!FileUtil::Exists(full_src_path)) { | ||
| 76 | return ERROR_PATH_NOT_FOUND; | ||
| 77 | } | ||
| 78 | // TODO(wwylele): Use correct error code | ||
| 79 | return FileUtil::Rename(full_src_path, full_dest_path) ? RESULT_SUCCESS : ResultCode(-1); | ||
| 80 | } | ||
| 81 | |||
| 82 | ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const { | ||
| 83 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 84 | // TODO(wwylele): Use correct error code | ||
| 85 | return ResultCode(-1); | ||
| 86 | } | ||
| 87 | |||
| 88 | ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const { | ||
| 89 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 90 | // TODO(wwylele): Use correct error code | ||
| 91 | return ResultCode(-1); | ||
| 92 | } | ||
| 93 | |||
| 94 | ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const { | ||
| 95 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 96 | |||
| 97 | std::string full_path = base_directory + path; | ||
| 98 | if (size == 0) { | ||
| 99 | FileUtil::CreateEmptyFile(full_path); | ||
| 100 | return RESULT_SUCCESS; | ||
| 101 | } | ||
| 102 | |||
| 103 | FileUtil::IOFile file(full_path, "wb"); | ||
| 104 | // Creates a sparse file (or a normal file on filesystems without the concept of sparse files) | ||
| 105 | // We do this by seeking to the right size, then writing a single null byte. | ||
| 106 | if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) { | ||
| 107 | return RESULT_SUCCESS; | ||
| 108 | } | ||
| 109 | |||
| 110 | LOG_ERROR(Service_FS, "Too large file"); | ||
| 111 | // TODO(Subv): Find out the correct error code | ||
| 112 | return ResultCode(-1); | ||
| 113 | } | ||
| 114 | |||
| 115 | ResultCode Disk_FileSystem::CreateDirectory(const std::string& path) const { | ||
| 116 | // TODO(Subv): Perform path validation to prevent escaping the emulator sandbox. | ||
| 117 | std::string full_path = base_directory + path; | ||
| 118 | |||
| 119 | if (FileUtil::CreateDir(full_path)) { | ||
| 120 | return RESULT_SUCCESS; | ||
| 121 | } | ||
| 122 | |||
| 123 | LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating {}", full_path); | ||
| 124 | // TODO(wwylele): Use correct error code | ||
| 125 | return ResultCode(-1); | ||
| 126 | } | ||
| 127 | |||
| 128 | ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { | ||
| 129 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 130 | // TODO(wwylele): Use correct error code | ||
| 131 | return ResultCode(-1); | ||
| 132 | } | ||
| 133 | |||
| 134 | ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory( | ||
| 135 | const std::string& path) const { | ||
| 136 | |||
| 137 | std::string full_path = base_directory + path; | ||
| 138 | |||
| 139 | if (!FileUtil::IsDirectory(full_path)) { | ||
| 140 | // TODO(Subv): Find the correct error code for this. | ||
| 141 | return ResultCode(-1); | ||
| 142 | } | ||
| 143 | |||
| 144 | auto directory = std::make_unique<Disk_Directory>(full_path); | ||
| 145 | return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory)); | ||
| 146 | } | ||
| 147 | |||
| 148 | u64 Disk_FileSystem::GetFreeSpaceSize() const { | ||
| 149 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 153 | ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& path) const { | ||
| 154 | std::string full_path = base_directory + path; | ||
| 155 | if (!FileUtil::Exists(full_path)) { | ||
| 156 | return ERROR_PATH_NOT_FOUND; | ||
| 157 | } | ||
| 158 | |||
| 159 | if (FileUtil::IsDirectory(full_path)) | ||
| 160 | return MakeResult(EntryType::Directory); | ||
| 161 | |||
| 162 | return MakeResult(EntryType::File); | ||
| 163 | } | ||
| 164 | |||
| 165 | ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { | ||
| 166 | LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); | ||
| 167 | file->Seek(offset, SEEK_SET); | ||
| 168 | return MakeResult<size_t>(file->ReadBytes(buffer, length)); | ||
| 169 | } | ||
| 170 | |||
| 171 | ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush, | ||
| 172 | const u8* buffer) const { | ||
| 173 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 174 | file->Seek(offset, SEEK_SET); | ||
| 175 | size_t written = file->WriteBytes(buffer, length); | ||
| 176 | if (flush) { | ||
| 177 | file->Flush(); | ||
| 178 | } | ||
| 179 | return MakeResult<size_t>(written); | ||
| 180 | } | ||
| 181 | |||
| 182 | u64 Disk_Storage::GetSize() const { | ||
| 183 | return file->GetSize(); | ||
| 184 | } | ||
| 185 | |||
| 186 | bool Disk_Storage::SetSize(const u64 size) const { | ||
| 187 | file->Resize(size); | ||
| 188 | file->Flush(); | ||
| 189 | return true; | ||
| 190 | } | ||
| 191 | |||
| 192 | Disk_Directory::Disk_Directory(const std::string& path) { | ||
| 193 | unsigned size = FileUtil::ScanDirectoryTree(path, directory); | ||
| 194 | directory.size = size; | ||
| 195 | directory.isDirectory = true; | ||
| 196 | children_iterator = directory.children.begin(); | ||
| 197 | } | ||
| 198 | |||
| 199 | u64 Disk_Directory::Read(const u64 count, Entry* entries) { | ||
| 200 | u64 entries_read = 0; | ||
| 201 | |||
| 202 | while (entries_read < count && children_iterator != directory.children.cend()) { | ||
| 203 | const FileUtil::FSTEntry& file = *children_iterator; | ||
| 204 | const std::string& filename = file.virtualName; | ||
| 205 | Entry& entry = entries[entries_read]; | ||
| 206 | |||
| 207 | LOG_TRACE(Service_FS, "File {}: size={} dir={}", filename, file.size, file.isDirectory); | ||
| 208 | |||
| 209 | // TODO(Link Mauve): use a proper conversion to UTF-16. | ||
| 210 | for (size_t j = 0; j < FILENAME_LENGTH; ++j) { | ||
| 211 | entry.filename[j] = filename[j]; | ||
| 212 | if (!filename[j]) | ||
| 213 | break; | ||
| 214 | } | ||
| 215 | |||
| 216 | if (file.isDirectory) { | ||
| 217 | entry.file_size = 0; | ||
| 218 | entry.type = EntryType::Directory; | ||
| 219 | } else { | ||
| 220 | entry.file_size = file.size; | ||
| 221 | entry.type = EntryType::File; | ||
| 222 | } | ||
| 223 | |||
| 224 | ++entries_read; | ||
| 225 | ++children_iterator; | ||
| 226 | } | ||
| 227 | return entries_read; | ||
| 228 | } | ||
| 229 | |||
| 230 | u64 Disk_Directory::GetEntryCount() const { | ||
| 231 | // We convert the children iterator into a const_iterator to allow template argument deduction | ||
| 232 | // in std::distance. | ||
| 233 | std::vector<FileUtil::FSTEntry>::const_iterator current = children_iterator; | ||
| 234 | return std::distance(current, directory.children.end()); | ||
| 235 | } | ||
| 236 | |||
| 237 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/disk_filesystem.h b/src/core/file_sys/disk_filesystem.h deleted file mode 100644 index 591e39fda..000000000 --- a/src/core/file_sys/disk_filesystem.h +++ /dev/null | |||
| @@ -1,84 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "common/file_util.h" | ||
| 12 | #include "core/file_sys/directory.h" | ||
| 13 | #include "core/file_sys/filesystem.h" | ||
| 14 | #include "core/file_sys/storage.h" | ||
| 15 | #include "core/hle/result.h" | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | class Disk_FileSystem : public FileSystemBackend { | ||
| 20 | public: | ||
| 21 | explicit Disk_FileSystem(std::string base_directory) | ||
| 22 | : base_directory(std::move(base_directory)) {} | ||
| 23 | |||
| 24 | std::string GetName() const override; | ||
| 25 | |||
| 26 | ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, | ||
| 27 | Mode mode) const override; | ||
| 28 | ResultCode DeleteFile(const std::string& path) const override; | ||
| 29 | ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const override; | ||
| 30 | ResultCode DeleteDirectory(const Path& path) const override; | ||
| 31 | ResultCode DeleteDirectoryRecursively(const Path& path) const override; | ||
| 32 | ResultCode CreateFile(const std::string& path, u64 size) const override; | ||
| 33 | ResultCode CreateDirectory(const std::string& path) const override; | ||
| 34 | ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; | ||
| 35 | ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory( | ||
| 36 | const std::string& path) const override; | ||
| 37 | u64 GetFreeSpaceSize() const override; | ||
| 38 | ResultVal<EntryType> GetEntryType(const std::string& path) const override; | ||
| 39 | |||
| 40 | protected: | ||
| 41 | std::string base_directory; | ||
| 42 | }; | ||
| 43 | |||
| 44 | class Disk_Storage : public StorageBackend { | ||
| 45 | public: | ||
| 46 | explicit Disk_Storage(std::shared_ptr<FileUtil::IOFile> file) : file(std::move(file)) {} | ||
| 47 | |||
| 48 | ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; | ||
| 49 | ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; | ||
| 50 | u64 GetSize() const override; | ||
| 51 | bool SetSize(u64 size) const override; | ||
| 52 | bool Close() const override { | ||
| 53 | return false; | ||
| 54 | } | ||
| 55 | void Flush() const override {} | ||
| 56 | |||
| 57 | private: | ||
| 58 | std::shared_ptr<FileUtil::IOFile> file; | ||
| 59 | }; | ||
| 60 | |||
| 61 | class Disk_Directory : public DirectoryBackend { | ||
| 62 | public: | ||
| 63 | explicit Disk_Directory(const std::string& path); | ||
| 64 | |||
| 65 | ~Disk_Directory() override { | ||
| 66 | Close(); | ||
| 67 | } | ||
| 68 | |||
| 69 | u64 Read(const u64 count, Entry* entries) override; | ||
| 70 | u64 GetEntryCount() const override; | ||
| 71 | |||
| 72 | bool Close() const override { | ||
| 73 | return true; | ||
| 74 | } | ||
| 75 | |||
| 76 | protected: | ||
| 77 | FileUtil::FSTEntry directory; | ||
| 78 | |||
| 79 | // We need to remember the last entry we returned, so a subsequent call to Read will continue | ||
| 80 | // from the next one. This iterator will always point to the next unread entry. | ||
| 81 | std::vector<FileUtil::FSTEntry>::iterator children_iterator; | ||
| 82 | }; | ||
| 83 | |||
| 84 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/filesystem.h b/src/core/file_sys/filesystem.h index 295a3133e..2d925d9e4 100644 --- a/src/core/file_sys/filesystem.h +++ b/src/core/file_sys/filesystem.h | |||
| @@ -66,136 +66,4 @@ private: | |||
| 66 | std::u16string u16str; | 66 | std::u16string u16str; |
| 67 | }; | 67 | }; |
| 68 | 68 | ||
| 69 | /// Parameters of the archive, as specified in the Create or Format call. | ||
| 70 | struct ArchiveFormatInfo { | ||
| 71 | u32_le total_size; ///< The pre-defined size of the archive. | ||
| 72 | u32_le number_directories; ///< The pre-defined number of directories in the archive. | ||
| 73 | u32_le number_files; ///< The pre-defined number of files in the archive. | ||
| 74 | u8 duplicate_data; ///< Whether the archive should duplicate the data. | ||
| 75 | }; | ||
| 76 | static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD"); | ||
| 77 | |||
| 78 | class FileSystemBackend : NonCopyable { | ||
| 79 | public: | ||
| 80 | virtual ~FileSystemBackend() {} | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||
| 84 | */ | ||
| 85 | virtual std::string GetName() const = 0; | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Create a file specified by its path | ||
| 89 | * @param path Path relative to the Archive | ||
| 90 | * @param size The size of the new file, filled with zeroes | ||
| 91 | * @return Result of the operation | ||
| 92 | */ | ||
| 93 | virtual ResultCode CreateFile(const std::string& path, u64 size) const = 0; | ||
| 94 | |||
| 95 | /** | ||
| 96 | * Delete a file specified by its path | ||
| 97 | * @param path Path relative to the archive | ||
| 98 | * @return Result of the operation | ||
| 99 | */ | ||
| 100 | virtual ResultCode DeleteFile(const std::string& path) const = 0; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Create a directory specified by its path | ||
| 104 | * @param path Path relative to the archive | ||
| 105 | * @return Result of the operation | ||
| 106 | */ | ||
| 107 | virtual ResultCode CreateDirectory(const std::string& path) const = 0; | ||
| 108 | |||
| 109 | /** | ||
| 110 | * Delete a directory specified by its path | ||
| 111 | * @param path Path relative to the archive | ||
| 112 | * @return Result of the operation | ||
| 113 | */ | ||
| 114 | virtual ResultCode DeleteDirectory(const Path& path) const = 0; | ||
| 115 | |||
| 116 | /** | ||
| 117 | * Delete a directory specified by its path and anything under it | ||
| 118 | * @param path Path relative to the archive | ||
| 119 | * @return Result of the operation | ||
| 120 | */ | ||
| 121 | virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0; | ||
| 122 | |||
| 123 | /** | ||
| 124 | * Rename a File specified by its path | ||
| 125 | * @param src_path Source path relative to the archive | ||
| 126 | * @param dest_path Destination path relative to the archive | ||
| 127 | * @return Result of the operation | ||
| 128 | */ | ||
| 129 | virtual ResultCode RenameFile(const std::string& src_path, | ||
| 130 | const std::string& dest_path) const = 0; | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Rename a Directory specified by its path | ||
| 134 | * @param src_path Source path relative to the archive | ||
| 135 | * @param dest_path Destination path relative to the archive | ||
| 136 | * @return Result of the operation | ||
| 137 | */ | ||
| 138 | virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0; | ||
| 139 | |||
| 140 | /** | ||
| 141 | * Open a file specified by its path, using the specified mode | ||
| 142 | * @param path Path relative to the archive | ||
| 143 | * @param mode Mode to open the file with | ||
| 144 | * @return Opened file, or error code | ||
| 145 | */ | ||
| 146 | virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, | ||
| 147 | Mode mode) const = 0; | ||
| 148 | |||
| 149 | /** | ||
| 150 | * Open a directory specified by its path | ||
| 151 | * @param path Path relative to the archive | ||
| 152 | * @return Opened directory, or error code | ||
| 153 | */ | ||
| 154 | virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory( | ||
| 155 | const std::string& path) const = 0; | ||
| 156 | |||
| 157 | /** | ||
| 158 | * Get the free space | ||
| 159 | * @return The number of free bytes in the archive | ||
| 160 | */ | ||
| 161 | virtual u64 GetFreeSpaceSize() const = 0; | ||
| 162 | |||
| 163 | /** | ||
| 164 | * Get the type of the specified path | ||
| 165 | * @return The type of the specified path or error code | ||
| 166 | */ | ||
| 167 | virtual ResultVal<EntryType> GetEntryType(const std::string& path) const = 0; | ||
| 168 | }; | ||
| 169 | |||
| 170 | class FileSystemFactory : NonCopyable { | ||
| 171 | public: | ||
| 172 | virtual ~FileSystemFactory() {} | ||
| 173 | |||
| 174 | /** | ||
| 175 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||
| 176 | */ | ||
| 177 | virtual std::string GetName() const = 0; | ||
| 178 | |||
| 179 | /** | ||
| 180 | * Tries to open the archive of this type with the specified path | ||
| 181 | * @param path Path to the archive | ||
| 182 | * @return An ArchiveBackend corresponding operating specified archive path. | ||
| 183 | */ | ||
| 184 | virtual ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) = 0; | ||
| 185 | |||
| 186 | /** | ||
| 187 | * Deletes the archive contents and then re-creates the base folder | ||
| 188 | * @param path Path to the archive | ||
| 189 | * @return ResultCode of the operation, 0 on success | ||
| 190 | */ | ||
| 191 | virtual ResultCode Format(const Path& path) = 0; | ||
| 192 | |||
| 193 | /** | ||
| 194 | * Retrieves the format info about the archive with the specified path | ||
| 195 | * @param path Path to the archive | ||
| 196 | * @return Format information about the archive or error code | ||
| 197 | */ | ||
| 198 | virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const = 0; | ||
| 199 | }; | ||
| 200 | |||
| 201 | } // namespace FileSys | 69 | } // namespace FileSys |
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 46d438aca..15b1fb946 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp | |||
| @@ -6,29 +6,30 @@ | |||
| 6 | #include "common/file_util.h" | 6 | #include "common/file_util.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/file_sys/partition_filesystem.h" | 8 | #include "core/file_sys/partition_filesystem.h" |
| 9 | #include "core/file_sys/vfs_offset.h" | ||
| 9 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 10 | 11 | ||
| 11 | namespace FileSys { | 12 | namespace FileSys { |
| 12 | 13 | ||
| 13 | Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, size_t offset) { | 14 | PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { |
| 14 | FileUtil::IOFile file(file_path, "rb"); | ||
| 15 | if (!file.IsOpen()) | ||
| 16 | return Loader::ResultStatus::Error; | ||
| 17 | |||
| 18 | // At least be as large as the header | 15 | // At least be as large as the header |
| 19 | if (file.GetSize() < sizeof(Header)) | 16 | if (file->GetSize() < sizeof(Header)) { |
| 20 | return Loader::ResultStatus::Error; | 17 | status = Loader::ResultStatus::Error; |
| 18 | return; | ||
| 19 | } | ||
| 21 | 20 | ||
| 22 | file.Seek(offset, SEEK_SET); | ||
| 23 | // For cartridges, HFSs can get very large, so we need to calculate the size up to | 21 | // For cartridges, HFSs can get very large, so we need to calculate the size up to |
| 24 | // the actual content itself instead of just blindly reading in the entire file. | 22 | // the actual content itself instead of just blindly reading in the entire file. |
| 25 | Header pfs_header; | 23 | Header pfs_header; |
| 26 | if (!file.ReadBytes(&pfs_header, sizeof(Header))) | 24 | if (sizeof(Header) != file->ReadObject(&pfs_header)) { |
| 27 | return Loader::ResultStatus::Error; | 25 | status = Loader::ResultStatus::Error; |
| 26 | return; | ||
| 27 | } | ||
| 28 | 28 | ||
| 29 | if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') && | 29 | if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') && |
| 30 | pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) { | 30 | pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) { |
| 31 | return Loader::ResultStatus::ErrorInvalidFormat; | 31 | status = Loader::ResultStatus::ErrorInvalidFormat; |
| 32 | return; | ||
| 32 | } | 33 | } |
| 33 | 34 | ||
| 34 | bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); | 35 | bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); |
| @@ -38,99 +39,86 @@ Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, siz | |||
| 38 | sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; | 39 | sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; |
| 39 | 40 | ||
| 40 | // Actually read in now... | 41 | // Actually read in now... |
| 41 | file.Seek(offset, SEEK_SET); | 42 | std::vector<u8> file_data = file->ReadBytes(metadata_size); |
| 42 | std::vector<u8> file_data(metadata_size); | ||
| 43 | |||
| 44 | if (!file.ReadBytes(file_data.data(), metadata_size)) | ||
| 45 | return Loader::ResultStatus::Error; | ||
| 46 | 43 | ||
| 47 | Loader::ResultStatus result = Load(file_data); | 44 | if (file_data.size() != metadata_size) { |
| 48 | if (result != Loader::ResultStatus::Success) | 45 | status = Loader::ResultStatus::Error; |
| 49 | LOG_ERROR(Service_FS, "Failed to load PFS from file {}!", file_path); | 46 | return; |
| 50 | 47 | } | |
| 51 | return result; | ||
| 52 | } | ||
| 53 | 48 | ||
| 54 | Loader::ResultStatus PartitionFilesystem::Load(const std::vector<u8>& file_data, size_t offset) { | 49 | size_t total_size = file_data.size(); |
| 55 | size_t total_size = file_data.size() - offset; | 50 | if (total_size < sizeof(Header)) { |
| 56 | if (total_size < sizeof(Header)) | 51 | status = Loader::ResultStatus::Error; |
| 57 | return Loader::ResultStatus::Error; | 52 | return; |
| 53 | } | ||
| 58 | 54 | ||
| 59 | memcpy(&pfs_header, &file_data[offset], sizeof(Header)); | 55 | memcpy(&pfs_header, file_data.data(), sizeof(Header)); |
| 60 | if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') && | 56 | if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') && |
| 61 | pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) { | 57 | pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) { |
| 62 | return Loader::ResultStatus::ErrorInvalidFormat; | 58 | status = Loader::ResultStatus::ErrorInvalidFormat; |
| 59 | return; | ||
| 63 | } | 60 | } |
| 64 | 61 | ||
| 65 | is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); | 62 | is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); |
| 66 | 63 | ||
| 67 | size_t entries_offset = offset + sizeof(Header); | 64 | size_t entries_offset = sizeof(Header); |
| 68 | size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); | ||
| 69 | size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); | 65 | size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); |
| 66 | content_offset = strtab_offset + pfs_header.strtab_size; | ||
| 70 | for (u16 i = 0; i < pfs_header.num_entries; i++) { | 67 | for (u16 i = 0; i < pfs_header.num_entries; i++) { |
| 71 | FileEntry entry; | 68 | FSEntry entry; |
| 72 | 69 | ||
| 73 | memcpy(&entry.fs_entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry)); | 70 | memcpy(&entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry)); |
| 74 | entry.name = std::string(reinterpret_cast<const char*>( | 71 | std::string name( |
| 75 | &file_data[strtab_offset + entry.fs_entry.strtab_offset])); | 72 | reinterpret_cast<const char*>(&file_data[strtab_offset + entry.strtab_offset])); |
| 76 | pfs_entries.push_back(std::move(entry)); | ||
| 77 | } | ||
| 78 | 73 | ||
| 79 | content_offset = strtab_offset + pfs_header.strtab_size; | 74 | pfs_files.emplace_back( |
| 75 | std::make_shared<OffsetVfsFile>(file, entry.size, content_offset + entry.offset, name)); | ||
| 76 | } | ||
| 80 | 77 | ||
| 81 | return Loader::ResultStatus::Success; | 78 | status = Loader::ResultStatus::Success; |
| 82 | } | 79 | } |
| 83 | 80 | ||
| 84 | u32 PartitionFilesystem::GetNumEntries() const { | 81 | Loader::ResultStatus PartitionFilesystem::GetStatus() const { |
| 85 | return pfs_header.num_entries; | 82 | return status; |
| 86 | } | 83 | } |
| 87 | 84 | ||
| 88 | u64 PartitionFilesystem::GetEntryOffset(u32 index) const { | 85 | std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const { |
| 89 | if (index > GetNumEntries()) | 86 | return pfs_files; |
| 90 | return 0; | ||
| 91 | |||
| 92 | return content_offset + pfs_entries[index].fs_entry.offset; | ||
| 93 | } | 87 | } |
| 94 | 88 | ||
| 95 | u64 PartitionFilesystem::GetEntrySize(u32 index) const { | 89 | std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const { |
| 96 | if (index > GetNumEntries()) | 90 | return {}; |
| 97 | return 0; | ||
| 98 | |||
| 99 | return pfs_entries[index].fs_entry.size; | ||
| 100 | } | 91 | } |
| 101 | 92 | ||
| 102 | std::string PartitionFilesystem::GetEntryName(u32 index) const { | 93 | std::string PartitionFilesystem::GetName() const { |
| 103 | if (index > GetNumEntries()) | 94 | return is_hfs ? "HFS0" : "PFS0"; |
| 104 | return ""; | 95 | } |
| 105 | 96 | ||
| 106 | return pfs_entries[index].name; | 97 | std::shared_ptr<VfsDirectory> PartitionFilesystem::GetParentDirectory() const { |
| 98 | // TODO(DarkLordZach): Add support for nested containers. | ||
| 99 | return nullptr; | ||
| 107 | } | 100 | } |
| 108 | 101 | ||
| 109 | u64 PartitionFilesystem::GetFileOffset(const std::string& name) const { | 102 | void PartitionFilesystem::PrintDebugInfo() const { |
| 103 | LOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic); | ||
| 104 | LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); | ||
| 110 | for (u32 i = 0; i < pfs_header.num_entries; i++) { | 105 | for (u32 i = 0; i < pfs_header.num_entries; i++) { |
| 111 | if (pfs_entries[i].name == name) | 106 | LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, |
| 112 | return content_offset + pfs_entries[i].fs_entry.offset; | 107 | pfs_files[i]->GetName(), pfs_files[i]->GetSize(), |
| 108 | dynamic_cast<OffsetVfsFile*>(pfs_files[i].get())->GetOffset()); | ||
| 113 | } | 109 | } |
| 114 | |||
| 115 | return 0; | ||
| 116 | } | 110 | } |
| 117 | 111 | ||
| 118 | u64 PartitionFilesystem::GetFileSize(const std::string& name) const { | 112 | bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { |
| 119 | for (u32 i = 0; i < pfs_header.num_entries; i++) { | 113 | auto iter = std::find(pfs_files.begin(), pfs_files.end(), file); |
| 120 | if (pfs_entries[i].name == name) | 114 | if (iter == pfs_files.end()) |
| 121 | return pfs_entries[i].fs_entry.size; | 115 | return false; |
| 122 | } | ||
| 123 | 116 | ||
| 124 | return 0; | 117 | pfs_files[iter - pfs_files.begin()] = pfs_files.back(); |
| 125 | } | 118 | pfs_files.pop_back(); |
| 126 | 119 | ||
| 127 | void PartitionFilesystem::Print() const { | 120 | pfs_dirs.emplace_back(dir); |
| 128 | LOG_DEBUG(Service_FS, "Magic: {}", pfs_header.magic); | 121 | |
| 129 | LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); | 122 | return true; |
| 130 | for (u32 i = 0; i < pfs_header.num_entries; i++) { | ||
| 131 | LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, | ||
| 132 | pfs_entries[i].name.c_str(), pfs_entries[i].fs_entry.size, | ||
| 133 | GetFileOffset(pfs_entries[i].name)); | ||
| 134 | } | ||
| 135 | } | 123 | } |
| 136 | } // namespace FileSys | 124 | } // namespace FileSys |
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index 9c5810cf1..9656b40bf 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 13 | #include "core/file_sys/vfs.h" | ||
| 13 | 14 | ||
| 14 | namespace Loader { | 15 | namespace Loader { |
| 15 | enum class ResultStatus; | 16 | enum class ResultStatus; |
| @@ -21,19 +22,19 @@ namespace FileSys { | |||
| 21 | * Helper which implements an interface to parse PFS/HFS filesystems. | 22 | * Helper which implements an interface to parse PFS/HFS filesystems. |
| 22 | * Data can either be loaded from a file path or data with an offset into it. | 23 | * Data can either be loaded from a file path or data with an offset into it. |
| 23 | */ | 24 | */ |
| 24 | class PartitionFilesystem { | 25 | class PartitionFilesystem : public ReadOnlyVfsDirectory { |
| 25 | public: | 26 | public: |
| 26 | Loader::ResultStatus Load(const std::string& file_path, size_t offset = 0); | 27 | explicit PartitionFilesystem(std::shared_ptr<VfsFile> file); |
| 27 | Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0); | 28 | Loader::ResultStatus GetStatus() const; |
| 28 | 29 | ||
| 29 | u32 GetNumEntries() const; | 30 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; |
| 30 | u64 GetEntryOffset(u32 index) const; | 31 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; |
| 31 | u64 GetEntrySize(u32 index) const; | 32 | std::string GetName() const override; |
| 32 | std::string GetEntryName(u32 index) const; | 33 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; |
| 33 | u64 GetFileOffset(const std::string& name) const; | 34 | void PrintDebugInfo() const; |
| 34 | u64 GetFileSize(const std::string& name) const; | ||
| 35 | 35 | ||
| 36 | void Print() const; | 36 | protected: |
| 37 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 37 | 38 | ||
| 38 | private: | 39 | private: |
| 39 | struct Header { | 40 | struct Header { |
| @@ -72,16 +73,14 @@ private: | |||
| 72 | 73 | ||
| 73 | #pragma pack(pop) | 74 | #pragma pack(pop) |
| 74 | 75 | ||
| 75 | struct FileEntry { | 76 | Loader::ResultStatus status; |
| 76 | FSEntry fs_entry; | ||
| 77 | std::string name; | ||
| 78 | }; | ||
| 79 | 77 | ||
| 80 | Header pfs_header; | 78 | Header pfs_header; |
| 81 | bool is_hfs; | 79 | bool is_hfs; |
| 82 | size_t content_offset; | 80 | size_t content_offset; |
| 83 | 81 | ||
| 84 | std::vector<FileEntry> pfs_entries; | 82 | std::vector<VirtualFile> pfs_files; |
| 83 | std::vector<VirtualDir> pfs_dirs; | ||
| 85 | }; | 84 | }; |
| 86 | 85 | ||
| 87 | } // namespace FileSys | 86 | } // namespace FileSys |
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 226811115..63d4b6e4f 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp | |||
| @@ -9,40 +9,29 @@ | |||
| 9 | 9 | ||
| 10 | namespace FileSys { | 10 | namespace FileSys { |
| 11 | 11 | ||
| 12 | Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) { | 12 | Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { |
| 13 | FileUtil::IOFile file(file_path, "rb"); | 13 | size_t total_size = static_cast<size_t>(file->GetSize()); |
| 14 | if (!file.IsOpen()) | 14 | if (total_size < sizeof(Header)) |
| 15 | return Loader::ResultStatus::Error; | 15 | return Loader::ResultStatus::Error; |
| 16 | 16 | ||
| 17 | std::vector<u8> file_data(file.GetSize()); | 17 | // TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable. |
| 18 | 18 | std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header)); | |
| 19 | if (!file.ReadBytes(file_data.data(), file_data.size())) | 19 | if (sizeof(Header) != npdm_header_data.size()) |
| 20 | return Loader::ResultStatus::Error; | 20 | return Loader::ResultStatus::Error; |
| 21 | std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header)); | ||
| 21 | 22 | ||
| 22 | Loader::ResultStatus result = Load(file_data); | 23 | std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset); |
| 23 | if (result != Loader::ResultStatus::Success) | 24 | if (sizeof(AcidHeader) != acid_header_data.size()) |
| 24 | LOG_ERROR(Service_FS, "Failed to load NPDM from file {}!", file_path); | ||
| 25 | |||
| 26 | return result; | ||
| 27 | } | ||
| 28 | |||
| 29 | Loader::ResultStatus ProgramMetadata::Load(const std::vector<u8> file_data, size_t offset) { | ||
| 30 | size_t total_size = static_cast<size_t>(file_data.size() - offset); | ||
| 31 | if (total_size < sizeof(Header)) | ||
| 32 | return Loader::ResultStatus::Error; | 25 | return Loader::ResultStatus::Error; |
| 26 | std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader)); | ||
| 33 | 27 | ||
| 34 | size_t header_offset = offset; | 28 | if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) |
| 35 | memcpy(&npdm_header, &file_data[offset], sizeof(Header)); | 29 | return Loader::ResultStatus::Error; |
| 36 | |||
| 37 | size_t aci_offset = header_offset + npdm_header.aci_offset; | ||
| 38 | size_t acid_offset = header_offset + npdm_header.acid_offset; | ||
| 39 | memcpy(&aci_header, &file_data[aci_offset], sizeof(AciHeader)); | ||
| 40 | memcpy(&acid_header, &file_data[acid_offset], sizeof(AcidHeader)); | ||
| 41 | 30 | ||
| 42 | size_t fac_offset = acid_offset + acid_header.fac_offset; | 31 | if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) |
| 43 | size_t fah_offset = aci_offset + aci_header.fah_offset; | 32 | return Loader::ResultStatus::Error; |
| 44 | memcpy(&acid_file_access, &file_data[fac_offset], sizeof(FileAccessControl)); | 33 | if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) |
| 45 | memcpy(&aci_file_access, &file_data[fah_offset], sizeof(FileAccessHeader)); | 34 | return Loader::ResultStatus::Error; |
| 46 | 35 | ||
| 47 | return Loader::ResultStatus::Success; | 36 | return Loader::ResultStatus::Success; |
| 48 | } | 37 | } |
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index b80a08485..06a7315db 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h | |||
| @@ -10,6 +10,7 @@ | |||
| 10 | #include "common/bit_field.h" | 10 | #include "common/bit_field.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/swap.h" | 12 | #include "common/swap.h" |
| 13 | #include "partition_filesystem.h" | ||
| 13 | 14 | ||
| 14 | namespace Loader { | 15 | namespace Loader { |
| 15 | enum class ResultStatus; | 16 | enum class ResultStatus; |
| @@ -37,8 +38,7 @@ enum class ProgramFilePermission : u64 { | |||
| 37 | */ | 38 | */ |
| 38 | class ProgramMetadata { | 39 | class ProgramMetadata { |
| 39 | public: | 40 | public: |
| 40 | Loader::ResultStatus Load(const std::string& file_path); | 41 | Loader::ResultStatus Load(VirtualFile file); |
| 41 | Loader::ResultStatus Load(const std::vector<u8> file_data, size_t offset = 0); | ||
| 42 | 42 | ||
| 43 | bool Is64BitProgram() const; | 43 | bool Is64BitProgram() const; |
| 44 | ProgramAddressSpaceType GetAddressSpaceType() const; | 44 | ProgramAddressSpaceType GetAddressSpaceType() const; |
| @@ -51,6 +51,7 @@ public: | |||
| 51 | void Print() const; | 51 | void Print() const; |
| 52 | 52 | ||
| 53 | private: | 53 | private: |
| 54 | // TODO(DarkLordZach): BitField is not trivially copyable. | ||
| 54 | struct Header { | 55 | struct Header { |
| 55 | std::array<char, 4> magic; | 56 | std::array<char, 4> magic; |
| 56 | std::array<u8, 8> reserved; | 57 | std::array<u8, 8> reserved; |
| @@ -77,6 +78,7 @@ private: | |||
| 77 | 78 | ||
| 78 | static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong"); | 79 | static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong"); |
| 79 | 80 | ||
| 81 | // TODO(DarkLordZach): BitField is not trivially copyable. | ||
| 80 | struct AcidHeader { | 82 | struct AcidHeader { |
| 81 | std::array<u8, 0x100> signature; | 83 | std::array<u8, 0x100> signature; |
| 82 | std::array<u8, 0x100> nca_modulus; | 84 | std::array<u8, 0x100> nca_modulus; |
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp deleted file mode 100644 index 84ae0d99b..000000000 --- a/src/core/file_sys/romfs_factory.cpp +++ /dev/null | |||
| @@ -1,38 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <memory> | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/file_sys/romfs_factory.h" | ||
| 10 | #include "core/file_sys/romfs_filesystem.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) { | ||
| 15 | // Load the RomFS from the app | ||
| 16 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { | ||
| 17 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& path) { | ||
| 22 | auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size); | ||
| 23 | return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); | ||
| 24 | } | ||
| 25 | |||
| 26 | ResultCode RomFS_Factory::Format(const Path& path) { | ||
| 27 | LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName()); | ||
| 28 | // TODO(bunnei): Find the right error code for this | ||
| 29 | return ResultCode(-1); | ||
| 30 | } | ||
| 31 | |||
| 32 | ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const { | ||
| 33 | LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); | ||
| 34 | // TODO(bunnei): Find the right error code for this | ||
| 35 | return ResultCode(-1); | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h deleted file mode 100644 index e0698e642..000000000 --- a/src/core/file_sys/romfs_factory.h +++ /dev/null | |||
| @@ -1,35 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "core/file_sys/filesystem.h" | ||
| 12 | #include "core/hle/result.h" | ||
| 13 | #include "core/loader/loader.h" | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | |||
| 17 | /// File system interface to the RomFS archive | ||
| 18 | class RomFS_Factory final : public FileSystemFactory { | ||
| 19 | public: | ||
| 20 | explicit RomFS_Factory(Loader::AppLoader& app_loader); | ||
| 21 | |||
| 22 | std::string GetName() const override { | ||
| 23 | return "ArchiveFactory_RomFS"; | ||
| 24 | } | ||
| 25 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; | ||
| 26 | ResultCode Format(const Path& path) override; | ||
| 27 | ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||
| 28 | |||
| 29 | private: | ||
| 30 | std::shared_ptr<FileUtil::IOFile> romfs_file; | ||
| 31 | u64 data_offset; | ||
| 32 | u64 data_size; | ||
| 33 | }; | ||
| 34 | |||
| 35 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/romfs_filesystem.cpp b/src/core/file_sys/romfs_filesystem.cpp deleted file mode 100644 index 83162622b..000000000 --- a/src/core/file_sys/romfs_filesystem.cpp +++ /dev/null | |||
| @@ -1,110 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <cstring> | ||
| 6 | #include <memory> | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/file_sys/romfs_filesystem.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | std::string RomFS_FileSystem::GetName() const { | ||
| 14 | return "RomFS"; | ||
| 15 | } | ||
| 16 | |||
| 17 | ResultVal<std::unique_ptr<StorageBackend>> RomFS_FileSystem::OpenFile(const std::string& path, | ||
| 18 | Mode mode) const { | ||
| 19 | return MakeResult<std::unique_ptr<StorageBackend>>( | ||
| 20 | std::make_unique<RomFS_Storage>(romfs_file, data_offset, data_size)); | ||
| 21 | } | ||
| 22 | |||
| 23 | ResultCode RomFS_FileSystem::DeleteFile(const std::string& path) const { | ||
| 24 | LOG_CRITICAL(Service_FS, "Attempted to delete a file from an ROMFS archive ({}).", GetName()); | ||
| 25 | // TODO(bunnei): Use correct error code | ||
| 26 | return ResultCode(-1); | ||
| 27 | } | ||
| 28 | |||
| 29 | ResultCode RomFS_FileSystem::RenameFile(const std::string& src_path, | ||
| 30 | const std::string& dest_path) const { | ||
| 31 | LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName()); | ||
| 32 | // TODO(wwylele): Use correct error code | ||
| 33 | return ResultCode(-1); | ||
| 34 | } | ||
| 35 | |||
| 36 | ResultCode RomFS_FileSystem::DeleteDirectory(const Path& path) const { | ||
| 37 | LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).", | ||
| 38 | GetName()); | ||
| 39 | // TODO(wwylele): Use correct error code | ||
| 40 | return ResultCode(-1); | ||
| 41 | } | ||
| 42 | |||
| 43 | ResultCode RomFS_FileSystem::DeleteDirectoryRecursively(const Path& path) const { | ||
| 44 | LOG_CRITICAL(Service_FS, "Attempted to delete a directory from an ROMFS archive ({}).", | ||
| 45 | GetName()); | ||
| 46 | // TODO(wwylele): Use correct error code | ||
| 47 | return ResultCode(-1); | ||
| 48 | } | ||
| 49 | |||
| 50 | ResultCode RomFS_FileSystem::CreateFile(const std::string& path, u64 size) const { | ||
| 51 | LOG_CRITICAL(Service_FS, "Attempted to create a file in an ROMFS archive ({}).", GetName()); | ||
| 52 | // TODO(bunnei): Use correct error code | ||
| 53 | return ResultCode(-1); | ||
| 54 | } | ||
| 55 | |||
| 56 | ResultCode RomFS_FileSystem::CreateDirectory(const std::string& path) const { | ||
| 57 | LOG_CRITICAL(Service_FS, "Attempted to create a directory in an ROMFS archive ({}).", | ||
| 58 | GetName()); | ||
| 59 | // TODO(wwylele): Use correct error code | ||
| 60 | return ResultCode(-1); | ||
| 61 | } | ||
| 62 | |||
| 63 | ResultCode RomFS_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { | ||
| 64 | LOG_CRITICAL(Service_FS, "Attempted to rename a file within an ROMFS archive ({}).", GetName()); | ||
| 65 | // TODO(wwylele): Use correct error code | ||
| 66 | return ResultCode(-1); | ||
| 67 | } | ||
| 68 | |||
| 69 | ResultVal<std::unique_ptr<DirectoryBackend>> RomFS_FileSystem::OpenDirectory( | ||
| 70 | const std::string& path) const { | ||
| 71 | LOG_WARNING(Service_FS, "Opening Directory in a ROMFS archive"); | ||
| 72 | return MakeResult<std::unique_ptr<DirectoryBackend>>(std::make_unique<ROMFSDirectory>()); | ||
| 73 | } | ||
| 74 | |||
| 75 | u64 RomFS_FileSystem::GetFreeSpaceSize() const { | ||
| 76 | LOG_WARNING(Service_FS, "Attempted to get the free space in an ROMFS archive"); | ||
| 77 | return 0; | ||
| 78 | } | ||
| 79 | |||
| 80 | ResultVal<FileSys::EntryType> RomFS_FileSystem::GetEntryType(const std::string& path) const { | ||
| 81 | LOG_CRITICAL(Service_FS, "Called within an ROMFS archive (path {}).", path); | ||
| 82 | // TODO(wwylele): Use correct error code | ||
| 83 | return ResultCode(-1); | ||
| 84 | } | ||
| 85 | |||
| 86 | ResultVal<size_t> RomFS_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { | ||
| 87 | LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); | ||
| 88 | romfs_file->Seek(data_offset + offset, SEEK_SET); | ||
| 89 | size_t read_length = (size_t)std::min((u64)length, data_size - offset); | ||
| 90 | |||
| 91 | return MakeResult<size_t>(romfs_file->ReadBytes(buffer, read_length)); | ||
| 92 | } | ||
| 93 | |||
| 94 | ResultVal<size_t> RomFS_Storage::Write(const u64 offset, const size_t length, const bool flush, | ||
| 95 | const u8* buffer) const { | ||
| 96 | LOG_ERROR(Service_FS, "Attempted to write to ROMFS file"); | ||
| 97 | // TODO(Subv): Find error code | ||
| 98 | return MakeResult<size_t>(0); | ||
| 99 | } | ||
| 100 | |||
| 101 | u64 RomFS_Storage::GetSize() const { | ||
| 102 | return data_size; | ||
| 103 | } | ||
| 104 | |||
| 105 | bool RomFS_Storage::SetSize(const u64 size) const { | ||
| 106 | LOG_ERROR(Service_FS, "Attempted to set the size of an ROMFS file"); | ||
| 107 | return false; | ||
| 108 | } | ||
| 109 | |||
| 110 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/romfs_filesystem.h b/src/core/file_sys/romfs_filesystem.h deleted file mode 100644 index ba9d85823..000000000 --- a/src/core/file_sys/romfs_filesystem.h +++ /dev/null | |||
| @@ -1,85 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <cstddef> | ||
| 8 | #include <memory> | ||
| 9 | #include <string> | ||
| 10 | #include <vector> | ||
| 11 | #include "common/common_types.h" | ||
| 12 | #include "common/file_util.h" | ||
| 13 | #include "core/file_sys/directory.h" | ||
| 14 | #include "core/file_sys/filesystem.h" | ||
| 15 | #include "core/file_sys/storage.h" | ||
| 16 | #include "core/hle/result.h" | ||
| 17 | |||
| 18 | namespace FileSys { | ||
| 19 | |||
| 20 | /** | ||
| 21 | * Helper which implements an interface to deal with Switch .istorage ROMFS images used in some | ||
| 22 | * archives This should be subclassed by concrete archive types, which will provide the input data | ||
| 23 | * (load the raw ROMFS archive) and override any required methods | ||
| 24 | */ | ||
| 25 | class RomFS_FileSystem : public FileSystemBackend { | ||
| 26 | public: | ||
| 27 | RomFS_FileSystem(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) | ||
| 28 | : romfs_file(file), data_offset(offset), data_size(size) {} | ||
| 29 | |||
| 30 | std::string GetName() const override; | ||
| 31 | |||
| 32 | ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, | ||
| 33 | Mode mode) const override; | ||
| 34 | ResultCode DeleteFile(const std::string& path) const override; | ||
| 35 | ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const override; | ||
| 36 | ResultCode DeleteDirectory(const Path& path) const override; | ||
| 37 | ResultCode DeleteDirectoryRecursively(const Path& path) const override; | ||
| 38 | ResultCode CreateFile(const std::string& path, u64 size) const override; | ||
| 39 | ResultCode CreateDirectory(const std::string& path) const override; | ||
| 40 | ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const override; | ||
| 41 | ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory( | ||
| 42 | const std::string& path) const override; | ||
| 43 | u64 GetFreeSpaceSize() const override; | ||
| 44 | ResultVal<EntryType> GetEntryType(const std::string& path) const override; | ||
| 45 | |||
| 46 | protected: | ||
| 47 | std::shared_ptr<FileUtil::IOFile> romfs_file; | ||
| 48 | u64 data_offset; | ||
| 49 | u64 data_size; | ||
| 50 | }; | ||
| 51 | |||
| 52 | class RomFS_Storage : public StorageBackend { | ||
| 53 | public: | ||
| 54 | RomFS_Storage(std::shared_ptr<FileUtil::IOFile> file, u64 offset, u64 size) | ||
| 55 | : romfs_file(file), data_offset(offset), data_size(size) {} | ||
| 56 | |||
| 57 | ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const override; | ||
| 58 | ResultVal<size_t> Write(u64 offset, size_t length, bool flush, const u8* buffer) const override; | ||
| 59 | u64 GetSize() const override; | ||
| 60 | bool SetSize(u64 size) const override; | ||
| 61 | bool Close() const override { | ||
| 62 | return false; | ||
| 63 | } | ||
| 64 | void Flush() const override {} | ||
| 65 | |||
| 66 | private: | ||
| 67 | std::shared_ptr<FileUtil::IOFile> romfs_file; | ||
| 68 | u64 data_offset; | ||
| 69 | u64 data_size; | ||
| 70 | }; | ||
| 71 | |||
| 72 | class ROMFSDirectory : public DirectoryBackend { | ||
| 73 | public: | ||
| 74 | u64 Read(const u64 count, Entry* entries) override { | ||
| 75 | return 0; | ||
| 76 | } | ||
| 77 | u64 GetEntryCount() const override { | ||
| 78 | return 0; | ||
| 79 | } | ||
| 80 | bool Close() const override { | ||
| 81 | return false; | ||
| 82 | } | ||
| 83 | }; | ||
| 84 | |||
| 85 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp deleted file mode 100644 index d78baf9c3..000000000 --- a/src/core/file_sys/savedata_factory.cpp +++ /dev/null | |||
| @@ -1,54 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/file_sys/disk_filesystem.h" | ||
| 10 | #include "core/file_sys/savedata_factory.h" | ||
| 11 | #include "core/hle/kernel/process.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | SaveData_Factory::SaveData_Factory(std::string nand_directory) | ||
| 16 | : nand_directory(std::move(nand_directory)) {} | ||
| 17 | |||
| 18 | ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) { | ||
| 19 | std::string save_directory = GetFullPath(); | ||
| 20 | // Return an error if the save data doesn't actually exist. | ||
| 21 | if (!FileUtil::IsDirectory(save_directory)) { | ||
| 22 | // TODO(Subv): Find out correct error code. | ||
| 23 | return ResultCode(-1); | ||
| 24 | } | ||
| 25 | |||
| 26 | auto archive = std::make_unique<Disk_FileSystem>(save_directory); | ||
| 27 | return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); | ||
| 28 | } | ||
| 29 | |||
| 30 | ResultCode SaveData_Factory::Format(const Path& path) { | ||
| 31 | LOG_WARNING(Service_FS, "Format archive {}", GetName()); | ||
| 32 | // Create the save data directory. | ||
| 33 | if (!FileUtil::CreateFullPath(GetFullPath())) { | ||
| 34 | // TODO(Subv): Find the correct error code. | ||
| 35 | return ResultCode(-1); | ||
| 36 | } | ||
| 37 | |||
| 38 | return RESULT_SUCCESS; | ||
| 39 | } | ||
| 40 | |||
| 41 | ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const { | ||
| 42 | LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); | ||
| 43 | // TODO(bunnei): Find the right error code for this | ||
| 44 | return ResultCode(-1); | ||
| 45 | } | ||
| 46 | |||
| 47 | std::string SaveData_Factory::GetFullPath() const { | ||
| 48 | u64 title_id = Core::CurrentProcess()->program_id; | ||
| 49 | // TODO(Subv): Somehow obtain this value. | ||
| 50 | u32 user = 0; | ||
| 51 | return fmt::format("{}save/{:016X}/{:08X}/", nand_directory, title_id, user); | ||
| 52 | } | ||
| 53 | |||
| 54 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h deleted file mode 100644 index 73a42aab6..000000000 --- a/src/core/file_sys/savedata_factory.h +++ /dev/null | |||
| @@ -1,33 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/file_sys/filesystem.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | /// File system interface to the SaveData archive | ||
| 16 | class SaveData_Factory final : public FileSystemFactory { | ||
| 17 | public: | ||
| 18 | explicit SaveData_Factory(std::string nand_directory); | ||
| 19 | |||
| 20 | std::string GetName() const override { | ||
| 21 | return "SaveData_Factory"; | ||
| 22 | } | ||
| 23 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; | ||
| 24 | ResultCode Format(const Path& path) override; | ||
| 25 | ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||
| 26 | |||
| 27 | private: | ||
| 28 | std::string nand_directory; | ||
| 29 | |||
| 30 | std::string GetFullPath() const; | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp deleted file mode 100644 index 2e5ffb764..000000000 --- a/src/core/file_sys/sdmc_factory.cpp +++ /dev/null | |||
| @@ -1,39 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "common/string_util.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "core/file_sys/disk_filesystem.h" | ||
| 11 | #include "core/file_sys/sdmc_factory.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | SDMC_Factory::SDMC_Factory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {} | ||
| 16 | |||
| 17 | ResultVal<std::unique_ptr<FileSystemBackend>> SDMC_Factory::Open(const Path& path) { | ||
| 18 | // Create the SD Card directory if it doesn't already exist. | ||
| 19 | if (!FileUtil::IsDirectory(sd_directory)) { | ||
| 20 | FileUtil::CreateFullPath(sd_directory); | ||
| 21 | } | ||
| 22 | |||
| 23 | auto archive = std::make_unique<Disk_FileSystem>(sd_directory); | ||
| 24 | return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); | ||
| 25 | } | ||
| 26 | |||
| 27 | ResultCode SDMC_Factory::Format(const Path& path) { | ||
| 28 | LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName()); | ||
| 29 | // TODO(Subv): Find the right error code for this | ||
| 30 | return ResultCode(-1); | ||
| 31 | } | ||
| 32 | |||
| 33 | ResultVal<ArchiveFormatInfo> SDMC_Factory::GetFormatInfo(const Path& path) const { | ||
| 34 | LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); | ||
| 35 | // TODO(bunnei): Find the right error code for this | ||
| 36 | return ResultCode(-1); | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h deleted file mode 100644 index 93becda25..000000000 --- a/src/core/file_sys/sdmc_factory.h +++ /dev/null | |||
| @@ -1,31 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/file_sys/filesystem.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | /// File system interface to the SDCard archive | ||
| 16 | class SDMC_Factory final : public FileSystemFactory { | ||
| 17 | public: | ||
| 18 | explicit SDMC_Factory(std::string sd_directory); | ||
| 19 | |||
| 20 | std::string GetName() const override { | ||
| 21 | return "SDMC_Factory"; | ||
| 22 | } | ||
| 23 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; | ||
| 24 | ResultCode Format(const Path& path) override; | ||
| 25 | ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||
| 26 | |||
| 27 | private: | ||
| 28 | std::string sd_directory; | ||
| 29 | }; | ||
| 30 | |||
| 31 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp new file mode 100644 index 000000000..c99071d6a --- /dev/null +++ b/src/core/file_sys/vfs.cpp | |||
| @@ -0,0 +1,187 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <numeric> | ||
| 7 | #include "common/file_util.h" | ||
| 8 | #include "core/file_sys/vfs.h" | ||
| 9 | |||
| 10 | namespace FileSys { | ||
| 11 | |||
| 12 | VfsFile::~VfsFile() = default; | ||
| 13 | |||
| 14 | std::string VfsFile::GetExtension() const { | ||
| 15 | return FileUtil::GetExtensionFromFilename(GetName()); | ||
| 16 | } | ||
| 17 | |||
| 18 | VfsDirectory::~VfsDirectory() = default; | ||
| 19 | |||
| 20 | boost::optional<u8> VfsFile::ReadByte(size_t offset) const { | ||
| 21 | u8 out{}; | ||
| 22 | size_t size = Read(&out, 1, offset); | ||
| 23 | if (size == 1) | ||
| 24 | return out; | ||
| 25 | |||
| 26 | return boost::none; | ||
| 27 | } | ||
| 28 | |||
| 29 | std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const { | ||
| 30 | std::vector<u8> out(size); | ||
| 31 | size_t read_size = Read(out.data(), size, offset); | ||
| 32 | out.resize(read_size); | ||
| 33 | return out; | ||
| 34 | } | ||
| 35 | |||
| 36 | std::vector<u8> VfsFile::ReadAllBytes() const { | ||
| 37 | return ReadBytes(GetSize()); | ||
| 38 | } | ||
| 39 | |||
| 40 | bool VfsFile::WriteByte(u8 data, size_t offset) { | ||
| 41 | return Write(&data, 1, offset) == 1; | ||
| 42 | } | ||
| 43 | |||
| 44 | size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) { | ||
| 45 | return Write(data.data(), data.size(), offset); | ||
| 46 | } | ||
| 47 | |||
| 48 | std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& path) const { | ||
| 49 | auto vec = FileUtil::SplitPathComponents(path); | ||
| 50 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | ||
| 51 | vec.end()); | ||
| 52 | if (vec.empty()) | ||
| 53 | return nullptr; | ||
| 54 | if (vec.size() == 1) | ||
| 55 | return GetFile(vec[0]); | ||
| 56 | auto dir = GetSubdirectory(vec[0]); | ||
| 57 | for (size_t i = 1; i < vec.size() - 1; ++i) { | ||
| 58 | if (dir == nullptr) | ||
| 59 | return nullptr; | ||
| 60 | dir = dir->GetSubdirectory(vec[i]); | ||
| 61 | } | ||
| 62 | if (dir == nullptr) | ||
| 63 | return nullptr; | ||
| 64 | return dir->GetFile(vec.back()); | ||
| 65 | } | ||
| 66 | |||
| 67 | std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const std::string& path) const { | ||
| 68 | if (IsRoot()) | ||
| 69 | return GetFileRelative(path); | ||
| 70 | |||
| 71 | return GetParentDirectory()->GetFileAbsolute(path); | ||
| 72 | } | ||
| 73 | |||
| 74 | std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::string& path) const { | ||
| 75 | auto vec = FileUtil::SplitPathComponents(path); | ||
| 76 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | ||
| 77 | vec.end()); | ||
| 78 | if (vec.empty()) | ||
| 79 | // return std::shared_ptr<VfsDirectory>(this); | ||
| 80 | return nullptr; | ||
| 81 | auto dir = GetSubdirectory(vec[0]); | ||
| 82 | for (size_t i = 1; i < vec.size(); ++i) { | ||
| 83 | dir = dir->GetSubdirectory(vec[i]); | ||
| 84 | } | ||
| 85 | return dir; | ||
| 86 | } | ||
| 87 | |||
| 88 | std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(const std::string& path) const { | ||
| 89 | if (IsRoot()) | ||
| 90 | return GetDirectoryRelative(path); | ||
| 91 | |||
| 92 | return GetParentDirectory()->GetDirectoryAbsolute(path); | ||
| 93 | } | ||
| 94 | |||
| 95 | std::shared_ptr<VfsFile> VfsDirectory::GetFile(const std::string& name) const { | ||
| 96 | const auto& files = GetFiles(); | ||
| 97 | const auto iter = std::find_if(files.begin(), files.end(), | ||
| 98 | [&name](const auto& file1) { return name == file1->GetName(); }); | ||
| 99 | return iter == files.end() ? nullptr : *iter; | ||
| 100 | } | ||
| 101 | |||
| 102 | std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(const std::string& name) const { | ||
| 103 | const auto& subs = GetSubdirectories(); | ||
| 104 | const auto iter = std::find_if(subs.begin(), subs.end(), | ||
| 105 | [&name](const auto& file1) { return name == file1->GetName(); }); | ||
| 106 | return iter == subs.end() ? nullptr : *iter; | ||
| 107 | } | ||
| 108 | |||
| 109 | bool VfsDirectory::IsRoot() const { | ||
| 110 | return GetParentDirectory() == nullptr; | ||
| 111 | } | ||
| 112 | |||
| 113 | size_t VfsDirectory::GetSize() const { | ||
| 114 | const auto& files = GetFiles(); | ||
| 115 | const auto file_total = | ||
| 116 | std::accumulate(files.begin(), files.end(), 0ull, | ||
| 117 | [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); | ||
| 118 | |||
| 119 | const auto& sub_dir = GetSubdirectories(); | ||
| 120 | const auto subdir_total = | ||
| 121 | std::accumulate(sub_dir.begin(), sub_dir.end(), 0ull, | ||
| 122 | [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); | ||
| 123 | |||
| 124 | return file_total + subdir_total; | ||
| 125 | } | ||
| 126 | |||
| 127 | bool VfsDirectory::DeleteSubdirectoryRecursive(const std::string& name) { | ||
| 128 | auto dir = GetSubdirectory(name); | ||
| 129 | if (dir == nullptr) | ||
| 130 | return false; | ||
| 131 | |||
| 132 | bool success = true; | ||
| 133 | for (const auto& file : dir->GetFiles()) { | ||
| 134 | if (!DeleteFile(file->GetName())) | ||
| 135 | success = false; | ||
| 136 | } | ||
| 137 | |||
| 138 | for (const auto& sdir : dir->GetSubdirectories()) { | ||
| 139 | if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) | ||
| 140 | success = false; | ||
| 141 | } | ||
| 142 | |||
| 143 | return success; | ||
| 144 | } | ||
| 145 | |||
| 146 | bool VfsDirectory::Copy(const std::string& src, const std::string& dest) { | ||
| 147 | const auto f1 = GetFile(src); | ||
| 148 | auto f2 = CreateFile(dest); | ||
| 149 | if (f1 == nullptr || f2 == nullptr) | ||
| 150 | return false; | ||
| 151 | |||
| 152 | if (!f2->Resize(f1->GetSize())) { | ||
| 153 | DeleteFile(dest); | ||
| 154 | return false; | ||
| 155 | } | ||
| 156 | |||
| 157 | return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); | ||
| 158 | } | ||
| 159 | |||
| 160 | bool ReadOnlyVfsDirectory::IsWritable() const { | ||
| 161 | return false; | ||
| 162 | } | ||
| 163 | |||
| 164 | bool ReadOnlyVfsDirectory::IsReadable() const { | ||
| 165 | return true; | ||
| 166 | } | ||
| 167 | |||
| 168 | std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(const std::string& name) { | ||
| 169 | return nullptr; | ||
| 170 | } | ||
| 171 | |||
| 172 | std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(const std::string& name) { | ||
| 173 | return nullptr; | ||
| 174 | } | ||
| 175 | |||
| 176 | bool ReadOnlyVfsDirectory::DeleteSubdirectory(const std::string& name) { | ||
| 177 | return false; | ||
| 178 | } | ||
| 179 | |||
| 180 | bool ReadOnlyVfsDirectory::DeleteFile(const std::string& name) { | ||
| 181 | return false; | ||
| 182 | } | ||
| 183 | |||
| 184 | bool ReadOnlyVfsDirectory::Rename(const std::string& name) { | ||
| 185 | return false; | ||
| 186 | } | ||
| 187 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h new file mode 100644 index 000000000..0e30991b4 --- /dev/null +++ b/src/core/file_sys/vfs.h | |||
| @@ -0,0 +1,220 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include <type_traits> | ||
| 10 | #include <vector> | ||
| 11 | #include "boost/optional.hpp" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/file_util.h" | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | struct VfsFile; | ||
| 17 | struct VfsDirectory; | ||
| 18 | |||
| 19 | // Convenience typedefs to use VfsDirectory and VfsFile | ||
| 20 | using VirtualDir = std::shared_ptr<FileSys::VfsDirectory>; | ||
| 21 | using VirtualFile = std::shared_ptr<FileSys::VfsFile>; | ||
| 22 | |||
| 23 | // A class representing a file in an abstract filesystem. | ||
| 24 | struct VfsFile : NonCopyable { | ||
| 25 | virtual ~VfsFile(); | ||
| 26 | |||
| 27 | // Retrieves the file name. | ||
| 28 | virtual std::string GetName() const = 0; | ||
| 29 | // Retrieves the extension of the file name. | ||
| 30 | virtual std::string GetExtension() const; | ||
| 31 | // Retrieves the size of the file. | ||
| 32 | virtual size_t GetSize() const = 0; | ||
| 33 | // Resizes the file to new_size. Returns whether or not the operation was successful. | ||
| 34 | virtual bool Resize(size_t new_size) = 0; | ||
| 35 | // Gets a pointer to the directory containing this file, returning nullptr if there is none. | ||
| 36 | virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0; | ||
| 37 | |||
| 38 | // Returns whether or not the file can be written to. | ||
| 39 | virtual bool IsWritable() const = 0; | ||
| 40 | // Returns whether or not the file can be read from. | ||
| 41 | virtual bool IsReadable() const = 0; | ||
| 42 | |||
| 43 | // The primary method of reading from the file. Reads length bytes into data starting at offset | ||
| 44 | // into file. Returns number of bytes successfully read. | ||
| 45 | virtual size_t Read(u8* data, size_t length, size_t offset = 0) const = 0; | ||
| 46 | // The primary method of writing to the file. Writes length bytes from data starting at offset | ||
| 47 | // into file. Returns number of bytes successfully written. | ||
| 48 | virtual size_t Write(const u8* data, size_t length, size_t offset = 0) = 0; | ||
| 49 | |||
| 50 | // Reads exactly one byte at the offset provided, returning boost::none on error. | ||
| 51 | virtual boost::optional<u8> ReadByte(size_t offset = 0) const; | ||
| 52 | // Reads size bytes starting at offset in file into a vector. | ||
| 53 | virtual std::vector<u8> ReadBytes(size_t size, size_t offset = 0) const; | ||
| 54 | // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), | ||
| 55 | // 0)' | ||
| 56 | virtual std::vector<u8> ReadAllBytes() const; | ||
| 57 | |||
| 58 | // Reads an array of type T, size number_elements starting at offset. | ||
| 59 | // Returns the number of bytes (sizeof(T)*number_elements) read successfully. | ||
| 60 | template <typename T> | ||
| 61 | size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const { | ||
| 62 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 63 | "Data type must be trivially copyable."); | ||
| 64 | |||
| 65 | return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); | ||
| 66 | } | ||
| 67 | |||
| 68 | // Reads size bytes into the memory starting at data starting at offset into the file. | ||
| 69 | // Returns the number of bytes read successfully. | ||
| 70 | template <typename T> | ||
| 71 | size_t ReadBytes(T* data, size_t size, size_t offset = 0) const { | ||
| 72 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 73 | "Data type must be trivially copyable."); | ||
| 74 | return Read(reinterpret_cast<u8*>(data), size, offset); | ||
| 75 | } | ||
| 76 | |||
| 77 | // Reads one object of type T starting at offset in file. | ||
| 78 | // Returns the number of bytes read successfully (sizeof(T)). | ||
| 79 | template <typename T> | ||
| 80 | size_t ReadObject(T* data, size_t offset = 0) const { | ||
| 81 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 82 | "Data type must be trivially copyable."); | ||
| 83 | return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); | ||
| 84 | } | ||
| 85 | |||
| 86 | // Writes exactly one byte to offset in file and retuns whether or not the byte was written | ||
| 87 | // successfully. | ||
| 88 | virtual bool WriteByte(u8 data, size_t offset = 0); | ||
| 89 | // Writes a vector of bytes to offset in file and returns the number of bytes successfully | ||
| 90 | // written. | ||
| 91 | virtual size_t WriteBytes(std::vector<u8> data, size_t offset = 0); | ||
| 92 | |||
| 93 | // Writes an array of type T, size number_elements to offset in file. | ||
| 94 | // Returns the number of bytes (sizeof(T)*number_elements) written successfully. | ||
| 95 | template <typename T> | ||
| 96 | size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) { | ||
| 97 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 98 | "Data type must be trivially copyable."); | ||
| 99 | |||
| 100 | return Write(data, number_elements * sizeof(T), offset); | ||
| 101 | } | ||
| 102 | |||
| 103 | // Writes size bytes starting at memory location data to offset in file. | ||
| 104 | // Returns the number of bytes written successfully. | ||
| 105 | template <typename T> | ||
| 106 | size_t WriteBytes(T* data, size_t size, size_t offset = 0) { | ||
| 107 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 108 | "Data type must be trivially copyable."); | ||
| 109 | return Write(reinterpret_cast<u8*>(data), size, offset); | ||
| 110 | } | ||
| 111 | |||
| 112 | // Writes one object of type T to offset in file. | ||
| 113 | // Returns the number of bytes written successfully (sizeof(T)). | ||
| 114 | template <typename T> | ||
| 115 | size_t WriteObject(const T& data, size_t offset = 0) { | ||
| 116 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 117 | "Data type must be trivially copyable."); | ||
| 118 | return Write(&data, sizeof(T), offset); | ||
| 119 | } | ||
| 120 | |||
| 121 | // Renames the file to name. Returns whether or not the operation was successsful. | ||
| 122 | virtual bool Rename(const std::string& name) = 0; | ||
| 123 | }; | ||
| 124 | |||
| 125 | // A class representing a directory in an abstract filesystem. | ||
| 126 | struct VfsDirectory : NonCopyable { | ||
| 127 | virtual ~VfsDirectory(); | ||
| 128 | |||
| 129 | // Retrives the file located at path as if the current directory was root. Returns nullptr if | ||
| 130 | // not found. | ||
| 131 | virtual std::shared_ptr<VfsFile> GetFileRelative(const std::string& path) const; | ||
| 132 | // Calls GetFileRelative(path) on the root of the current directory. | ||
| 133 | virtual std::shared_ptr<VfsFile> GetFileAbsolute(const std::string& path) const; | ||
| 134 | |||
| 135 | // Retrives the directory located at path as if the current directory was root. Returns nullptr | ||
| 136 | // if not found. | ||
| 137 | virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(const std::string& path) const; | ||
| 138 | // Calls GetDirectoryRelative(path) on the root of the current directory. | ||
| 139 | virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(const std::string& path) const; | ||
| 140 | |||
| 141 | // Returns a vector containing all of the files in this directory. | ||
| 142 | virtual std::vector<std::shared_ptr<VfsFile>> GetFiles() const = 0; | ||
| 143 | // Returns the file with filename matching name. Returns nullptr if directory dosen't have a | ||
| 144 | // file with name. | ||
| 145 | virtual std::shared_ptr<VfsFile> GetFile(const std::string& name) const; | ||
| 146 | |||
| 147 | // Returns a vector containing all of the subdirectories in this directory. | ||
| 148 | virtual std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const = 0; | ||
| 149 | // Returns the directory with name matching name. Returns nullptr if directory dosen't have a | ||
| 150 | // directory with name. | ||
| 151 | virtual std::shared_ptr<VfsDirectory> GetSubdirectory(const std::string& name) const; | ||
| 152 | |||
| 153 | // Returns whether or not the directory can be written to. | ||
| 154 | virtual bool IsWritable() const = 0; | ||
| 155 | // Returns whether of not the directory can be read from. | ||
| 156 | virtual bool IsReadable() const = 0; | ||
| 157 | |||
| 158 | // Returns whether or not the directory is the root of the current file tree. | ||
| 159 | virtual bool IsRoot() const; | ||
| 160 | |||
| 161 | // Returns the name of the directory. | ||
| 162 | virtual std::string GetName() const = 0; | ||
| 163 | // Returns the total size of all files and subdirectories in this directory. | ||
| 164 | virtual size_t GetSize() const; | ||
| 165 | // Returns the parent directory of this directory. Returns nullptr if this directory is root or | ||
| 166 | // has no parent. | ||
| 167 | virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0; | ||
| 168 | |||
| 169 | // Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr | ||
| 170 | // if the operation failed. | ||
| 171 | virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) = 0; | ||
| 172 | // Creates a new file with name name. Returns a pointer to the new file or nullptr if the | ||
| 173 | // operation failed. | ||
| 174 | virtual std::shared_ptr<VfsFile> CreateFile(const std::string& name) = 0; | ||
| 175 | |||
| 176 | // Deletes the subdirectory with name and returns true on success. | ||
| 177 | virtual bool DeleteSubdirectory(const std::string& name) = 0; | ||
| 178 | // Deletes all subdirectories and files of subdirectory with name recirsively and then deletes | ||
| 179 | // the subdirectory. Returns true on success. | ||
| 180 | virtual bool DeleteSubdirectoryRecursive(const std::string& name); | ||
| 181 | // Returnes whether or not the file with name name was deleted successfully. | ||
| 182 | virtual bool DeleteFile(const std::string& name) = 0; | ||
| 183 | |||
| 184 | // Returns whether or not this directory was renamed to name. | ||
| 185 | virtual bool Rename(const std::string& name) = 0; | ||
| 186 | |||
| 187 | // Returns whether or not the file with name src was successfully copied to a new file with name | ||
| 188 | // dest. | ||
| 189 | virtual bool Copy(const std::string& src, const std::string& dest); | ||
| 190 | |||
| 191 | // Interprets the file with name file instead as a directory of type directory. | ||
| 192 | // The directory must have a constructor that takes a single argument of type | ||
| 193 | // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a | ||
| 194 | // subdirectory in one call. | ||
| 195 | template <typename Directory> | ||
| 196 | bool InterpretAsDirectory(const std::string& file) { | ||
| 197 | auto file_p = GetFile(file); | ||
| 198 | if (file_p == nullptr) | ||
| 199 | return false; | ||
| 200 | return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file_p)); | ||
| 201 | } | ||
| 202 | |||
| 203 | protected: | ||
| 204 | // Backend for InterpretAsDirectory. | ||
| 205 | // Removes all references to file and adds a reference to dir in the directory's implementation. | ||
| 206 | virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0; | ||
| 207 | }; | ||
| 208 | |||
| 209 | // A convenience partial-implementation of VfsDirectory that stubs out methods that should only work | ||
| 210 | // if writable. This is to avoid redundant empty methods everywhere. | ||
| 211 | struct ReadOnlyVfsDirectory : public VfsDirectory { | ||
| 212 | bool IsWritable() const override; | ||
| 213 | bool IsReadable() const override; | ||
| 214 | std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override; | ||
| 215 | std::shared_ptr<VfsFile> CreateFile(const std::string& name) override; | ||
| 216 | bool DeleteSubdirectory(const std::string& name) override; | ||
| 217 | bool DeleteFile(const std::string& name) override; | ||
| 218 | bool Rename(const std::string& name) override; | ||
| 219 | }; | ||
| 220 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp new file mode 100644 index 000000000..288499cb5 --- /dev/null +++ b/src/core/file_sys/vfs_offset.cpp | |||
| @@ -0,0 +1,92 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "core/file_sys/vfs_offset.h" | ||
| 6 | |||
| 7 | namespace FileSys { | ||
| 8 | |||
| 9 | OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_, | ||
| 10 | const std::string& name_) | ||
| 11 | : file(file_), offset(offset_), size(size_), name(name_) {} | ||
| 12 | |||
| 13 | std::string OffsetVfsFile::GetName() const { | ||
| 14 | return name.empty() ? file->GetName() : name; | ||
| 15 | } | ||
| 16 | |||
| 17 | size_t OffsetVfsFile::GetSize() const { | ||
| 18 | return size; | ||
| 19 | } | ||
| 20 | |||
| 21 | bool OffsetVfsFile::Resize(size_t new_size) { | ||
| 22 | if (offset + new_size < file->GetSize()) { | ||
| 23 | size = new_size; | ||
| 24 | } else { | ||
| 25 | auto res = file->Resize(offset + new_size); | ||
| 26 | if (!res) | ||
| 27 | return false; | ||
| 28 | size = new_size; | ||
| 29 | } | ||
| 30 | |||
| 31 | return true; | ||
| 32 | } | ||
| 33 | |||
| 34 | std::shared_ptr<VfsDirectory> OffsetVfsFile::GetContainingDirectory() const { | ||
| 35 | return file->GetContainingDirectory(); | ||
| 36 | } | ||
| 37 | |||
| 38 | bool OffsetVfsFile::IsWritable() const { | ||
| 39 | return file->IsWritable(); | ||
| 40 | } | ||
| 41 | |||
| 42 | bool OffsetVfsFile::IsReadable() const { | ||
| 43 | return file->IsReadable(); | ||
| 44 | } | ||
| 45 | |||
| 46 | size_t OffsetVfsFile::Read(u8* data, size_t length, size_t r_offset) const { | ||
| 47 | return file->Read(data, TrimToFit(length, r_offset), offset + r_offset); | ||
| 48 | } | ||
| 49 | |||
| 50 | size_t OffsetVfsFile::Write(const u8* data, size_t length, size_t r_offset) { | ||
| 51 | return file->Write(data, TrimToFit(length, r_offset), offset + r_offset); | ||
| 52 | } | ||
| 53 | |||
| 54 | boost::optional<u8> OffsetVfsFile::ReadByte(size_t r_offset) const { | ||
| 55 | if (r_offset < size) | ||
| 56 | return file->ReadByte(offset + r_offset); | ||
| 57 | |||
| 58 | return boost::none; | ||
| 59 | } | ||
| 60 | |||
| 61 | std::vector<u8> OffsetVfsFile::ReadBytes(size_t r_size, size_t r_offset) const { | ||
| 62 | return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset); | ||
| 63 | } | ||
| 64 | |||
| 65 | std::vector<u8> OffsetVfsFile::ReadAllBytes() const { | ||
| 66 | return file->ReadBytes(size, offset); | ||
| 67 | } | ||
| 68 | |||
| 69 | bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) { | ||
| 70 | if (r_offset < size) | ||
| 71 | return file->WriteByte(data, offset + r_offset); | ||
| 72 | |||
| 73 | return false; | ||
| 74 | } | ||
| 75 | |||
| 76 | size_t OffsetVfsFile::WriteBytes(std::vector<u8> data, size_t r_offset) { | ||
| 77 | return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset); | ||
| 78 | } | ||
| 79 | |||
| 80 | bool OffsetVfsFile::Rename(const std::string& name) { | ||
| 81 | return file->Rename(name); | ||
| 82 | } | ||
| 83 | |||
| 84 | size_t OffsetVfsFile::GetOffset() const { | ||
| 85 | return offset; | ||
| 86 | } | ||
| 87 | |||
| 88 | size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const { | ||
| 89 | return std::max<size_t>(std::min<size_t>(size - r_offset, r_size), 0); | ||
| 90 | } | ||
| 91 | |||
| 92 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h new file mode 100644 index 000000000..adc615b38 --- /dev/null +++ b/src/core/file_sys/vfs_offset.h | |||
| @@ -0,0 +1,46 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "core/file_sys/vfs.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | // An implementation of VfsFile that wraps around another VfsFile at a certain offset. | ||
| 12 | // Similar to seeking to an offset. | ||
| 13 | // If the file is writable, operations that would write past the end of the offset file will expand | ||
| 14 | // the size of this wrapper. | ||
| 15 | struct OffsetVfsFile : public VfsFile { | ||
| 16 | OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0, | ||
| 17 | const std::string& new_name = ""); | ||
| 18 | |||
| 19 | std::string GetName() const override; | ||
| 20 | size_t GetSize() const override; | ||
| 21 | bool Resize(size_t new_size) override; | ||
| 22 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | ||
| 23 | bool IsWritable() const override; | ||
| 24 | bool IsReadable() const override; | ||
| 25 | size_t Read(u8* data, size_t length, size_t offset) const override; | ||
| 26 | size_t Write(const u8* data, size_t length, size_t offset) override; | ||
| 27 | boost::optional<u8> ReadByte(size_t offset) const override; | ||
| 28 | std::vector<u8> ReadBytes(size_t size, size_t offset) const override; | ||
| 29 | std::vector<u8> ReadAllBytes() const override; | ||
| 30 | bool WriteByte(u8 data, size_t offset) override; | ||
| 31 | size_t WriteBytes(std::vector<u8> data, size_t offset) override; | ||
| 32 | |||
| 33 | bool Rename(const std::string& name) override; | ||
| 34 | |||
| 35 | size_t GetOffset() const; | ||
| 36 | |||
| 37 | private: | ||
| 38 | size_t TrimToFit(size_t r_size, size_t r_offset) const; | ||
| 39 | |||
| 40 | std::shared_ptr<VfsFile> file; | ||
| 41 | size_t offset; | ||
| 42 | size_t size; | ||
| 43 | std::string name; | ||
| 44 | }; | ||
| 45 | |||
| 46 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp new file mode 100644 index 000000000..8b95e8c72 --- /dev/null +++ b/src/core/file_sys/vfs_real.cpp | |||
| @@ -0,0 +1,168 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include "common/common_paths.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "core/file_sys/vfs_real.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | static std::string PermissionsToCharArray(Mode perms) { | ||
| 12 | std::string out; | ||
| 13 | switch (perms) { | ||
| 14 | case Mode::Read: | ||
| 15 | out += "r"; | ||
| 16 | break; | ||
| 17 | case Mode::Write: | ||
| 18 | out += "r+"; | ||
| 19 | break; | ||
| 20 | case Mode::Append: | ||
| 21 | out += "a"; | ||
| 22 | break; | ||
| 23 | } | ||
| 24 | return out + "b"; | ||
| 25 | } | ||
| 26 | |||
| 27 | RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_) | ||
| 28 | : backing(path_, PermissionsToCharArray(perms_).c_str()), path(path_), | ||
| 29 | parent_path(FileUtil::GetParentPath(path_)), | ||
| 30 | path_components(FileUtil::SplitPathComponents(path_)), | ||
| 31 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | ||
| 32 | perms(perms_) {} | ||
| 33 | |||
| 34 | std::string RealVfsFile::GetName() const { | ||
| 35 | return path_components.back(); | ||
| 36 | } | ||
| 37 | |||
| 38 | size_t RealVfsFile::GetSize() const { | ||
| 39 | return backing.GetSize(); | ||
| 40 | } | ||
| 41 | |||
| 42 | bool RealVfsFile::Resize(size_t new_size) { | ||
| 43 | return backing.Resize(new_size); | ||
| 44 | } | ||
| 45 | |||
| 46 | std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const { | ||
| 47 | return std::make_shared<RealVfsDirectory>(parent_path, perms); | ||
| 48 | } | ||
| 49 | |||
| 50 | bool RealVfsFile::IsWritable() const { | ||
| 51 | return perms == Mode::Append || perms == Mode::Write; | ||
| 52 | } | ||
| 53 | |||
| 54 | bool RealVfsFile::IsReadable() const { | ||
| 55 | return perms == Mode::Read || perms == Mode::Write; | ||
| 56 | } | ||
| 57 | |||
| 58 | size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { | ||
| 59 | if (!backing.Seek(offset, SEEK_SET)) | ||
| 60 | return 0; | ||
| 61 | return backing.ReadBytes(data, length); | ||
| 62 | } | ||
| 63 | |||
| 64 | size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { | ||
| 65 | if (!backing.Seek(offset, SEEK_SET)) | ||
| 66 | return 0; | ||
| 67 | return backing.WriteBytes(data, length); | ||
| 68 | } | ||
| 69 | |||
| 70 | bool RealVfsFile::Rename(const std::string& name) { | ||
| 71 | const auto out = FileUtil::Rename(GetName(), name); | ||
| 72 | path = parent_path + DIR_SEP + name; | ||
| 73 | path_components = parent_components; | ||
| 74 | path_components.push_back(name); | ||
| 75 | backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str()); | ||
| 76 | return out; | ||
| 77 | } | ||
| 78 | |||
| 79 | RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_) | ||
| 80 | : path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)), | ||
| 81 | path_components(FileUtil::SplitPathComponents(path)), | ||
| 82 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | ||
| 83 | perms(perms_) { | ||
| 84 | if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append)) | ||
| 85 | FileUtil::CreateDir(path); | ||
| 86 | unsigned size; | ||
| 87 | if (perms != Mode::Append) { | ||
| 88 | FileUtil::ForeachDirectoryEntry( | ||
| 89 | &size, path, | ||
| 90 | [this](unsigned* entries_out, const std::string& directory, | ||
| 91 | const std::string& filename) { | ||
| 92 | std::string full_path = directory + DIR_SEP + filename; | ||
| 93 | if (FileUtil::IsDirectory(full_path)) | ||
| 94 | subdirectories.emplace_back( | ||
| 95 | std::make_shared<RealVfsDirectory>(full_path, perms)); | ||
| 96 | else | ||
| 97 | files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms)); | ||
| 98 | return true; | ||
| 99 | }); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const { | ||
| 104 | return std::vector<std::shared_ptr<VfsFile>>(files); | ||
| 105 | } | ||
| 106 | |||
| 107 | std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const { | ||
| 108 | return std::vector<std::shared_ptr<VfsDirectory>>(subdirectories); | ||
| 109 | } | ||
| 110 | |||
| 111 | bool RealVfsDirectory::IsWritable() const { | ||
| 112 | return perms == Mode::Write || perms == Mode::Append; | ||
| 113 | } | ||
| 114 | |||
| 115 | bool RealVfsDirectory::IsReadable() const { | ||
| 116 | return perms == Mode::Read || perms == Mode::Write; | ||
| 117 | } | ||
| 118 | |||
| 119 | std::string RealVfsDirectory::GetName() const { | ||
| 120 | return path_components.back(); | ||
| 121 | } | ||
| 122 | |||
| 123 | std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { | ||
| 124 | if (path_components.size() <= 1) | ||
| 125 | return nullptr; | ||
| 126 | |||
| 127 | return std::make_shared<RealVfsDirectory>(parent_path, perms); | ||
| 128 | } | ||
| 129 | |||
| 130 | std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(const std::string& name) { | ||
| 131 | if (!FileUtil::CreateDir(path + DIR_SEP + name)) | ||
| 132 | return nullptr; | ||
| 133 | subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path + DIR_SEP + name, perms)); | ||
| 134 | return subdirectories.back(); | ||
| 135 | } | ||
| 136 | |||
| 137 | std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(const std::string& name) { | ||
| 138 | if (!FileUtil::CreateEmptyFile(path + DIR_SEP + name)) | ||
| 139 | return nullptr; | ||
| 140 | files.emplace_back(std::make_shared<RealVfsFile>(path + DIR_SEP + name, perms)); | ||
| 141 | return files.back(); | ||
| 142 | } | ||
| 143 | |||
| 144 | bool RealVfsDirectory::DeleteSubdirectory(const std::string& name) { | ||
| 145 | return FileUtil::DeleteDirRecursively(path + DIR_SEP + name); | ||
| 146 | } | ||
| 147 | |||
| 148 | bool RealVfsDirectory::DeleteFile(const std::string& name) { | ||
| 149 | return FileUtil::Delete(path + DIR_SEP + name); | ||
| 150 | } | ||
| 151 | |||
| 152 | bool RealVfsDirectory::Rename(const std::string& name) { | ||
| 153 | return FileUtil::Rename(path, parent_path + DIR_SEP + name); | ||
| 154 | } | ||
| 155 | |||
| 156 | bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 157 | auto iter = std::find(files.begin(), files.end(), file); | ||
| 158 | if (iter == files.end()) | ||
| 159 | return false; | ||
| 160 | |||
| 161 | files[iter - files.begin()] = files.back(); | ||
| 162 | files.pop_back(); | ||
| 163 | |||
| 164 | subdirectories.emplace_back(dir); | ||
| 165 | |||
| 166 | return true; | ||
| 167 | } | ||
| 168 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h new file mode 100644 index 000000000..01717f485 --- /dev/null +++ b/src/core/file_sys/vfs_real.h | |||
| @@ -0,0 +1,65 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "common/file_util.h" | ||
| 8 | #include "core/file_sys/filesystem.h" | ||
| 9 | #include "core/file_sys/vfs.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | // An implmentation of VfsFile that represents a file on the user's computer. | ||
| 14 | struct RealVfsFile : public VfsFile { | ||
| 15 | RealVfsFile(const std::string& name, Mode perms = Mode::Read); | ||
| 16 | |||
| 17 | std::string GetName() const override; | ||
| 18 | size_t GetSize() const override; | ||
| 19 | bool Resize(size_t new_size) override; | ||
| 20 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | ||
| 21 | bool IsWritable() const override; | ||
| 22 | bool IsReadable() const override; | ||
| 23 | size_t Read(u8* data, size_t length, size_t offset) const override; | ||
| 24 | size_t Write(const u8* data, size_t length, size_t offset) override; | ||
| 25 | bool Rename(const std::string& name) override; | ||
| 26 | |||
| 27 | private: | ||
| 28 | FileUtil::IOFile backing; | ||
| 29 | std::string path; | ||
| 30 | std::string parent_path; | ||
| 31 | std::vector<std::string> path_components; | ||
| 32 | std::vector<std::string> parent_components; | ||
| 33 | Mode perms; | ||
| 34 | }; | ||
| 35 | |||
| 36 | // An implementation of VfsDirectory that represents a directory on the user's computer. | ||
| 37 | struct RealVfsDirectory : public VfsDirectory { | ||
| 38 | RealVfsDirectory(const std::string& path, Mode perms); | ||
| 39 | |||
| 40 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | ||
| 41 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | ||
| 42 | bool IsWritable() const override; | ||
| 43 | bool IsReadable() const override; | ||
| 44 | std::string GetName() const override; | ||
| 45 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | ||
| 46 | std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override; | ||
| 47 | std::shared_ptr<VfsFile> CreateFile(const std::string& name) override; | ||
| 48 | bool DeleteSubdirectory(const std::string& name) override; | ||
| 49 | bool DeleteFile(const std::string& name) override; | ||
| 50 | bool Rename(const std::string& name) override; | ||
| 51 | |||
| 52 | protected: | ||
| 53 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 54 | |||
| 55 | private: | ||
| 56 | std::string path; | ||
| 57 | std::string parent_path; | ||
| 58 | std::vector<std::string> path_components; | ||
| 59 | std::vector<std::string> parent_components; | ||
| 60 | Mode perms; | ||
| 61 | std::vector<std::shared_ptr<VfsFile>> files; | ||
| 62 | std::vector<std::shared_ptr<VfsDirectory>> subdirectories; | ||
| 63 | }; | ||
| 64 | |||
| 65 | } // namespace FileSys | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index a871b3eaa..7bb13c735 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -621,7 +621,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { | |||
| 621 | IPC::ResponseBuilder rb{ctx, 4}; | 621 | IPC::ResponseBuilder rb{ctx, 4}; |
| 622 | 622 | ||
| 623 | FileSys::Path unused; | 623 | FileSys::Path unused; |
| 624 | auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused); | 624 | auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData); |
| 625 | if (savedata.Failed()) { | 625 | if (savedata.Failed()) { |
| 626 | // Create the save data and return an error indicating that the operation was performed. | 626 | // Create the save data and return an error indicating that the operation was performed. |
| 627 | FileSystem::FormatFileSystem(FileSystem::Type::SaveData); | 627 | FileSystem::FormatFileSystem(FileSystem::Type::SaveData); |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index f58b518b6..25810c165 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -2,36 +2,221 @@ | |||
| 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 <boost/container/flat_map.hpp> | 5 | #include "boost/container/flat_map.hpp" |
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/common_paths.h" | ||
| 6 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | #include "core/core.h" | ||
| 10 | #include "core/file_sys/errors.h" | ||
| 7 | #include "core/file_sys/filesystem.h" | 11 | #include "core/file_sys/filesystem.h" |
| 8 | #include "core/file_sys/savedata_factory.h" | 12 | #include "core/file_sys/vfs.h" |
| 9 | #include "core/file_sys/sdmc_factory.h" | 13 | #include "core/file_sys/vfs_offset.h" |
| 14 | #include "core/file_sys/vfs_real.h" | ||
| 15 | #include "core/hle/kernel/process.h" | ||
| 10 | #include "core/hle/service/filesystem/filesystem.h" | 16 | #include "core/hle/service/filesystem/filesystem.h" |
| 11 | #include "core/hle/service/filesystem/fsp_srv.h" | 17 | #include "core/hle/service/filesystem/fsp_srv.h" |
| 12 | 18 | ||
| 13 | namespace Service::FileSystem { | 19 | namespace Service::FileSystem { |
| 14 | 20 | ||
| 21 | // Size of emulated sd card free space, reported in bytes. | ||
| 22 | // Just using 32GB because thats reasonable | ||
| 23 | // TODO(DarkLordZach): Eventually make this configurable in settings. | ||
| 24 | constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000; | ||
| 25 | |||
| 26 | static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, | ||
| 27 | const std::string& dir_name) { | ||
| 28 | if (dir_name == "." || dir_name == "" || dir_name == "/" || dir_name == "\\") | ||
| 29 | return base; | ||
| 30 | |||
| 31 | return base->GetDirectoryRelative(dir_name); | ||
| 32 | } | ||
| 33 | |||
| 34 | VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_) | ||
| 35 | : backing(backing_) {} | ||
| 36 | |||
| 37 | std::string VfsDirectoryServiceWrapper::GetName() const { | ||
| 38 | return backing->GetName(); | ||
| 39 | } | ||
| 40 | |||
| 41 | ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const { | ||
| 42 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 43 | auto file = dir->CreateFile(FileUtil::GetFilename(path)); | ||
| 44 | if (file == nullptr) | ||
| 45 | return ResultCode(-1); | ||
| 46 | if (!file->Resize(size)) | ||
| 47 | return ResultCode(-1); | ||
| 48 | return RESULT_SUCCESS; | ||
| 49 | } | ||
| 50 | |||
| 51 | ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const { | ||
| 52 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 53 | if (!backing->DeleteFile(FileUtil::GetFilename(path))) | ||
| 54 | return ResultCode(-1); | ||
| 55 | return RESULT_SUCCESS; | ||
| 56 | } | ||
| 57 | |||
| 58 | ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const { | ||
| 59 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 60 | if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty()) | ||
| 61 | dir = backing; | ||
| 62 | auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path)); | ||
| 63 | if (new_dir == nullptr) | ||
| 64 | return ResultCode(-1); | ||
| 65 | return RESULT_SUCCESS; | ||
| 66 | } | ||
| 67 | |||
| 68 | ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const { | ||
| 69 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 70 | if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) | ||
| 71 | return ResultCode(-1); | ||
| 72 | return RESULT_SUCCESS; | ||
| 73 | } | ||
| 74 | |||
| 75 | ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const { | ||
| 76 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 77 | if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) | ||
| 78 | return ResultCode(-1); | ||
| 79 | return RESULT_SUCCESS; | ||
| 80 | } | ||
| 81 | |||
| 82 | ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path, | ||
| 83 | const std::string& dest_path) const { | ||
| 84 | auto src = backing->GetFileRelative(src_path); | ||
| 85 | if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { | ||
| 86 | // Use more-optimized vfs implementation rename. | ||
| 87 | if (src == nullptr) | ||
| 88 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 89 | if (!src->Rename(FileUtil::GetFilename(dest_path))) | ||
| 90 | return ResultCode(-1); | ||
| 91 | return RESULT_SUCCESS; | ||
| 92 | } | ||
| 93 | |||
| 94 | // Move by hand -- TODO(DarkLordZach): Optimize | ||
| 95 | auto c_res = CreateFile(dest_path, src->GetSize()); | ||
| 96 | if (c_res != RESULT_SUCCESS) | ||
| 97 | return c_res; | ||
| 98 | |||
| 99 | auto dest = backing->GetFileRelative(dest_path); | ||
| 100 | ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found."); | ||
| 101 | |||
| 102 | ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), | ||
| 103 | "Could not write all of the bytes but everything else has succeded."); | ||
| 104 | |||
| 105 | if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) | ||
| 106 | return ResultCode(-1); | ||
| 107 | |||
| 108 | return RESULT_SUCCESS; | ||
| 109 | } | ||
| 110 | |||
| 111 | ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path, | ||
| 112 | const std::string& dest_path) const { | ||
| 113 | auto src = GetDirectoryRelativeWrapped(backing, src_path); | ||
| 114 | if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { | ||
| 115 | // Use more-optimized vfs implementation rename. | ||
| 116 | if (src == nullptr) | ||
| 117 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 118 | if (!src->Rename(FileUtil::GetFilename(dest_path))) | ||
| 119 | return ResultCode(-1); | ||
| 120 | return RESULT_SUCCESS; | ||
| 121 | } | ||
| 122 | |||
| 123 | // TODO(DarkLordZach): Implement renaming across the tree (move). | ||
| 124 | ASSERT_MSG(false, | ||
| 125 | "Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs " | ||
| 126 | "don't match -- UNIMPLEMENTED", | ||
| 127 | src_path, dest_path); | ||
| 128 | |||
| 129 | return ResultCode(-1); | ||
| 130 | } | ||
| 131 | |||
| 132 | ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path, | ||
| 133 | FileSys::Mode mode) const { | ||
| 134 | auto file = backing->GetFileRelative(path); | ||
| 135 | if (file == nullptr) | ||
| 136 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 137 | |||
| 138 | if ((static_cast<u32>(mode) & static_cast<u32>(FileSys::Mode::Append)) != 0) { | ||
| 139 | return MakeResult<FileSys::VirtualFile>( | ||
| 140 | std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize())); | ||
| 141 | } | ||
| 142 | |||
| 143 | return MakeResult<FileSys::VirtualFile>(file); | ||
| 144 | } | ||
| 145 | |||
| 146 | ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) { | ||
| 147 | auto dir = GetDirectoryRelativeWrapped(backing, path); | ||
| 148 | if (dir == nullptr) | ||
| 149 | return ResultCode(-1); | ||
| 150 | return MakeResult(dir); | ||
| 151 | } | ||
| 152 | |||
| 153 | u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const { | ||
| 154 | if (backing->IsWritable()) | ||
| 155 | return EMULATED_SD_REPORTED_SIZE; | ||
| 156 | |||
| 157 | return 0; | ||
| 158 | } | ||
| 159 | |||
| 160 | ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( | ||
| 161 | const std::string& path) const { | ||
| 162 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 163 | if (dir == nullptr) | ||
| 164 | return ResultCode(-1); | ||
| 165 | auto filename = FileUtil::GetFilename(path); | ||
| 166 | if (dir->GetFile(filename) != nullptr) | ||
| 167 | return MakeResult(FileSys::EntryType::File); | ||
| 168 | if (dir->GetSubdirectory(filename) != nullptr) | ||
| 169 | return MakeResult(FileSys::EntryType::Directory); | ||
| 170 | return ResultCode(-1); | ||
| 171 | } | ||
| 172 | |||
| 173 | // A deferred filesystem for nand save data. | ||
| 174 | // This must be deferred because the directory is dependent on title id, which is not set at | ||
| 175 | // registration time. | ||
| 176 | struct SaveDataDeferredFilesystem : DeferredFilesystem { | ||
| 177 | protected: | ||
| 178 | FileSys::VirtualDir CreateFilesystem() override { | ||
| 179 | u64 title_id = Core::CurrentProcess()->program_id; | ||
| 180 | // TODO(DarkLordZach): Users | ||
| 181 | u32 user_id = 0; | ||
| 182 | std::string nand_directory = fmt::format( | ||
| 183 | "{}save/{:016X}/{:08X}/", FileUtil::GetUserPath(D_NAND_IDX), title_id, user_id); | ||
| 184 | |||
| 185 | auto savedata = | ||
| 186 | std::make_shared<FileSys::RealVfsDirectory>(nand_directory, FileSys::Mode::Write); | ||
| 187 | return savedata; | ||
| 188 | } | ||
| 189 | }; | ||
| 190 | |||
| 15 | /** | 191 | /** |
| 16 | * Map of registered file systems, identified by type. Once an file system is registered here, it | 192 | * Map of registered file systems, identified by type. Once an file system is registered here, it |
| 17 | * is never removed until UnregisterFileSystems is called. | 193 | * is never removed until UnregisterFileSystems is called. |
| 18 | */ | 194 | */ |
| 19 | static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map; | 195 | static boost::container::flat_map<Type, std::unique_ptr<DeferredFilesystem>> filesystem_map; |
| 196 | static FileSys::VirtualFile filesystem_romfs = nullptr; | ||
| 20 | 197 | ||
| 21 | ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) { | 198 | ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& factory, Type type) { |
| 22 | auto result = filesystem_map.emplace(type, std::move(factory)); | 199 | auto result = filesystem_map.emplace(type, std::move(factory)); |
| 23 | 200 | ||
| 24 | bool inserted = result.second; | 201 | bool inserted = result.second; |
| 25 | ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); | 202 | ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); |
| 26 | 203 | ||
| 27 | auto& filesystem = result.first->second; | 204 | auto& filesystem = result.first->second; |
| 205 | LOG_DEBUG(Service_FS, "Registered file system with id code 0x{:08X}", static_cast<u32>(type)); | ||
| 206 | return RESULT_SUCCESS; | ||
| 207 | } | ||
| 208 | |||
| 209 | ResultCode RegisterRomFS(FileSys::VirtualFile filesystem) { | ||
| 210 | ASSERT_MSG(filesystem_romfs == nullptr, | ||
| 211 | "Tried to register more than one system with same id code"); | ||
| 212 | |||
| 213 | filesystem_romfs = filesystem; | ||
| 28 | LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(), | 214 | LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(), |
| 29 | static_cast<u32>(type)); | 215 | static_cast<u32>(Type::RomFS)); |
| 30 | return RESULT_SUCCESS; | 216 | return RESULT_SUCCESS; |
| 31 | } | 217 | } |
| 32 | 218 | ||
| 33 | ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, | 219 | ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type) { |
| 34 | FileSys::Path& path) { | ||
| 35 | LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type)); | 220 | LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type)); |
| 36 | 221 | ||
| 37 | auto itr = filesystem_map.find(type); | 222 | auto itr = filesystem_map.find(type); |
| @@ -40,7 +225,13 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, | |||
| 40 | return ResultCode(-1); | 225 | return ResultCode(-1); |
| 41 | } | 226 | } |
| 42 | 227 | ||
| 43 | return itr->second->Open(path); | 228 | return MakeResult(itr->second->Get()); |
| 229 | } | ||
| 230 | |||
| 231 | ResultVal<FileSys::VirtualFile> OpenRomFS() { | ||
| 232 | if (filesystem_romfs == nullptr) | ||
| 233 | return ResultCode(-1); | ||
| 234 | return MakeResult(filesystem_romfs); | ||
| 44 | } | 235 | } |
| 45 | 236 | ||
| 46 | ResultCode FormatFileSystem(Type type) { | 237 | ResultCode FormatFileSystem(Type type) { |
| @@ -52,21 +243,21 @@ ResultCode FormatFileSystem(Type type) { | |||
| 52 | return ResultCode(-1); | 243 | return ResultCode(-1); |
| 53 | } | 244 | } |
| 54 | 245 | ||
| 55 | FileSys::Path unused; | 246 | return itr->second->Get()->GetParentDirectory()->DeleteSubdirectory( |
| 56 | return itr->second->Format(unused); | 247 | itr->second->Get()->GetName()) |
| 248 | ? RESULT_SUCCESS | ||
| 249 | : ResultCode(-1); | ||
| 57 | } | 250 | } |
| 58 | 251 | ||
| 59 | void RegisterFileSystems() { | 252 | void RegisterFileSystems() { |
| 60 | filesystem_map.clear(); | 253 | filesystem_map.clear(); |
| 254 | filesystem_romfs = nullptr; | ||
| 61 | 255 | ||
| 62 | std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); | ||
| 63 | std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX); | 256 | std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX); |
| 257 | auto sdcard = std::make_shared<FileSys::RealVfsDirectory>(sd_directory, FileSys::Mode::Write); | ||
| 258 | RegisterFileSystem(std::make_unique<DeferredFilesystem>(sdcard), Type::SDMC); | ||
| 64 | 259 | ||
| 65 | auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory)); | 260 | RegisterFileSystem(std::make_unique<SaveDataDeferredFilesystem>(), Type::SaveData); |
| 66 | RegisterFileSystem(std::move(savedata), Type::SaveData); | ||
| 67 | |||
| 68 | auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory)); | ||
| 69 | RegisterFileSystem(std::move(sdcard), Type::SDMC); | ||
| 70 | } | 261 | } |
| 71 | 262 | ||
| 72 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 263 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 56d26146e..d3de797f1 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h | |||
| @@ -6,6 +6,8 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/file_sys/filesystem.h" | ||
| 10 | #include "core/file_sys/vfs.h" | ||
| 9 | #include "core/hle/result.h" | 11 | #include "core/hle/result.h" |
| 10 | 12 | ||
| 11 | namespace FileSys { | 13 | namespace FileSys { |
| @@ -29,12 +31,136 @@ enum class Type { | |||
| 29 | SDMC = 3, | 31 | SDMC = 3, |
| 30 | }; | 32 | }; |
| 31 | 33 | ||
| 34 | // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of | ||
| 35 | // pointers and booleans. This makes using a VfsDirectory with switch services much easier and | ||
| 36 | // avoids repetitive code. | ||
| 37 | class VfsDirectoryServiceWrapper { | ||
| 38 | public: | ||
| 39 | explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing); | ||
| 40 | |||
| 41 | /** | ||
| 42 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||
| 43 | */ | ||
| 44 | std::string GetName() const; | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Create a file specified by its path | ||
| 48 | * @param path Path relative to the Archive | ||
| 49 | * @param size The size of the new file, filled with zeroes | ||
| 50 | * @return Result of the operation | ||
| 51 | */ | ||
| 52 | ResultCode CreateFile(const std::string& path, u64 size) const; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Delete a file specified by its path | ||
| 56 | * @param path Path relative to the archive | ||
| 57 | * @return Result of the operation | ||
| 58 | */ | ||
| 59 | ResultCode DeleteFile(const std::string& path) const; | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Create a directory specified by its path | ||
| 63 | * @param path Path relative to the archive | ||
| 64 | * @return Result of the operation | ||
| 65 | */ | ||
| 66 | ResultCode CreateDirectory(const std::string& path) const; | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Delete a directory specified by its path | ||
| 70 | * @param path Path relative to the archive | ||
| 71 | * @return Result of the operation | ||
| 72 | */ | ||
| 73 | ResultCode DeleteDirectory(const std::string& path) const; | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Delete a directory specified by its path and anything under it | ||
| 77 | * @param path Path relative to the archive | ||
| 78 | * @return Result of the operation | ||
| 79 | */ | ||
| 80 | ResultCode DeleteDirectoryRecursively(const std::string& path) const; | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Rename a File specified by its path | ||
| 84 | * @param src_path Source path relative to the archive | ||
| 85 | * @param dest_path Destination path relative to the archive | ||
| 86 | * @return Result of the operation | ||
| 87 | */ | ||
| 88 | ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const; | ||
| 89 | |||
| 90 | /** | ||
| 91 | * Rename a Directory specified by its path | ||
| 92 | * @param src_path Source path relative to the archive | ||
| 93 | * @param dest_path Destination path relative to the archive | ||
| 94 | * @return Result of the operation | ||
| 95 | */ | ||
| 96 | ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const; | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Open a file specified by its path, using the specified mode | ||
| 100 | * @param path Path relative to the archive | ||
| 101 | * @param mode Mode to open the file with | ||
| 102 | * @return Opened file, or error code | ||
| 103 | */ | ||
| 104 | ResultVal<FileSys::VirtualFile> OpenFile(const std::string& path, FileSys::Mode mode) const; | ||
| 105 | |||
| 106 | /** | ||
| 107 | * Open a directory specified by its path | ||
| 108 | * @param path Path relative to the archive | ||
| 109 | * @return Opened directory, or error code | ||
| 110 | */ | ||
| 111 | ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path); | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Get the free space | ||
| 115 | * @return The number of free bytes in the archive | ||
| 116 | */ | ||
| 117 | u64 GetFreeSpaceSize() const; | ||
| 118 | |||
| 119 | /** | ||
| 120 | * Get the type of the specified path | ||
| 121 | * @return The type of the specified path or error code | ||
| 122 | */ | ||
| 123 | ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const; | ||
| 124 | |||
| 125 | private: | ||
| 126 | FileSys::VirtualDir backing; | ||
| 127 | }; | ||
| 128 | |||
| 129 | // A class that deferres the creation of a filesystem until a later time. | ||
| 130 | // This is useful if construction depends on a variable not known when the filesystem is registered. | ||
| 131 | // Construct this with a filesystem (VirtualDir) to avoid the deferrence feature or override the | ||
| 132 | // CreateFilesystem method which will be called on first use. | ||
| 133 | struct DeferredFilesystem { | ||
| 134 | DeferredFilesystem() = default; | ||
| 135 | |||
| 136 | explicit DeferredFilesystem(FileSys::VirtualDir vfs_directory) : fs(std::move(vfs_directory)) {} | ||
| 137 | |||
| 138 | FileSys::VirtualDir Get() { | ||
| 139 | if (fs == nullptr) | ||
| 140 | fs = CreateFilesystem(); | ||
| 141 | |||
| 142 | return fs; | ||
| 143 | } | ||
| 144 | |||
| 145 | virtual ~DeferredFilesystem() = default; | ||
| 146 | |||
| 147 | protected: | ||
| 148 | virtual FileSys::VirtualDir CreateFilesystem() { | ||
| 149 | return fs; | ||
| 150 | } | ||
| 151 | |||
| 152 | private: | ||
| 153 | FileSys::VirtualDir fs; | ||
| 154 | }; | ||
| 155 | |||
| 32 | /** | 156 | /** |
| 33 | * Registers a FileSystem, instances of which can later be opened using its IdCode. | 157 | * Registers a FileSystem, instances of which can later be opened using its IdCode. |
| 34 | * @param factory FileSystem backend interface to use | 158 | * @param factory FileSystem backend interface to use |
| 35 | * @param type Type used to access this type of FileSystem | 159 | * @param type Type used to access this type of FileSystem |
| 36 | */ | 160 | */ |
| 37 | ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type); | 161 | ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& fs, Type type); |
| 162 | |||
| 163 | ResultCode RegisterRomFS(FileSys::VirtualFile fs); | ||
| 38 | 164 | ||
| 39 | /** | 165 | /** |
| 40 | * Opens a file system | 166 | * Opens a file system |
| @@ -42,8 +168,9 @@ ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& fact | |||
| 42 | * @param path Path to the file system, used with Binary paths | 168 | * @param path Path to the file system, used with Binary paths |
| 43 | * @return FileSys::FileSystemBackend interface to the file system | 169 | * @return FileSys::FileSystemBackend interface to the file system |
| 44 | */ | 170 | */ |
| 45 | ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, | 171 | ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type); |
| 46 | FileSys::Path& path); | 172 | |
| 173 | ResultVal<FileSys::VirtualFile> OpenRomFS(); | ||
| 47 | 174 | ||
| 48 | /** | 175 | /** |
| 49 | * Formats a file system | 176 | * Formats a file system |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 216bfea0a..26e8a8c88 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -19,8 +19,8 @@ namespace Service::FileSystem { | |||
| 19 | 19 | ||
| 20 | class IStorage final : public ServiceFramework<IStorage> { | 20 | class IStorage final : public ServiceFramework<IStorage> { |
| 21 | public: | 21 | public: |
| 22 | IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend) | 22 | IStorage(FileSys::VirtualFile backend_) |
| 23 | : ServiceFramework("IStorage"), backend(std::move(backend)) { | 23 | : ServiceFramework("IStorage"), backend(std::move(backend_)) { |
| 24 | static const FunctionInfo functions[] = { | 24 | static const FunctionInfo functions[] = { |
| 25 | {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, | 25 | {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, |
| 26 | {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"}, | 26 | {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"}, |
| @@ -29,7 +29,7 @@ public: | |||
| 29 | } | 29 | } |
| 30 | 30 | ||
| 31 | private: | 31 | private: |
| 32 | std::unique_ptr<FileSys::StorageBackend> backend; | 32 | FileSys::VirtualFile backend; |
| 33 | 33 | ||
| 34 | void Read(Kernel::HLERequestContext& ctx) { | 34 | void Read(Kernel::HLERequestContext& ctx) { |
| 35 | IPC::RequestParser rp{ctx}; | 35 | IPC::RequestParser rp{ctx}; |
| @@ -51,8 +51,8 @@ private: | |||
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | // Read the data from the Storage backend | 53 | // Read the data from the Storage backend |
| 54 | std::vector<u8> output(length); | 54 | std::vector<u8> output = backend->ReadBytes(length, offset); |
| 55 | ResultVal<size_t> res = backend->Read(offset, length, output.data()); | 55 | auto res = MakeResult<size_t>(output.size()); |
| 56 | if (res.Failed()) { | 56 | if (res.Failed()) { |
| 57 | IPC::ResponseBuilder rb{ctx, 2}; | 57 | IPC::ResponseBuilder rb{ctx, 2}; |
| 58 | rb.Push(res.Code()); | 58 | rb.Push(res.Code()); |
| @@ -69,8 +69,8 @@ private: | |||
| 69 | 69 | ||
| 70 | class IFile final : public ServiceFramework<IFile> { | 70 | class IFile final : public ServiceFramework<IFile> { |
| 71 | public: | 71 | public: |
| 72 | explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend) | 72 | explicit IFile(FileSys::VirtualFile backend_) |
| 73 | : ServiceFramework("IFile"), backend(std::move(backend)) { | 73 | : ServiceFramework("IFile"), backend(std::move(backend_)) { |
| 74 | static const FunctionInfo functions[] = { | 74 | static const FunctionInfo functions[] = { |
| 75 | {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, | 75 | {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, |
| 76 | {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"}, | 76 | {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"}, |
| @@ -80,7 +80,7 @@ public: | |||
| 80 | } | 80 | } |
| 81 | 81 | ||
| 82 | private: | 82 | private: |
| 83 | std::unique_ptr<FileSys::StorageBackend> backend; | 83 | FileSys::VirtualFile backend; |
| 84 | 84 | ||
| 85 | void Read(Kernel::HLERequestContext& ctx) { | 85 | void Read(Kernel::HLERequestContext& ctx) { |
| 86 | IPC::RequestParser rp{ctx}; | 86 | IPC::RequestParser rp{ctx}; |
| @@ -103,8 +103,8 @@ private: | |||
| 103 | } | 103 | } |
| 104 | 104 | ||
| 105 | // Read the data from the Storage backend | 105 | // Read the data from the Storage backend |
| 106 | std::vector<u8> output(length); | 106 | std::vector<u8> output = backend->ReadBytes(length, offset); |
| 107 | ResultVal<size_t> res = backend->Read(offset, length, output.data()); | 107 | auto res = MakeResult<size_t>(output.size()); |
| 108 | if (res.Failed()) { | 108 | if (res.Failed()) { |
| 109 | IPC::ResponseBuilder rb{ctx, 2}; | 109 | IPC::ResponseBuilder rb{ctx, 2}; |
| 110 | rb.Push(res.Code()); | 110 | rb.Push(res.Code()); |
| @@ -139,9 +139,10 @@ private: | |||
| 139 | return; | 139 | return; |
| 140 | } | 140 | } |
| 141 | 141 | ||
| 142 | // Write the data to the Storage backend | ||
| 143 | std::vector<u8> data = ctx.ReadBuffer(); | 142 | std::vector<u8> data = ctx.ReadBuffer(); |
| 144 | ResultVal<size_t> res = backend->Write(offset, length, true, data.data()); | 143 | data.resize(length); |
| 144 | // Write the data to the Storage backend | ||
| 145 | auto res = MakeResult<size_t>(backend->WriteBytes(data, offset)); | ||
| 145 | if (res.Failed()) { | 146 | if (res.Failed()) { |
| 146 | IPC::ResponseBuilder rb{ctx, 2}; | 147 | IPC::ResponseBuilder rb{ctx, 2}; |
| 147 | rb.Push(res.Code()); | 148 | rb.Push(res.Code()); |
| @@ -154,7 +155,8 @@ private: | |||
| 154 | 155 | ||
| 155 | void Flush(Kernel::HLERequestContext& ctx) { | 156 | void Flush(Kernel::HLERequestContext& ctx) { |
| 156 | LOG_DEBUG(Service_FS, "called"); | 157 | LOG_DEBUG(Service_FS, "called"); |
| 157 | backend->Flush(); | 158 | |
| 159 | // Exists for SDK compatibiltity -- No need to flush file. | ||
| 158 | 160 | ||
| 159 | IPC::ResponseBuilder rb{ctx, 2}; | 161 | IPC::ResponseBuilder rb{ctx, 2}; |
| 160 | rb.Push(RESULT_SUCCESS); | 162 | rb.Push(RESULT_SUCCESS); |
| @@ -163,7 +165,7 @@ private: | |||
| 163 | void SetSize(Kernel::HLERequestContext& ctx) { | 165 | void SetSize(Kernel::HLERequestContext& ctx) { |
| 164 | IPC::RequestParser rp{ctx}; | 166 | IPC::RequestParser rp{ctx}; |
| 165 | const u64 size = rp.Pop<u64>(); | 167 | const u64 size = rp.Pop<u64>(); |
| 166 | backend->SetSize(size); | 168 | backend->Resize(size); |
| 167 | LOG_DEBUG(Service_FS, "called, size={}", size); | 169 | LOG_DEBUG(Service_FS, "called, size={}", size); |
| 168 | 170 | ||
| 169 | IPC::ResponseBuilder rb{ctx, 2}; | 171 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -180,19 +182,38 @@ private: | |||
| 180 | } | 182 | } |
| 181 | }; | 183 | }; |
| 182 | 184 | ||
| 185 | template <typename T> | ||
| 186 | static void BuildEntryIndex(std::vector<FileSys::Entry>& entries, const std::vector<T>& new_data, | ||
| 187 | FileSys::EntryType type) { | ||
| 188 | for (const auto& new_entry : new_data) { | ||
| 189 | FileSys::Entry entry; | ||
| 190 | entry.filename[0] = '\0'; | ||
| 191 | std::strncat(entry.filename, new_entry->GetName().c_str(), FileSys::FILENAME_LENGTH - 1); | ||
| 192 | entry.type = type; | ||
| 193 | entry.file_size = new_entry->GetSize(); | ||
| 194 | entries.emplace_back(std::move(entry)); | ||
| 195 | } | ||
| 196 | } | ||
| 197 | |||
| 183 | class IDirectory final : public ServiceFramework<IDirectory> { | 198 | class IDirectory final : public ServiceFramework<IDirectory> { |
| 184 | public: | 199 | public: |
| 185 | explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend) | 200 | explicit IDirectory(FileSys::VirtualDir backend_) |
| 186 | : ServiceFramework("IDirectory"), backend(std::move(backend)) { | 201 | : ServiceFramework("IDirectory"), backend(std::move(backend_)) { |
| 187 | static const FunctionInfo functions[] = { | 202 | static const FunctionInfo functions[] = { |
| 188 | {0, &IDirectory::Read, "Read"}, | 203 | {0, &IDirectory::Read, "Read"}, |
| 189 | {1, &IDirectory::GetEntryCount, "GetEntryCount"}, | 204 | {1, &IDirectory::GetEntryCount, "GetEntryCount"}, |
| 190 | }; | 205 | }; |
| 191 | RegisterHandlers(functions); | 206 | RegisterHandlers(functions); |
| 207 | |||
| 208 | // Build entry index now to save time later. | ||
| 209 | BuildEntryIndex(entries, backend->GetFiles(), FileSys::File); | ||
| 210 | BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::Directory); | ||
| 192 | } | 211 | } |
| 193 | 212 | ||
| 194 | private: | 213 | private: |
| 195 | std::unique_ptr<FileSys::DirectoryBackend> backend; | 214 | FileSys::VirtualDir backend; |
| 215 | std::vector<FileSys::Entry> entries; | ||
| 216 | u64 next_entry_index = 0; | ||
| 196 | 217 | ||
| 197 | void Read(Kernel::HLERequestContext& ctx) { | 218 | void Read(Kernel::HLERequestContext& ctx) { |
| 198 | IPC::RequestParser rp{ctx}; | 219 | IPC::RequestParser rp{ctx}; |
| @@ -203,26 +224,31 @@ private: | |||
| 203 | // Calculate how many entries we can fit in the output buffer | 224 | // Calculate how many entries we can fit in the output buffer |
| 204 | u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); | 225 | u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); |
| 205 | 226 | ||
| 227 | // Cap at total number of entries. | ||
| 228 | u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index); | ||
| 229 | |||
| 206 | // Read the data from the Directory backend | 230 | // Read the data from the Directory backend |
| 207 | std::vector<FileSys::Entry> entries(count_entries); | 231 | std::vector<FileSys::Entry> entry_data(entries.begin() + next_entry_index, |
| 208 | u64 read_entries = backend->Read(count_entries, entries.data()); | 232 | entries.begin() + next_entry_index + actual_entries); |
| 233 | |||
| 234 | next_entry_index += actual_entries; | ||
| 209 | 235 | ||
| 210 | // Convert the data into a byte array | 236 | // Convert the data into a byte array |
| 211 | std::vector<u8> output(entries.size() * sizeof(FileSys::Entry)); | 237 | std::vector<u8> output(entry_data.size() * sizeof(FileSys::Entry)); |
| 212 | std::memcpy(output.data(), entries.data(), output.size()); | 238 | std::memcpy(output.data(), entry_data.data(), output.size()); |
| 213 | 239 | ||
| 214 | // Write the data to memory | 240 | // Write the data to memory |
| 215 | ctx.WriteBuffer(output); | 241 | ctx.WriteBuffer(output); |
| 216 | 242 | ||
| 217 | IPC::ResponseBuilder rb{ctx, 4}; | 243 | IPC::ResponseBuilder rb{ctx, 4}; |
| 218 | rb.Push(RESULT_SUCCESS); | 244 | rb.Push(RESULT_SUCCESS); |
| 219 | rb.Push(read_entries); | 245 | rb.Push(actual_entries); |
| 220 | } | 246 | } |
| 221 | 247 | ||
| 222 | void GetEntryCount(Kernel::HLERequestContext& ctx) { | 248 | void GetEntryCount(Kernel::HLERequestContext& ctx) { |
| 223 | LOG_DEBUG(Service_FS, "called"); | 249 | LOG_DEBUG(Service_FS, "called"); |
| 224 | 250 | ||
| 225 | u64 count = backend->GetEntryCount(); | 251 | u64 count = entries.size() - next_entry_index; |
| 226 | 252 | ||
| 227 | IPC::ResponseBuilder rb{ctx, 4}; | 253 | IPC::ResponseBuilder rb{ctx, 4}; |
| 228 | rb.Push(RESULT_SUCCESS); | 254 | rb.Push(RESULT_SUCCESS); |
| @@ -232,7 +258,7 @@ private: | |||
| 232 | 258 | ||
| 233 | class IFileSystem final : public ServiceFramework<IFileSystem> { | 259 | class IFileSystem final : public ServiceFramework<IFileSystem> { |
| 234 | public: | 260 | public: |
| 235 | explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend) | 261 | explicit IFileSystem(FileSys::VirtualDir backend) |
| 236 | : ServiceFramework("IFileSystem"), backend(std::move(backend)) { | 262 | : ServiceFramework("IFileSystem"), backend(std::move(backend)) { |
| 237 | static const FunctionInfo functions[] = { | 263 | static const FunctionInfo functions[] = { |
| 238 | {0, &IFileSystem::CreateFile, "CreateFile"}, | 264 | {0, &IFileSystem::CreateFile, "CreateFile"}, |
| @@ -267,7 +293,7 @@ public: | |||
| 267 | LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); | 293 | LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); |
| 268 | 294 | ||
| 269 | IPC::ResponseBuilder rb{ctx, 2}; | 295 | IPC::ResponseBuilder rb{ctx, 2}; |
| 270 | rb.Push(backend->CreateFile(name, size)); | 296 | rb.Push(backend.CreateFile(name, size)); |
| 271 | } | 297 | } |
| 272 | 298 | ||
| 273 | void DeleteFile(Kernel::HLERequestContext& ctx) { | 299 | void DeleteFile(Kernel::HLERequestContext& ctx) { |
| @@ -279,7 +305,7 @@ public: | |||
| 279 | LOG_DEBUG(Service_FS, "called file {}", name); | 305 | LOG_DEBUG(Service_FS, "called file {}", name); |
| 280 | 306 | ||
| 281 | IPC::ResponseBuilder rb{ctx, 2}; | 307 | IPC::ResponseBuilder rb{ctx, 2}; |
| 282 | rb.Push(backend->DeleteFile(name)); | 308 | rb.Push(backend.DeleteFile(name)); |
| 283 | } | 309 | } |
| 284 | 310 | ||
| 285 | void CreateDirectory(Kernel::HLERequestContext& ctx) { | 311 | void CreateDirectory(Kernel::HLERequestContext& ctx) { |
| @@ -291,7 +317,7 @@ public: | |||
| 291 | LOG_DEBUG(Service_FS, "called directory {}", name); | 317 | LOG_DEBUG(Service_FS, "called directory {}", name); |
| 292 | 318 | ||
| 293 | IPC::ResponseBuilder rb{ctx, 2}; | 319 | IPC::ResponseBuilder rb{ctx, 2}; |
| 294 | rb.Push(backend->CreateDirectory(name)); | 320 | rb.Push(backend.CreateDirectory(name)); |
| 295 | } | 321 | } |
| 296 | 322 | ||
| 297 | void RenameFile(Kernel::HLERequestContext& ctx) { | 323 | void RenameFile(Kernel::HLERequestContext& ctx) { |
| @@ -309,7 +335,7 @@ public: | |||
| 309 | LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); | 335 | LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); |
| 310 | 336 | ||
| 311 | IPC::ResponseBuilder rb{ctx, 2}; | 337 | IPC::ResponseBuilder rb{ctx, 2}; |
| 312 | rb.Push(backend->RenameFile(src_name, dst_name)); | 338 | rb.Push(backend.RenameFile(src_name, dst_name)); |
| 313 | } | 339 | } |
| 314 | 340 | ||
| 315 | void OpenFile(Kernel::HLERequestContext& ctx) { | 341 | void OpenFile(Kernel::HLERequestContext& ctx) { |
| @@ -322,14 +348,14 @@ public: | |||
| 322 | 348 | ||
| 323 | LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); | 349 | LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); |
| 324 | 350 | ||
| 325 | auto result = backend->OpenFile(name, mode); | 351 | auto result = backend.OpenFile(name, mode); |
| 326 | if (result.Failed()) { | 352 | if (result.Failed()) { |
| 327 | IPC::ResponseBuilder rb{ctx, 2}; | 353 | IPC::ResponseBuilder rb{ctx, 2}; |
| 328 | rb.Push(result.Code()); | 354 | rb.Push(result.Code()); |
| 329 | return; | 355 | return; |
| 330 | } | 356 | } |
| 331 | 357 | ||
| 332 | auto file = std::move(result.Unwrap()); | 358 | IFile file(result.Unwrap()); |
| 333 | 359 | ||
| 334 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 360 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 335 | rb.Push(RESULT_SUCCESS); | 361 | rb.Push(RESULT_SUCCESS); |
| @@ -347,14 +373,14 @@ public: | |||
| 347 | 373 | ||
| 348 | LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); | 374 | LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); |
| 349 | 375 | ||
| 350 | auto result = backend->OpenDirectory(name); | 376 | auto result = backend.OpenDirectory(name); |
| 351 | if (result.Failed()) { | 377 | if (result.Failed()) { |
| 352 | IPC::ResponseBuilder rb{ctx, 2}; | 378 | IPC::ResponseBuilder rb{ctx, 2}; |
| 353 | rb.Push(result.Code()); | 379 | rb.Push(result.Code()); |
| 354 | return; | 380 | return; |
| 355 | } | 381 | } |
| 356 | 382 | ||
| 357 | auto directory = std::move(result.Unwrap()); | 383 | IDirectory directory(result.Unwrap()); |
| 358 | 384 | ||
| 359 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 385 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 360 | rb.Push(RESULT_SUCCESS); | 386 | rb.Push(RESULT_SUCCESS); |
| @@ -369,7 +395,7 @@ public: | |||
| 369 | 395 | ||
| 370 | LOG_DEBUG(Service_FS, "called file {}", name); | 396 | LOG_DEBUG(Service_FS, "called file {}", name); |
| 371 | 397 | ||
| 372 | auto result = backend->GetEntryType(name); | 398 | auto result = backend.GetEntryType(name); |
| 373 | if (result.Failed()) { | 399 | if (result.Failed()) { |
| 374 | IPC::ResponseBuilder rb{ctx, 2}; | 400 | IPC::ResponseBuilder rb{ctx, 2}; |
| 375 | rb.Push(result.Code()); | 401 | rb.Push(result.Code()); |
| @@ -389,7 +415,7 @@ public: | |||
| 389 | } | 415 | } |
| 390 | 416 | ||
| 391 | private: | 417 | private: |
| 392 | std::unique_ptr<FileSys::FileSystemBackend> backend; | 418 | VfsDirectoryServiceWrapper backend; |
| 393 | }; | 419 | }; |
| 394 | 420 | ||
| 395 | FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | 421 | FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { |
| @@ -490,8 +516,7 @@ void FSP_SRV::TryLoadRomFS() { | |||
| 490 | if (romfs) { | 516 | if (romfs) { |
| 491 | return; | 517 | return; |
| 492 | } | 518 | } |
| 493 | FileSys::Path unused; | 519 | auto res = OpenRomFS(); |
| 494 | auto res = OpenFileSystem(Type::RomFS, unused); | ||
| 495 | if (res.Succeeded()) { | 520 | if (res.Succeeded()) { |
| 496 | romfs = std::move(res.Unwrap()); | 521 | romfs = std::move(res.Unwrap()); |
| 497 | } | 522 | } |
| @@ -507,8 +532,7 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) { | |||
| 507 | void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { | 532 | void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { |
| 508 | LOG_DEBUG(Service_FS, "called"); | 533 | LOG_DEBUG(Service_FS, "called"); |
| 509 | 534 | ||
| 510 | FileSys::Path unused; | 535 | IFileSystem filesystem(OpenFileSystem(Type::SDMC).Unwrap()); |
| 511 | auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap(); | ||
| 512 | 536 | ||
| 513 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 537 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 514 | rb.Push(RESULT_SUCCESS); | 538 | rb.Push(RESULT_SUCCESS); |
| @@ -531,8 +555,7 @@ void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) { | |||
| 531 | void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { | 555 | void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { |
| 532 | LOG_WARNING(Service_FS, "(STUBBED) called"); | 556 | LOG_WARNING(Service_FS, "(STUBBED) called"); |
| 533 | 557 | ||
| 534 | FileSys::Path unused; | 558 | IFileSystem filesystem(OpenFileSystem(Type::SaveData).Unwrap()); |
| 535 | auto filesystem = OpenFileSystem(Type::SaveData, unused).Unwrap(); | ||
| 536 | 559 | ||
| 537 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 560 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 538 | rb.Push(RESULT_SUCCESS); | 561 | rb.Push(RESULT_SUCCESS); |
| @@ -559,18 +582,11 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { | |||
| 559 | return; | 582 | return; |
| 560 | } | 583 | } |
| 561 | 584 | ||
| 562 | // Attempt to open a StorageBackend interface to the RomFS | 585 | IStorage storage(romfs); |
| 563 | auto storage = romfs->OpenFile({}, {}); | ||
| 564 | if (storage.Failed()) { | ||
| 565 | LOG_CRITICAL(Service_FS, "no storage interface available!"); | ||
| 566 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 567 | rb.Push(storage.Code()); | ||
| 568 | return; | ||
| 569 | } | ||
| 570 | 586 | ||
| 571 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 587 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 572 | rb.Push(RESULT_SUCCESS); | 588 | rb.Push(RESULT_SUCCESS); |
| 573 | rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap())); | 589 | rb.PushIpcInterface<IStorage>(std::move(storage)); |
| 574 | } | 590 | } |
| 575 | 591 | ||
| 576 | void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { | 592 | void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index acb78fac1..b1ef6397f 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h | |||
| @@ -29,7 +29,7 @@ private: | |||
| 29 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); | 29 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); |
| 30 | void OpenRomStorage(Kernel::HLERequestContext& ctx); | 30 | void OpenRomStorage(Kernel::HLERequestContext& ctx); |
| 31 | 31 | ||
| 32 | std::unique_ptr<FileSys::FileSystemBackend> romfs; | 32 | FileSys::VirtualFile romfs; |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | } // namespace Service::FileSystem | 35 | } // namespace Service::FileSystem |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index eb7feb617..0b3b4cd73 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -4,11 +4,10 @@ | |||
| 4 | 4 | ||
| 5 | #include <cinttypes> | 5 | #include <cinttypes> |
| 6 | #include "common/common_funcs.h" | 6 | #include "common/common_funcs.h" |
| 7 | #include "common/common_paths.h" | ||
| 8 | #include "common/file_util.h" | 7 | #include "common/file_util.h" |
| 9 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 10 | #include "common/string_util.h" | 9 | #include "common/string_util.h" |
| 11 | #include "core/file_sys/romfs_factory.h" | 10 | #include "core/file_sys/content_archive.h" |
| 12 | #include "core/hle/kernel/process.h" | 11 | #include "core/hle/kernel/process.h" |
| 13 | #include "core/hle/kernel/resource_limit.h" | 12 | #include "core/hle/kernel/resource_limit.h" |
| 14 | #include "core/hle/service/filesystem/filesystem.h" | 13 | #include "core/hle/service/filesystem/filesystem.h" |
| @@ -46,55 +45,11 @@ static std::string FindRomFS(const std::string& directory) { | |||
| 46 | return filepath_romfs; | 45 | return filepath_romfs; |
| 47 | } | 46 | } |
| 48 | 47 | ||
| 49 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, | 48 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file) |
| 50 | std::string filepath) | 49 | : AppLoader(std::move(file)) {} |
| 51 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 52 | |||
| 53 | FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& file, | ||
| 54 | const std::string& filepath) { | ||
| 55 | bool is_main_found{}; | ||
| 56 | bool is_npdm_found{}; | ||
| 57 | bool is_rtld_found{}; | ||
| 58 | bool is_sdk_found{}; | ||
| 59 | |||
| 60 | const auto callback = [&](unsigned* num_entries_out, const std::string& directory, | ||
| 61 | const std::string& virtual_name) -> bool { | ||
| 62 | // Skip directories | ||
| 63 | std::string physical_name = directory + virtual_name; | ||
| 64 | if (FileUtil::IsDirectory(physical_name)) { | ||
| 65 | return true; | ||
| 66 | } | ||
| 67 | |||
| 68 | // Verify filename | ||
| 69 | if (Common::ToLower(virtual_name) == "main") { | ||
| 70 | is_main_found = true; | ||
| 71 | } else if (Common::ToLower(virtual_name) == "main.npdm") { | ||
| 72 | is_npdm_found = true; | ||
| 73 | return true; | ||
| 74 | } else if (Common::ToLower(virtual_name) == "rtld") { | ||
| 75 | is_rtld_found = true; | ||
| 76 | } else if (Common::ToLower(virtual_name) == "sdk") { | ||
| 77 | is_sdk_found = true; | ||
| 78 | } else { | ||
| 79 | // Continue searching | ||
| 80 | return true; | ||
| 81 | } | ||
| 82 | |||
| 83 | // Verify file is an NSO | ||
| 84 | FileUtil::IOFile file(physical_name, "rb"); | ||
| 85 | if (AppLoader_NSO::IdentifyType(file, physical_name) != FileType::NSO) { | ||
| 86 | return false; | ||
| 87 | } | ||
| 88 | |||
| 89 | // We are done if we've found and verified all required NSOs | ||
| 90 | return !(is_main_found && is_npdm_found && is_rtld_found && is_sdk_found); | ||
| 91 | }; | ||
| 92 | |||
| 93 | // Search the directory recursively, looking for the required modules | ||
| 94 | const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; | ||
| 95 | FileUtil::ForeachDirectoryEntry(nullptr, directory, callback); | ||
| 96 | 50 | ||
| 97 | if (is_main_found && is_npdm_found && is_rtld_found && is_sdk_found) { | 51 | FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) { |
| 52 | if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) { | ||
| 98 | return FileType::DeconstructedRomDirectory; | 53 | return FileType::DeconstructedRomDirectory; |
| 99 | } | 54 | } |
| 100 | 55 | ||
| @@ -106,14 +61,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 106 | if (is_loaded) { | 61 | if (is_loaded) { |
| 107 | return ResultStatus::ErrorAlreadyLoaded; | 62 | return ResultStatus::ErrorAlreadyLoaded; |
| 108 | } | 63 | } |
| 109 | if (!file.IsOpen()) { | ||
| 110 | return ResultStatus::Error; | ||
| 111 | } | ||
| 112 | 64 | ||
| 113 | const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; | 65 | const FileSys::VirtualDir dir = file->GetContainingDirectory(); |
| 114 | const std::string npdm_path = directory + DIR_SEP + "main.npdm"; | 66 | const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); |
| 67 | if (npdm == nullptr) | ||
| 68 | return ResultStatus::ErrorInvalidFormat; | ||
| 115 | 69 | ||
| 116 | ResultStatus result = metadata.Load(npdm_path); | 70 | ResultStatus result = metadata.Load(npdm); |
| 117 | if (result != ResultStatus::Success) { | 71 | if (result != ResultStatus::Success) { |
| 118 | return result; | 72 | return result; |
| 119 | } | 73 | } |
| @@ -128,9 +82,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 128 | VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; | 82 | VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; |
| 129 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", | 83 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", |
| 130 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { | 84 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { |
| 131 | const std::string path = directory + DIR_SEP + module; | ||
| 132 | const VAddr load_addr = next_load_addr; | 85 | const VAddr load_addr = next_load_addr; |
| 133 | next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); | 86 | const FileSys::VirtualFile module_file = dir->GetFile(module); |
| 87 | if (module_file != nullptr) | ||
| 88 | next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr); | ||
| 134 | if (next_load_addr) { | 89 | if (next_load_addr) { |
| 135 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); | 90 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); |
| 136 | } else { | 91 | } else { |
| @@ -147,41 +102,19 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 147 | metadata.GetMainThreadStackSize()); | 102 | metadata.GetMainThreadStackSize()); |
| 148 | 103 | ||
| 149 | // Find the RomFS by searching for a ".romfs" file in this directory | 104 | // Find the RomFS by searching for a ".romfs" file in this directory |
| 150 | filepath_romfs = FindRomFS(directory); | 105 | const auto& files = dir->GetFiles(); |
| 151 | 106 | const auto romfs_iter = | |
| 152 | // Register the RomFS if a ".romfs" file was found | 107 | std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { |
| 153 | if (!filepath_romfs.empty()) { | 108 | return file->GetName().find(".romfs") != std::string::npos; |
| 154 | Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this), | 109 | }); |
| 155 | Service::FileSystem::Type::RomFS); | ||
| 156 | } | ||
| 157 | |||
| 158 | is_loaded = true; | ||
| 159 | return ResultStatus::Success; | ||
| 160 | } | ||
| 161 | 110 | ||
| 162 | ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS( | 111 | // TODO(DarkLordZach): Identify RomFS if its a subdirectory. |
| 163 | std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { | 112 | const auto romfs = (romfs_iter == files.end()) ? nullptr : *romfs_iter; |
| 164 | 113 | ||
| 165 | if (filepath_romfs.empty()) { | 114 | if (romfs != nullptr) |
| 166 | LOG_DEBUG(Loader, "No RomFS available"); | 115 | Service::FileSystem::RegisterRomFS(romfs); |
| 167 | return ResultStatus::ErrorNotUsed; | ||
| 168 | } | ||
| 169 | |||
| 170 | // We reopen the file, to allow its position to be independent | ||
| 171 | romfs_file = std::make_shared<FileUtil::IOFile>(filepath_romfs, "rb"); | ||
| 172 | if (!romfs_file->IsOpen()) { | ||
| 173 | return ResultStatus::Error; | ||
| 174 | } | ||
| 175 | |||
| 176 | offset = 0; | ||
| 177 | size = romfs_file->GetSize(); | ||
| 178 | |||
| 179 | LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset); | ||
| 180 | LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size); | ||
| 181 | |||
| 182 | // Reset read pointer | ||
| 183 | file.Seek(0, SEEK_SET); | ||
| 184 | 116 | ||
| 117 | is_loaded = true; | ||
| 185 | return ResultStatus::Success; | 118 | return ResultStatus::Success; |
| 186 | } | 119 | } |
| 187 | 120 | ||
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 23295d911..494220756 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h | |||
| @@ -20,28 +20,22 @@ namespace Loader { | |||
| 20 | */ | 20 | */ |
| 21 | class AppLoader_DeconstructedRomDirectory final : public AppLoader { | 21 | class AppLoader_DeconstructedRomDirectory final : public AppLoader { |
| 22 | public: | 22 | public: |
| 23 | AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, std::string filepath); | 23 | explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file); |
| 24 | 24 | ||
| 25 | /** | 25 | /** |
| 26 | * Returns the type of the file | 26 | * Returns the type of the file |
| 27 | * @param file FileUtil::IOFile open file | 27 | * @param file std::shared_ptr<VfsFile> open file |
| 28 | * @param filepath Path of the file that we are opening. | ||
| 29 | * @return FileType found, or FileType::Error if this loader doesn't know it | 28 | * @return FileType found, or FileType::Error if this loader doesn't know it |
| 30 | */ | 29 | */ |
| 31 | static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); | 30 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 32 | 31 | ||
| 33 | FileType GetFileType() override { | 32 | FileType GetFileType() override { |
| 34 | return IdentifyType(file, filepath); | 33 | return IdentifyType(file); |
| 35 | } | 34 | } |
| 36 | 35 | ||
| 37 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 36 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 38 | 37 | ||
| 39 | ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | ||
| 40 | u64& size) override; | ||
| 41 | |||
| 42 | private: | 38 | private: |
| 43 | std::string filepath_romfs; | ||
| 44 | std::string filepath; | ||
| 45 | FileSys::ProgramMetadata metadata; | 39 | FileSys::ProgramMetadata metadata; |
| 46 | }; | 40 | }; |
| 47 | 41 | ||
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index b69e5c6ef..4bfd5f536 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -365,20 +365,17 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const | |||
| 365 | 365 | ||
| 366 | namespace Loader { | 366 | namespace Loader { |
| 367 | 367 | ||
| 368 | AppLoader_ELF::AppLoader_ELF(FileUtil::IOFile&& file, std::string filename) | 368 | AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} |
| 369 | : AppLoader(std::move(file)), filename(std::move(filename)) {} | ||
| 370 | 369 | ||
| 371 | FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file, const std::string&) { | 370 | FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) { |
| 372 | static constexpr u16 ELF_MACHINE_ARM{0x28}; | 371 | static constexpr u16 ELF_MACHINE_ARM{0x28}; |
| 373 | 372 | ||
| 374 | u32 magic = 0; | 373 | u32 magic = 0; |
| 375 | file.Seek(0, SEEK_SET); | 374 | if (4 != file->ReadObject(&magic)) |
| 376 | if (1 != file.ReadArray<u32>(&magic, 1)) | ||
| 377 | return FileType::Error; | 375 | return FileType::Error; |
| 378 | 376 | ||
| 379 | u16 machine = 0; | 377 | u16 machine = 0; |
| 380 | file.Seek(18, SEEK_SET); | 378 | if (2 != file->ReadObject(&machine, 18)) |
| 381 | if (1 != file.ReadArray<u16>(&machine, 1)) | ||
| 382 | return FileType::Error; | 379 | return FileType::Error; |
| 383 | 380 | ||
| 384 | if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) | 381 | if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) |
| @@ -391,20 +388,13 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 391 | if (is_loaded) | 388 | if (is_loaded) |
| 392 | return ResultStatus::ErrorAlreadyLoaded; | 389 | return ResultStatus::ErrorAlreadyLoaded; |
| 393 | 390 | ||
| 394 | if (!file.IsOpen()) | 391 | std::vector<u8> buffer = file->ReadAllBytes(); |
| 395 | return ResultStatus::Error; | 392 | if (buffer.size() != file->GetSize()) |
| 396 | |||
| 397 | // Reset read pointer in case this file has been read before. | ||
| 398 | file.Seek(0, SEEK_SET); | ||
| 399 | |||
| 400 | size_t size = file.GetSize(); | ||
| 401 | std::unique_ptr<u8[]> buffer(new u8[size]); | ||
| 402 | if (file.ReadBytes(&buffer[0], size) != size) | ||
| 403 | return ResultStatus::Error; | 393 | return ResultStatus::Error; |
| 404 | 394 | ||
| 405 | ElfReader elf_reader(&buffer[0]); | 395 | ElfReader elf_reader(&buffer[0]); |
| 406 | SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); | 396 | SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); |
| 407 | codeset->name = filename; | 397 | codeset->name = file->GetName(); |
| 408 | 398 | ||
| 409 | process->LoadModule(codeset, codeset->entrypoint); | 399 | process->LoadModule(codeset, codeset->entrypoint); |
| 410 | process->svc_access_mask.set(); | 400 | process->svc_access_mask.set(); |
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index ee741a789..b8fb982d0 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h | |||
| @@ -16,24 +16,20 @@ namespace Loader { | |||
| 16 | /// Loads an ELF/AXF file | 16 | /// Loads an ELF/AXF file |
| 17 | class AppLoader_ELF final : public AppLoader { | 17 | class AppLoader_ELF final : public AppLoader { |
| 18 | public: | 18 | public: |
| 19 | AppLoader_ELF(FileUtil::IOFile&& file, std::string filename); | 19 | explicit AppLoader_ELF(FileSys::VirtualFile file); |
| 20 | 20 | ||
| 21 | /** | 21 | /** |
| 22 | * Returns the type of the file | 22 | * Returns the type of the file |
| 23 | * @param file FileUtil::IOFile open file | 23 | * @param file std::shared_ptr<VfsFile> open file |
| 24 | * @param filepath Path of the file that we are opening. | ||
| 25 | * @return FileType found, or FileType::Error if this loader doesn't know it | 24 | * @return FileType found, or FileType::Error if this loader doesn't know it |
| 26 | */ | 25 | */ |
| 27 | static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); | 26 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 28 | 27 | ||
| 29 | FileType GetFileType() override { | 28 | FileType GetFileType() override { |
| 30 | return IdentifyType(file, filename); | 29 | return IdentifyType(file); |
| 31 | } | 30 | } |
| 32 | 31 | ||
| 33 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 32 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 34 | |||
| 35 | private: | ||
| 36 | std::string filename; | ||
| 37 | }; | 33 | }; |
| 38 | 34 | ||
| 39 | } // namespace Loader | 35 | } // namespace Loader |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 8831d8e83..1574345a1 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include <string> | 6 | #include <string> |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "common/string_util.h" | 8 | #include "common/string_util.h" |
| 9 | #include "core/file_sys/vfs_real.h" | ||
| 9 | #include "core/hle/kernel/process.h" | 10 | #include "core/hle/kernel/process.h" |
| 10 | #include "core/loader/deconstructed_rom_directory.h" | 11 | #include "core/loader/deconstructed_rom_directory.h" |
| 11 | #include "core/loader/elf.h" | 12 | #include "core/loader/elf.h" |
| @@ -21,11 +22,11 @@ const std::initializer_list<Kernel::AddressMapping> default_address_mappings = { | |||
| 21 | {0x1F000000, 0x600000, false}, // entire VRAM | 22 | {0x1F000000, 0x600000, false}, // entire VRAM |
| 22 | }; | 23 | }; |
| 23 | 24 | ||
| 24 | FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) { | 25 | FileType IdentifyFile(FileSys::VirtualFile file) { |
| 25 | FileType type; | 26 | FileType type; |
| 26 | 27 | ||
| 27 | #define CHECK_TYPE(loader) \ | 28 | #define CHECK_TYPE(loader) \ |
| 28 | type = AppLoader_##loader::IdentifyType(file, filepath); \ | 29 | type = AppLoader_##loader::IdentifyType(file); \ |
| 29 | if (FileType::Error != type) \ | 30 | if (FileType::Error != type) \ |
| 30 | return type; | 31 | return type; |
| 31 | 32 | ||
| @@ -41,25 +42,22 @@ FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) { | |||
| 41 | } | 42 | } |
| 42 | 43 | ||
| 43 | FileType IdentifyFile(const std::string& file_name) { | 44 | FileType IdentifyFile(const std::string& file_name) { |
| 44 | FileUtil::IOFile file(file_name, "rb"); | 45 | return IdentifyFile(FileSys::VirtualFile(std::make_shared<FileSys::RealVfsFile>(file_name))); |
| 45 | if (!file.IsOpen()) { | ||
| 46 | LOG_ERROR(Loader, "Failed to load file {}", file_name); | ||
| 47 | return FileType::Unknown; | ||
| 48 | } | ||
| 49 | |||
| 50 | return IdentifyFile(file, file_name); | ||
| 51 | } | 46 | } |
| 52 | 47 | ||
| 53 | FileType GuessFromExtension(const std::string& extension_) { | 48 | FileType GuessFromFilename(const std::string& name) { |
| 54 | std::string extension = Common::ToLower(extension_); | 49 | if (name == "main") |
| 50 | return FileType::DeconstructedRomDirectory; | ||
| 55 | 51 | ||
| 56 | if (extension == ".elf") | 52 | const std::string extension = Common::ToLower(FileUtil::GetExtensionFromFilename(name)); |
| 53 | |||
| 54 | if (extension == "elf") | ||
| 57 | return FileType::ELF; | 55 | return FileType::ELF; |
| 58 | else if (extension == ".nro") | 56 | if (extension == "nro") |
| 59 | return FileType::NRO; | 57 | return FileType::NRO; |
| 60 | else if (extension == ".nso") | 58 | if (extension == "nso") |
| 61 | return FileType::NSO; | 59 | return FileType::NSO; |
| 62 | else if (extension == ".nca") | 60 | if (extension == "nca") |
| 63 | return FileType::NCA; | 61 | return FileType::NCA; |
| 64 | 62 | ||
| 65 | return FileType::Unknown; | 63 | return FileType::Unknown; |
| @@ -93,58 +91,47 @@ const char* GetFileTypeString(FileType type) { | |||
| 93 | * @param filepath the file full path (with name) | 91 | * @param filepath the file full path (with name) |
| 94 | * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type | 92 | * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type |
| 95 | */ | 93 | */ |
| 96 | static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileType type, | 94 | static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileType type) { |
| 97 | const std::string& filename, | ||
| 98 | const std::string& filepath) { | ||
| 99 | switch (type) { | 95 | switch (type) { |
| 100 | 96 | ||
| 101 | // Standard ELF file format. | 97 | // Standard ELF file format. |
| 102 | case FileType::ELF: | 98 | case FileType::ELF: |
| 103 | return std::make_unique<AppLoader_ELF>(std::move(file), filename); | 99 | return std::make_unique<AppLoader_ELF>(std::move(file)); |
| 104 | 100 | ||
| 105 | // NX NSO file format. | 101 | // NX NSO file format. |
| 106 | case FileType::NSO: | 102 | case FileType::NSO: |
| 107 | return std::make_unique<AppLoader_NSO>(std::move(file), filepath); | 103 | return std::make_unique<AppLoader_NSO>(std::move(file)); |
| 108 | 104 | ||
| 109 | // NX NRO file format. | 105 | // NX NRO file format. |
| 110 | case FileType::NRO: | 106 | case FileType::NRO: |
| 111 | return std::make_unique<AppLoader_NRO>(std::move(file), filepath); | 107 | return std::make_unique<AppLoader_NRO>(std::move(file)); |
| 112 | 108 | ||
| 113 | // NX NCA file format. | 109 | // NX NCA file format. |
| 114 | case FileType::NCA: | 110 | case FileType::NCA: |
| 115 | return std::make_unique<AppLoader_NCA>(std::move(file), filepath); | 111 | return std::make_unique<AppLoader_NCA>(std::move(file)); |
| 116 | 112 | ||
| 117 | // NX deconstructed ROM directory. | 113 | // NX deconstructed ROM directory. |
| 118 | case FileType::DeconstructedRomDirectory: | 114 | case FileType::DeconstructedRomDirectory: |
| 119 | return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file), filepath); | 115 | return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); |
| 120 | 116 | ||
| 121 | default: | 117 | default: |
| 122 | return nullptr; | 118 | return nullptr; |
| 123 | } | 119 | } |
| 124 | } | 120 | } |
| 125 | 121 | ||
| 126 | std::unique_ptr<AppLoader> GetLoader(const std::string& filename) { | 122 | std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file) { |
| 127 | FileUtil::IOFile file(filename, "rb"); | 123 | FileType type = IdentifyFile(file); |
| 128 | if (!file.IsOpen()) { | 124 | FileType filename_type = GuessFromFilename(file->GetName()); |
| 129 | LOG_ERROR(Loader, "Failed to load file {}", filename); | ||
| 130 | return nullptr; | ||
| 131 | } | ||
| 132 | |||
| 133 | std::string filename_filename, filename_extension; | ||
| 134 | Common::SplitPath(filename, nullptr, &filename_filename, &filename_extension); | ||
| 135 | |||
| 136 | FileType type = IdentifyFile(file, filename); | ||
| 137 | FileType filename_type = GuessFromExtension(filename_extension); | ||
| 138 | 125 | ||
| 139 | if (type != filename_type) { | 126 | if (type != filename_type) { |
| 140 | LOG_WARNING(Loader, "File {} has a different type than its extension.", filename); | 127 | LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName()); |
| 141 | if (FileType::Unknown == type) | 128 | if (FileType::Unknown == type) |
| 142 | type = filename_type; | 129 | type = filename_type; |
| 143 | } | 130 | } |
| 144 | 131 | ||
| 145 | LOG_DEBUG(Loader, "Loading file {} as {}...", filename, GetFileTypeString(type)); | 132 | LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type)); |
| 146 | 133 | ||
| 147 | return GetFileLoader(std::move(file), type, filename_filename, filename); | 134 | return GetFileLoader(std::move(file), type); |
| 148 | } | 135 | } |
| 149 | 136 | ||
| 150 | } // namespace Loader | 137 | } // namespace Loader |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index b76f7b13d..1da9e8099 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -13,6 +13,7 @@ | |||
| 13 | #include <boost/optional.hpp> | 13 | #include <boost/optional.hpp> |
| 14 | #include "common/common_types.h" | 14 | #include "common/common_types.h" |
| 15 | #include "common/file_util.h" | 15 | #include "common/file_util.h" |
| 16 | #include "core/file_sys/vfs.h" | ||
| 16 | #include "core/hle/kernel/kernel.h" | 17 | #include "core/hle/kernel/kernel.h" |
| 17 | 18 | ||
| 18 | namespace Kernel { | 19 | namespace Kernel { |
| @@ -36,10 +37,9 @@ enum class FileType { | |||
| 36 | /** | 37 | /** |
| 37 | * Identifies the type of a bootable file based on the magic value in its header. | 38 | * Identifies the type of a bootable file based on the magic value in its header. |
| 38 | * @param file open file | 39 | * @param file open file |
| 39 | * @param filepath Path of the file that we are opening. | ||
| 40 | * @return FileType of file | 40 | * @return FileType of file |
| 41 | */ | 41 | */ |
| 42 | FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath); | 42 | FileType IdentifyFile(FileSys::VirtualFile file); |
| 43 | 43 | ||
| 44 | /** | 44 | /** |
| 45 | * Identifies the type of a bootable file based on the magic value in its header. | 45 | * Identifies the type of a bootable file based on the magic value in its header. |
| @@ -50,12 +50,12 @@ FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath); | |||
| 50 | FileType IdentifyFile(const std::string& file_name); | 50 | FileType IdentifyFile(const std::string& file_name); |
| 51 | 51 | ||
| 52 | /** | 52 | /** |
| 53 | * Guess the type of a bootable file from its extension | 53 | * Guess the type of a bootable file from its name |
| 54 | * @param extension String extension of bootable file | 54 | * @param name String name of bootable file |
| 55 | * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine | 55 | * @return FileType of file. Note: this will return FileType::Unknown if it is unable to determine |
| 56 | * a filetype, and will never return FileType::Error. | 56 | * a filetype, and will never return FileType::Error. |
| 57 | */ | 57 | */ |
| 58 | FileType GuessFromExtension(const std::string& extension); | 58 | FileType GuessFromFilename(const std::string& name); |
| 59 | 59 | ||
| 60 | /** | 60 | /** |
| 61 | * Convert a FileType into a string which can be displayed to the user. | 61 | * Convert a FileType into a string which can be displayed to the user. |
| @@ -79,7 +79,7 @@ enum class ResultStatus { | |||
| 79 | /// Interface for loading an application | 79 | /// Interface for loading an application |
| 80 | class AppLoader : NonCopyable { | 80 | class AppLoader : NonCopyable { |
| 81 | public: | 81 | public: |
| 82 | AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) {} | 82 | AppLoader(FileSys::VirtualFile file) : file(std::move(file)) {} |
| 83 | virtual ~AppLoader() {} | 83 | virtual ~AppLoader() {} |
| 84 | 84 | ||
| 85 | /** | 85 | /** |
| @@ -154,26 +154,20 @@ public: | |||
| 154 | /** | 154 | /** |
| 155 | * Get the RomFS of the application | 155 | * Get the RomFS of the application |
| 156 | * Since the RomFS can be huge, we return a file reference instead of copying to a buffer | 156 | * Since the RomFS can be huge, we return a file reference instead of copying to a buffer |
| 157 | * @param romfs_file The file containing the RomFS | 157 | * @param file The file containing the RomFS |
| 158 | * @param offset The offset the romfs begins on | ||
| 159 | * @param size The size of the romfs | ||
| 160 | * @return ResultStatus result of function | 158 | * @return ResultStatus result of function |
| 161 | */ | 159 | */ |
| 162 | virtual ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | 160 | virtual ResultStatus ReadRomFS(FileSys::VirtualFile& dir) { |
| 163 | u64& size) { | ||
| 164 | return ResultStatus::ErrorNotImplemented; | 161 | return ResultStatus::ErrorNotImplemented; |
| 165 | } | 162 | } |
| 166 | 163 | ||
| 167 | /** | 164 | /** |
| 168 | * Get the update RomFS of the application | 165 | * Get the update RomFS of the application |
| 169 | * Since the RomFS can be huge, we return a file reference instead of copying to a buffer | 166 | * Since the RomFS can be huge, we return a file reference instead of copying to a buffer |
| 170 | * @param romfs_file The file containing the RomFS | 167 | * @param file The file containing the RomFS |
| 171 | * @param offset The offset the romfs begins on | ||
| 172 | * @param size The size of the romfs | ||
| 173 | * @return ResultStatus result of function | 168 | * @return ResultStatus result of function |
| 174 | */ | 169 | */ |
| 175 | virtual ResultStatus ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | 170 | virtual ResultStatus ReadUpdateRomFS(FileSys::VirtualFile& file) { |
| 176 | u64& size) { | ||
| 177 | return ResultStatus::ErrorNotImplemented; | 171 | return ResultStatus::ErrorNotImplemented; |
| 178 | } | 172 | } |
| 179 | 173 | ||
| @@ -187,7 +181,7 @@ public: | |||
| 187 | } | 181 | } |
| 188 | 182 | ||
| 189 | protected: | 183 | protected: |
| 190 | FileUtil::IOFile file; | 184 | FileSys::VirtualFile file; |
| 191 | bool is_loaded = false; | 185 | bool is_loaded = false; |
| 192 | }; | 186 | }; |
| 193 | 187 | ||
| @@ -202,6 +196,6 @@ extern const std::initializer_list<Kernel::AddressMapping> default_address_mappi | |||
| 202 | * @param filename String filename of bootable file | 196 | * @param filename String filename of bootable file |
| 203 | * @return best loader for this file | 197 | * @return best loader for this file |
| 204 | */ | 198 | */ |
| 205 | std::unique_ptr<AppLoader> GetLoader(const std::string& filename); | 199 | std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file); |
| 206 | 200 | ||
| 207 | } // namespace Loader | 201 | } // namespace Loader |
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index da064f8e3..874f42b91 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp | |||
| @@ -4,13 +4,11 @@ | |||
| 4 | 4 | ||
| 5 | #include <vector> | 5 | #include <vector> |
| 6 | 6 | ||
| 7 | #include "common/common_funcs.h" | ||
| 8 | #include "common/file_util.h" | 7 | #include "common/file_util.h" |
| 9 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 10 | #include "common/swap.h" | ||
| 11 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/file_sys/content_archive.h" | ||
| 12 | #include "core/file_sys/program_metadata.h" | 11 | #include "core/file_sys/program_metadata.h" |
| 13 | #include "core/file_sys/romfs_factory.h" | ||
| 14 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 15 | #include "core/hle/kernel/resource_limit.h" | 13 | #include "core/hle/kernel/resource_limit.h" |
| 16 | #include "core/hle/service/filesystem/filesystem.h" | 14 | #include "core/hle/service/filesystem/filesystem.h" |
| @@ -20,208 +18,15 @@ | |||
| 20 | 18 | ||
| 21 | namespace Loader { | 19 | namespace Loader { |
| 22 | 20 | ||
| 23 | // Media offsets in headers are stored divided by 512. Mult. by this to get real offset. | 21 | AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(file) {} |
| 24 | constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200; | ||
| 25 | |||
| 26 | constexpr u64 SECTION_HEADER_SIZE = 0x200; | ||
| 27 | constexpr u64 SECTION_HEADER_OFFSET = 0x400; | ||
| 28 | |||
| 29 | enum class NcaContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 }; | ||
| 30 | |||
| 31 | enum class NcaSectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 }; | ||
| 32 | |||
| 33 | struct NcaSectionTableEntry { | ||
| 34 | u32_le media_offset; | ||
| 35 | u32_le media_end_offset; | ||
| 36 | INSERT_PADDING_BYTES(0x8); | ||
| 37 | }; | ||
| 38 | static_assert(sizeof(NcaSectionTableEntry) == 0x10, "NcaSectionTableEntry has incorrect size."); | ||
| 39 | |||
| 40 | struct NcaHeader { | ||
| 41 | std::array<u8, 0x100> rsa_signature_1; | ||
| 42 | std::array<u8, 0x100> rsa_signature_2; | ||
| 43 | u32_le magic; | ||
| 44 | u8 is_system; | ||
| 45 | NcaContentType content_type; | ||
| 46 | u8 crypto_type; | ||
| 47 | u8 key_index; | ||
| 48 | u64_le size; | ||
| 49 | u64_le title_id; | ||
| 50 | INSERT_PADDING_BYTES(0x4); | ||
| 51 | u32_le sdk_version; | ||
| 52 | u8 crypto_type_2; | ||
| 53 | INSERT_PADDING_BYTES(15); | ||
| 54 | std::array<u8, 0x10> rights_id; | ||
| 55 | std::array<NcaSectionTableEntry, 0x4> section_tables; | ||
| 56 | std::array<std::array<u8, 0x20>, 0x4> hash_tables; | ||
| 57 | std::array<std::array<u8, 0x10>, 0x4> key_area; | ||
| 58 | INSERT_PADDING_BYTES(0xC0); | ||
| 59 | }; | ||
| 60 | static_assert(sizeof(NcaHeader) == 0x400, "NcaHeader has incorrect size."); | ||
| 61 | |||
| 62 | struct NcaSectionHeaderBlock { | ||
| 63 | INSERT_PADDING_BYTES(3); | ||
| 64 | NcaSectionFilesystemType filesystem_type; | ||
| 65 | u8 crypto_type; | ||
| 66 | INSERT_PADDING_BYTES(3); | ||
| 67 | }; | ||
| 68 | static_assert(sizeof(NcaSectionHeaderBlock) == 0x8, "NcaSectionHeaderBlock has incorrect size."); | ||
| 69 | |||
| 70 | struct Pfs0Superblock { | ||
| 71 | NcaSectionHeaderBlock header_block; | ||
| 72 | std::array<u8, 0x20> hash; | ||
| 73 | u32_le size; | ||
| 74 | INSERT_PADDING_BYTES(4); | ||
| 75 | u64_le hash_table_offset; | ||
| 76 | u64_le hash_table_size; | ||
| 77 | u64_le pfs0_header_offset; | ||
| 78 | u64_le pfs0_size; | ||
| 79 | INSERT_PADDING_BYTES(432); | ||
| 80 | }; | ||
| 81 | static_assert(sizeof(Pfs0Superblock) == 0x200, "Pfs0Superblock has incorrect size."); | ||
| 82 | |||
| 83 | static bool IsValidNca(const NcaHeader& header) { | ||
| 84 | return header.magic == Common::MakeMagic('N', 'C', 'A', '2') || | ||
| 85 | header.magic == Common::MakeMagic('N', 'C', 'A', '3'); | ||
| 86 | } | ||
| 87 | |||
| 88 | // TODO(DarkLordZach): Add support for encrypted. | ||
| 89 | class Nca final { | ||
| 90 | std::vector<FileSys::PartitionFilesystem> pfs; | ||
| 91 | std::vector<u64> pfs_offset; | ||
| 92 | |||
| 93 | u64 romfs_offset = 0; | ||
| 94 | u64 romfs_size = 0; | ||
| 95 | |||
| 96 | boost::optional<u8> exefs_id = boost::none; | ||
| 97 | |||
| 98 | FileUtil::IOFile file; | ||
| 99 | std::string path; | ||
| 100 | |||
| 101 | u64 GetExeFsFileOffset(const std::string& file_name) const; | ||
| 102 | u64 GetExeFsFileSize(const std::string& file_name) const; | ||
| 103 | |||
| 104 | public: | ||
| 105 | ResultStatus Load(FileUtil::IOFile&& file, std::string path); | ||
| 106 | |||
| 107 | FileSys::PartitionFilesystem GetPfs(u8 id) const; | ||
| 108 | |||
| 109 | u64 GetRomFsOffset() const; | ||
| 110 | u64 GetRomFsSize() const; | ||
| 111 | |||
| 112 | std::vector<u8> GetExeFsFile(const std::string& file_name); | ||
| 113 | }; | ||
| 114 | |||
| 115 | static bool IsPfsExeFs(const FileSys::PartitionFilesystem& pfs) { | ||
| 116 | // According to switchbrew, an exefs must only contain these two files: | ||
| 117 | return pfs.GetFileSize("main") > 0 && pfs.GetFileSize("main.npdm") > 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | ResultStatus Nca::Load(FileUtil::IOFile&& in_file, std::string in_path) { | ||
| 121 | file = std::move(in_file); | ||
| 122 | path = in_path; | ||
| 123 | file.Seek(0, SEEK_SET); | ||
| 124 | std::array<u8, sizeof(NcaHeader)> header_array{}; | ||
| 125 | if (sizeof(NcaHeader) != file.ReadBytes(header_array.data(), sizeof(NcaHeader))) | ||
| 126 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 127 | |||
| 128 | NcaHeader header{}; | ||
| 129 | std::memcpy(&header, header_array.data(), sizeof(NcaHeader)); | ||
| 130 | if (!IsValidNca(header)) | ||
| 131 | return ResultStatus::ErrorInvalidFormat; | ||
| 132 | |||
| 133 | int number_sections = | ||
| 134 | std::count_if(std::begin(header.section_tables), std::end(header.section_tables), | ||
| 135 | [](NcaSectionTableEntry entry) { return entry.media_offset > 0; }); | ||
| 136 | |||
| 137 | for (int i = 0; i < number_sections; ++i) { | ||
| 138 | // Seek to beginning of this section. | ||
| 139 | file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET); | ||
| 140 | std::array<u8, sizeof(NcaSectionHeaderBlock)> array{}; | ||
| 141 | if (sizeof(NcaSectionHeaderBlock) != | ||
| 142 | file.ReadBytes(array.data(), sizeof(NcaSectionHeaderBlock))) | ||
| 143 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 144 | |||
| 145 | NcaSectionHeaderBlock block{}; | ||
| 146 | std::memcpy(&block, array.data(), sizeof(NcaSectionHeaderBlock)); | ||
| 147 | |||
| 148 | if (block.filesystem_type == NcaSectionFilesystemType::ROMFS) { | ||
| 149 | romfs_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER; | ||
| 150 | romfs_size = | ||
| 151 | header.section_tables[i].media_end_offset * MEDIA_OFFSET_MULTIPLIER - romfs_offset; | ||
| 152 | } else if (block.filesystem_type == NcaSectionFilesystemType::PFS0) { | ||
| 153 | Pfs0Superblock sb{}; | ||
| 154 | // Seek back to beginning of this section. | ||
| 155 | file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET); | ||
| 156 | if (sizeof(Pfs0Superblock) != file.ReadBytes(&sb, sizeof(Pfs0Superblock))) | ||
| 157 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 158 | |||
| 159 | u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * | ||
| 160 | MEDIA_OFFSET_MULTIPLIER) + | ||
| 161 | sb.pfs0_header_offset; | ||
| 162 | FileSys::PartitionFilesystem npfs{}; | ||
| 163 | ResultStatus status = npfs.Load(path, offset); | ||
| 164 | |||
| 165 | if (status == ResultStatus::Success) { | ||
| 166 | pfs.emplace_back(std::move(npfs)); | ||
| 167 | pfs_offset.emplace_back(offset); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | for (size_t i = 0; i < pfs.size(); ++i) { | ||
| 173 | if (IsPfsExeFs(pfs[i])) | ||
| 174 | exefs_id = i; | ||
| 175 | } | ||
| 176 | |||
| 177 | return ResultStatus::Success; | ||
| 178 | } | ||
| 179 | |||
| 180 | FileSys::PartitionFilesystem Nca::GetPfs(u8 id) const { | ||
| 181 | return pfs[id]; | ||
| 182 | } | ||
| 183 | |||
| 184 | u64 Nca::GetExeFsFileOffset(const std::string& file_name) const { | ||
| 185 | if (exefs_id == boost::none) | ||
| 186 | return 0; | ||
| 187 | return pfs[*exefs_id].GetFileOffset(file_name) + pfs_offset[*exefs_id]; | ||
| 188 | } | ||
| 189 | |||
| 190 | u64 Nca::GetExeFsFileSize(const std::string& file_name) const { | ||
| 191 | if (exefs_id == boost::none) | ||
| 192 | return 0; | ||
| 193 | return pfs[*exefs_id].GetFileSize(file_name); | ||
| 194 | } | ||
| 195 | |||
| 196 | u64 Nca::GetRomFsOffset() const { | ||
| 197 | return romfs_offset; | ||
| 198 | } | ||
| 199 | |||
| 200 | u64 Nca::GetRomFsSize() const { | ||
| 201 | return romfs_size; | ||
| 202 | } | ||
| 203 | |||
| 204 | std::vector<u8> Nca::GetExeFsFile(const std::string& file_name) { | ||
| 205 | std::vector<u8> out(GetExeFsFileSize(file_name)); | ||
| 206 | file.Seek(GetExeFsFileOffset(file_name), SEEK_SET); | ||
| 207 | file.ReadBytes(out.data(), GetExeFsFileSize(file_name)); | ||
| 208 | return out; | ||
| 209 | } | ||
| 210 | |||
| 211 | AppLoader_NCA::AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath) | ||
| 212 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 213 | |||
| 214 | FileType AppLoader_NCA::IdentifyType(FileUtil::IOFile& file, const std::string&) { | ||
| 215 | file.Seek(0, SEEK_SET); | ||
| 216 | std::array<u8, 0x400> header_enc_array{}; | ||
| 217 | if (0x400 != file.ReadBytes(header_enc_array.data(), 0x400)) | ||
| 218 | return FileType::Error; | ||
| 219 | 22 | ||
| 23 | FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) { | ||
| 220 | // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support. | 24 | // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support. |
| 221 | NcaHeader header{}; | 25 | FileSys::NCAHeader header{}; |
| 222 | std::memcpy(&header, header_enc_array.data(), sizeof(NcaHeader)); | 26 | if (sizeof(FileSys::NCAHeader) != file->ReadObject(&header)) |
| 27 | return FileType::Error; | ||
| 223 | 28 | ||
| 224 | if (IsValidNca(header) && header.content_type == NcaContentType::Program) | 29 | if (IsValidNCA(header) && header.content_type == FileSys::NCAContentType::Program) |
| 225 | return FileType::NCA; | 30 | return FileType::NCA; |
| 226 | 31 | ||
| 227 | return FileType::Error; | 32 | return FileType::Error; |
| @@ -231,17 +36,22 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 231 | if (is_loaded) { | 36 | if (is_loaded) { |
| 232 | return ResultStatus::ErrorAlreadyLoaded; | 37 | return ResultStatus::ErrorAlreadyLoaded; |
| 233 | } | 38 | } |
| 234 | if (!file.IsOpen()) { | ||
| 235 | return ResultStatus::Error; | ||
| 236 | } | ||
| 237 | 39 | ||
| 238 | nca = std::make_unique<Nca>(); | 40 | nca = std::make_unique<FileSys::NCA>(file); |
| 239 | ResultStatus result = nca->Load(std::move(file), filepath); | 41 | ResultStatus result = nca->GetStatus(); |
| 240 | if (result != ResultStatus::Success) { | 42 | if (result != ResultStatus::Success) { |
| 241 | return result; | 43 | return result; |
| 242 | } | 44 | } |
| 243 | 45 | ||
| 244 | result = metadata.Load(nca->GetExeFsFile("main.npdm")); | 46 | if (nca->GetType() != FileSys::NCAContentType::Program) |
| 47 | return ResultStatus::ErrorInvalidFormat; | ||
| 48 | |||
| 49 | auto exefs = nca->GetExeFS(); | ||
| 50 | |||
| 51 | if (exefs == nullptr) | ||
| 52 | return ResultStatus::ErrorInvalidFormat; | ||
| 53 | |||
| 54 | result = metadata.Load(exefs->GetFile("main.npdm")); | ||
| 245 | if (result != ResultStatus::Success) { | 55 | if (result != ResultStatus::Success) { |
| 246 | return result; | 56 | return result; |
| 247 | } | 57 | } |
| @@ -256,7 +66,8 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 256 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", | 66 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", |
| 257 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { | 67 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { |
| 258 | const VAddr load_addr = next_load_addr; | 68 | const VAddr load_addr = next_load_addr; |
| 259 | next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr); | 69 | |
| 70 | next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr); | ||
| 260 | if (next_load_addr) { | 71 | if (next_load_addr) { |
| 261 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); | 72 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); |
| 262 | } else { | 73 | } else { |
| @@ -272,28 +83,11 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 272 | process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), | 83 | process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), |
| 273 | metadata.GetMainThreadStackSize()); | 84 | metadata.GetMainThreadStackSize()); |
| 274 | 85 | ||
| 275 | if (nca->GetRomFsSize() > 0) | ||
| 276 | Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this), | ||
| 277 | Service::FileSystem::Type::RomFS); | ||
| 278 | |||
| 279 | is_loaded = true; | 86 | is_loaded = true; |
| 280 | return ResultStatus::Success; | ||
| 281 | } | ||
| 282 | |||
| 283 | ResultStatus AppLoader_NCA::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | ||
| 284 | u64& size) { | ||
| 285 | if (nca->GetRomFsSize() == 0) { | ||
| 286 | LOG_DEBUG(Loader, "No RomFS available"); | ||
| 287 | return ResultStatus::ErrorNotUsed; | ||
| 288 | } | ||
| 289 | |||
| 290 | romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb"); | ||
| 291 | |||
| 292 | offset = nca->GetRomFsOffset(); | ||
| 293 | size = nca->GetRomFsSize(); | ||
| 294 | 87 | ||
| 295 | LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset); | 88 | const auto romfs = nca->GetRomFS(); |
| 296 | LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size); | 89 | if (romfs != nullptr) |
| 90 | Service::FileSystem::RegisterRomFS(romfs); | ||
| 297 | 91 | ||
| 298 | return ResultStatus::Success; | 92 | return ResultStatus::Success; |
| 299 | } | 93 | } |
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 3b6c451d0..a5639f149 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h | |||
| @@ -6,44 +6,37 @@ | |||
| 6 | 6 | ||
| 7 | #include <string> | 7 | #include <string> |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/file_sys/partition_filesystem.h" | 9 | #include "core/file_sys/content_archive.h" |
| 10 | #include "core/file_sys/program_metadata.h" | 10 | #include "core/file_sys/program_metadata.h" |
| 11 | #include "core/hle/kernel/kernel.h" | 11 | #include "core/hle/kernel/kernel.h" |
| 12 | #include "core/loader/loader.h" | 12 | #include "core/loader/loader.h" |
| 13 | 13 | ||
| 14 | namespace Loader { | 14 | namespace Loader { |
| 15 | 15 | ||
| 16 | class Nca; | ||
| 17 | |||
| 18 | /// Loads an NCA file | 16 | /// Loads an NCA file |
| 19 | class AppLoader_NCA final : public AppLoader { | 17 | class AppLoader_NCA final : public AppLoader { |
| 20 | public: | 18 | public: |
| 21 | AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath); | 19 | explicit AppLoader_NCA(FileSys::VirtualFile file); |
| 22 | 20 | ||
| 23 | /** | 21 | /** |
| 24 | * Returns the type of the file | 22 | * Returns the type of the file |
| 25 | * @param file FileUtil::IOFile open file | 23 | * @param file std::shared_ptr<VfsFile> open file |
| 26 | * @param filepath Path of the file that we are opening. | ||
| 27 | * @return FileType found, or FileType::Error if this loader doesn't know it | 24 | * @return FileType found, or FileType::Error if this loader doesn't know it |
| 28 | */ | 25 | */ |
| 29 | static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); | 26 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 30 | 27 | ||
| 31 | FileType GetFileType() override { | 28 | FileType GetFileType() override { |
| 32 | return IdentifyType(file, filepath); | 29 | return IdentifyType(file); |
| 33 | } | 30 | } |
| 34 | 31 | ||
| 35 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 32 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 36 | 33 | ||
| 37 | ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | ||
| 38 | u64& size) override; | ||
| 39 | |||
| 40 | ~AppLoader_NCA(); | 34 | ~AppLoader_NCA(); |
| 41 | 35 | ||
| 42 | private: | 36 | private: |
| 43 | std::string filepath; | ||
| 44 | FileSys::ProgramMetadata metadata; | 37 | FileSys::ProgramMetadata metadata; |
| 45 | 38 | ||
| 46 | std::unique_ptr<Nca> nca; | 39 | std::unique_ptr<FileSys::NCA> nca; |
| 47 | }; | 40 | }; |
| 48 | 41 | ||
| 49 | } // namespace Loader | 42 | } // namespace Loader |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 3853cfa1a..08b7aad7a 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -47,14 +47,12 @@ struct ModHeader { | |||
| 47 | }; | 47 | }; |
| 48 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); | 48 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); |
| 49 | 49 | ||
| 50 | AppLoader_NRO::AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath) | 50 | AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(file) {} |
| 51 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 52 | 51 | ||
| 53 | FileType AppLoader_NRO::IdentifyType(FileUtil::IOFile& file, const std::string&) { | 52 | FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) { |
| 54 | // Read NSO header | 53 | // Read NSO header |
| 55 | NroHeader nro_header{}; | 54 | NroHeader nro_header{}; |
| 56 | file.Seek(0, SEEK_SET); | 55 | if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { |
| 57 | if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) { | ||
| 58 | return FileType::Error; | 56 | return FileType::Error; |
| 59 | } | 57 | } |
| 60 | if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) { | 58 | if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) { |
| @@ -67,16 +65,10 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 67 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | 65 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; |
| 68 | } | 66 | } |
| 69 | 67 | ||
| 70 | bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) { | 68 | bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { |
| 71 | FileUtil::IOFile file(path, "rb"); | ||
| 72 | if (!file.IsOpen()) { | ||
| 73 | return {}; | ||
| 74 | } | ||
| 75 | |||
| 76 | // Read NSO header | 69 | // Read NSO header |
| 77 | NroHeader nro_header{}; | 70 | NroHeader nro_header{}; |
| 78 | file.Seek(0, SEEK_SET); | 71 | if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { |
| 79 | if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) { | ||
| 80 | return {}; | 72 | return {}; |
| 81 | } | 73 | } |
| 82 | if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { | 74 | if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { |
| @@ -85,10 +77,9 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) { | |||
| 85 | 77 | ||
| 86 | // Build program image | 78 | // Build program image |
| 87 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); | 79 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); |
| 88 | std::vector<u8> program_image; | 80 | std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size)); |
| 89 | program_image.resize(PageAlignSize(nro_header.file_size)); | 81 | if (program_image.size() != PageAlignSize(nro_header.file_size)) |
| 90 | file.Seek(0, SEEK_SET); | 82 | return {}; |
| 91 | file.ReadBytes(program_image.data(), nro_header.file_size); | ||
| 92 | 83 | ||
| 93 | for (int i = 0; i < nro_header.segments.size(); ++i) { | 84 | for (int i = 0; i < nro_header.segments.size(); ++i) { |
| 94 | codeset->segments[i].addr = nro_header.segments[i].offset; | 85 | codeset->segments[i].addr = nro_header.segments[i].offset; |
| @@ -111,7 +102,7 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) { | |||
| 111 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); | 102 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); |
| 112 | 103 | ||
| 113 | // Load codeset for current process | 104 | // Load codeset for current process |
| 114 | codeset->name = path; | 105 | codeset->name = file->GetName(); |
| 115 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | 106 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); |
| 116 | Core::CurrentProcess()->LoadModule(codeset, load_base); | 107 | Core::CurrentProcess()->LoadModule(codeset, load_base); |
| 117 | 108 | ||
| @@ -122,14 +113,11 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 122 | if (is_loaded) { | 113 | if (is_loaded) { |
| 123 | return ResultStatus::ErrorAlreadyLoaded; | 114 | return ResultStatus::ErrorAlreadyLoaded; |
| 124 | } | 115 | } |
| 125 | if (!file.IsOpen()) { | ||
| 126 | return ResultStatus::Error; | ||
| 127 | } | ||
| 128 | 116 | ||
| 129 | // Load NRO | 117 | // Load NRO |
| 130 | static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; | 118 | static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; |
| 131 | 119 | ||
| 132 | if (!LoadNro(filepath, base_addr)) { | 120 | if (!LoadNro(file, base_addr)) { |
| 133 | return ResultStatus::ErrorInvalidFormat; | 121 | return ResultStatus::ErrorInvalidFormat; |
| 134 | } | 122 | } |
| 135 | 123 | ||
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 599adb253..2c03d06bb 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h | |||
| @@ -15,26 +15,23 @@ namespace Loader { | |||
| 15 | /// Loads an NRO file | 15 | /// Loads an NRO file |
| 16 | class AppLoader_NRO final : public AppLoader, Linker { | 16 | class AppLoader_NRO final : public AppLoader, Linker { |
| 17 | public: | 17 | public: |
| 18 | AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath); | 18 | AppLoader_NRO(FileSys::VirtualFile file); |
| 19 | 19 | ||
| 20 | /** | 20 | /** |
| 21 | * Returns the type of the file | 21 | * Returns the type of the file |
| 22 | * @param file FileUtil::IOFile open file | 22 | * @param file std::shared_ptr<VfsFile> open file |
| 23 | * @param filepath Path of the file that we are opening. | ||
| 24 | * @return FileType found, or FileType::Error if this loader doesn't know it | 23 | * @return FileType found, or FileType::Error if this loader doesn't know it |
| 25 | */ | 24 | */ |
| 26 | static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); | 25 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 27 | 26 | ||
| 28 | FileType GetFileType() override { | 27 | FileType GetFileType() override { |
| 29 | return IdentifyType(file, filepath); | 28 | return IdentifyType(file); |
| 30 | } | 29 | } |
| 31 | 30 | ||
| 32 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 31 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 33 | 32 | ||
| 34 | private: | 33 | private: |
| 35 | bool LoadNro(const std::string& path, VAddr load_base); | 34 | bool LoadNro(FileSys::VirtualFile file, VAddr load_base); |
| 36 | |||
| 37 | std::string filepath; | ||
| 38 | }; | 35 | }; |
| 39 | 36 | ||
| 40 | } // namespace Loader | 37 | } // namespace Loader |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 7f84e4b1b..2ed12a5ab 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -37,6 +37,7 @@ struct NsoHeader { | |||
| 37 | std::array<u32_le, 3> segments_compressed_size; | 37 | std::array<u32_le, 3> segments_compressed_size; |
| 38 | }; | 38 | }; |
| 39 | static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); | 39 | static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); |
| 40 | static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable."); | ||
| 40 | 41 | ||
| 41 | struct ModHeader { | 42 | struct ModHeader { |
| 42 | u32_le magic; | 43 | u32_le magic; |
| @@ -49,15 +50,11 @@ struct ModHeader { | |||
| 49 | }; | 50 | }; |
| 50 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); | 51 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); |
| 51 | 52 | ||
| 52 | AppLoader_NSO::AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath) | 53 | AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} |
| 53 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 54 | 54 | ||
| 55 | FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file, const std::string&) { | 55 | FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) { |
| 56 | u32 magic = 0; | 56 | u32 magic = 0; |
| 57 | file.Seek(0, SEEK_SET); | 57 | file->ReadObject(&magic); |
| 58 | if (1 != file.ReadArray<u32>(&magic, 1)) { | ||
| 59 | return FileType::Error; | ||
| 60 | } | ||
| 61 | 58 | ||
| 62 | if (Common::MakeMagic('N', 'S', 'O', '0') == magic) { | 59 | if (Common::MakeMagic('N', 'S', 'O', '0') == magic) { |
| 63 | return FileType::NSO; | 60 | return FileType::NSO; |
| @@ -98,80 +95,27 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 98 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | 95 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; |
| 99 | } | 96 | } |
| 100 | 97 | ||
| 101 | VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& file_data, | 98 | VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { |
| 102 | VAddr load_base) { | 99 | if (file == nullptr) |
| 103 | if (file_data.size() < sizeof(NsoHeader)) | ||
| 104 | return {}; | 100 | return {}; |
| 105 | 101 | ||
| 106 | NsoHeader nso_header; | 102 | if (file->GetSize() < sizeof(NsoHeader)) |
| 107 | std::memcpy(&nso_header, file_data.data(), sizeof(NsoHeader)); | ||
| 108 | |||
| 109 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) | ||
| 110 | return {}; | 103 | return {}; |
| 111 | 104 | ||
| 112 | // Build program image | ||
| 113 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); | ||
| 114 | std::vector<u8> program_image; | ||
| 115 | for (int i = 0; i < nso_header.segments.size(); ++i) { | ||
| 116 | std::vector<u8> compressed_data(nso_header.segments_compressed_size[i]); | ||
| 117 | for (int j = 0; j < nso_header.segments_compressed_size[i]; ++j) | ||
| 118 | compressed_data[j] = file_data[nso_header.segments[i].offset + j]; | ||
| 119 | std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]); | ||
| 120 | program_image.resize(nso_header.segments[i].location); | ||
| 121 | program_image.insert(program_image.end(), data.begin(), data.end()); | ||
| 122 | codeset->segments[i].addr = nso_header.segments[i].location; | ||
| 123 | codeset->segments[i].offset = nso_header.segments[i].location; | ||
| 124 | codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); | ||
| 125 | } | ||
| 126 | |||
| 127 | // MOD header pointer is at .text offset + 4 | ||
| 128 | u32 module_offset; | ||
| 129 | std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); | ||
| 130 | |||
| 131 | // Read MOD header | ||
| 132 | ModHeader mod_header{}; | ||
| 133 | // Default .bss to size in segment header if MOD0 section doesn't exist | ||
| 134 | u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; | ||
| 135 | std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader)); | ||
| 136 | const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; | ||
| 137 | if (has_mod_header) { | ||
| 138 | // Resize program image to include .bss section and page align each section | ||
| 139 | bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); | ||
| 140 | } | ||
| 141 | codeset->data.size += bss_size; | ||
| 142 | const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; | ||
| 143 | program_image.resize(image_size); | ||
| 144 | |||
| 145 | // Load codeset for current process | ||
| 146 | codeset->name = name; | ||
| 147 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | ||
| 148 | Core::CurrentProcess()->LoadModule(codeset, load_base); | ||
| 149 | |||
| 150 | return load_base + image_size; | ||
| 151 | } | ||
| 152 | |||
| 153 | VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) { | ||
| 154 | FileUtil::IOFile file(path, "rb"); | ||
| 155 | if (!file.IsOpen()) { | ||
| 156 | return {}; | ||
| 157 | } | ||
| 158 | |||
| 159 | // Read NSO header | ||
| 160 | NsoHeader nso_header{}; | 105 | NsoHeader nso_header{}; |
| 161 | file.Seek(0, SEEK_SET); | 106 | if (sizeof(NsoHeader) != file->ReadObject(&nso_header)) |
| 162 | if (sizeof(NsoHeader) != file.ReadBytes(&nso_header, sizeof(NsoHeader))) { | ||
| 163 | return {}; | 107 | return {}; |
| 164 | } | 108 | |
| 165 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { | 109 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) |
| 166 | return {}; | 110 | return {}; |
| 167 | } | ||
| 168 | 111 | ||
| 169 | // Build program image | 112 | // Build program image |
| 170 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); | 113 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); |
| 171 | std::vector<u8> program_image; | 114 | std::vector<u8> program_image; |
| 172 | for (int i = 0; i < nso_header.segments.size(); ++i) { | 115 | for (int i = 0; i < nso_header.segments.size(); ++i) { |
| 173 | std::vector<u8> data = | 116 | const std::vector<u8> compressed_data = |
| 174 | ReadSegment(file, nso_header.segments[i], nso_header.segments_compressed_size[i]); | 117 | file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); |
| 118 | std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]); | ||
| 175 | program_image.resize(nso_header.segments[i].location); | 119 | program_image.resize(nso_header.segments[i].location); |
| 176 | program_image.insert(program_image.end(), data.begin(), data.end()); | 120 | program_image.insert(program_image.end(), data.begin(), data.end()); |
| 177 | codeset->segments[i].addr = nso_header.segments[i].location; | 121 | codeset->segments[i].addr = nso_header.segments[i].location; |
| @@ -198,7 +142,7 @@ VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) { | |||
| 198 | program_image.resize(image_size); | 142 | program_image.resize(image_size); |
| 199 | 143 | ||
| 200 | // Load codeset for current process | 144 | // Load codeset for current process |
| 201 | codeset->name = path; | 145 | codeset->name = file->GetName(); |
| 202 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | 146 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); |
| 203 | Core::CurrentProcess()->LoadModule(codeset, load_base); | 147 | Core::CurrentProcess()->LoadModule(codeset, load_base); |
| 204 | 148 | ||
| @@ -209,13 +153,10 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 209 | if (is_loaded) { | 153 | if (is_loaded) { |
| 210 | return ResultStatus::ErrorAlreadyLoaded; | 154 | return ResultStatus::ErrorAlreadyLoaded; |
| 211 | } | 155 | } |
| 212 | if (!file.IsOpen()) { | ||
| 213 | return ResultStatus::Error; | ||
| 214 | } | ||
| 215 | 156 | ||
| 216 | // Load module | 157 | // Load module |
| 217 | LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR); | 158 | LoadModule(file, Memory::PROCESS_IMAGE_VADDR); |
| 218 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", filepath, Memory::PROCESS_IMAGE_VADDR); | 159 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), Memory::PROCESS_IMAGE_VADDR); |
| 219 | 160 | ||
| 220 | process->svc_access_mask.set(); | 161 | process->svc_access_mask.set(); |
| 221 | process->address_mappings = default_address_mappings; | 162 | process->address_mappings = default_address_mappings; |
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 386f4d39a..3f7567500 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h | |||
| @@ -15,29 +15,22 @@ namespace Loader { | |||
| 15 | /// Loads an NSO file | 15 | /// Loads an NSO file |
| 16 | class AppLoader_NSO final : public AppLoader, Linker { | 16 | class AppLoader_NSO final : public AppLoader, Linker { |
| 17 | public: | 17 | public: |
| 18 | AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath); | 18 | explicit AppLoader_NSO(FileSys::VirtualFile file); |
| 19 | 19 | ||
| 20 | /** | 20 | /** |
| 21 | * Returns the type of the file | 21 | * Returns the type of the file |
| 22 | * @param file FileUtil::IOFile open file | 22 | * @param file std::shared_ptr<VfsFile> open file |
| 23 | * @param filepath Path of the file that we are opening. | ||
| 24 | * @return FileType found, or FileType::Error if this loader doesn't know it | 23 | * @return FileType found, or FileType::Error if this loader doesn't know it |
| 25 | */ | 24 | */ |
| 26 | static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); | 25 | static FileType IdentifyType(const FileSys::VirtualFile& file); |
| 27 | 26 | ||
| 28 | FileType GetFileType() override { | 27 | FileType GetFileType() override { |
| 29 | return IdentifyType(file, filepath); | 28 | return IdentifyType(file); |
| 30 | } | 29 | } |
| 31 | 30 | ||
| 32 | static VAddr LoadModule(const std::string& name, const std::vector<u8>& file_data, | 31 | static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base); |
| 33 | VAddr load_base); | ||
| 34 | |||
| 35 | static VAddr LoadModule(const std::string& path, VAddr load_base); | ||
| 36 | 32 | ||
| 37 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 33 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 38 | |||
| 39 | private: | ||
| 40 | std::string filepath; | ||
| 41 | }; | 34 | }; |
| 42 | 35 | ||
| 43 | } // namespace Loader | 36 | } // namespace Loader |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 5a708dc73..be6cd6da4 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -12,6 +12,7 @@ | |||
| 12 | #include "common/common_paths.h" | 12 | #include "common/common_paths.h" |
| 13 | #include "common/logging/log.h" | 13 | #include "common/logging/log.h" |
| 14 | #include "common/string_util.h" | 14 | #include "common/string_util.h" |
| 15 | #include "core/file_sys/vfs_real.h" | ||
| 15 | #include "core/loader/loader.h" | 16 | #include "core/loader/loader.h" |
| 16 | #include "game_list.h" | 17 | #include "game_list.h" |
| 17 | #include "game_list_p.h" | 18 | #include "game_list_p.h" |
| @@ -404,7 +405,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||
| 404 | bool is_dir = FileUtil::IsDirectory(physical_name); | 405 | bool is_dir = FileUtil::IsDirectory(physical_name); |
| 405 | if (!is_dir && | 406 | if (!is_dir && |
| 406 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { | 407 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { |
| 407 | std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name); | 408 | std::unique_ptr<Loader::AppLoader> loader = |
| 409 | Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name)); | ||
| 408 | if (!loader) | 410 | if (!loader) |
| 409 | return true; | 411 | return true; |
| 410 | 412 | ||
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h index 5896971d4..71d2e040f 100644 --- a/src/yuzu_cmd/default_ini.h +++ b/src/yuzu_cmd/default_ini.h | |||
| @@ -166,7 +166,7 @@ use_virtual_sd = | |||
| 166 | # 1 (default): Yes, 0: No | 166 | # 1 (default): Yes, 0: No |
| 167 | use_docked_mode = | 167 | use_docked_mode = |
| 168 | 168 | ||
| 169 | # The system region that Citra will use during emulation | 169 | # The system region that yuzu will use during emulation |
| 170 | # -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan | 170 | # -1: Auto-select (default), 0: Japan, 1: USA, 2: Europe, 3: Australia, 4: China, 5: Korea, 6: Taiwan |
| 171 | region_value = | 171 | region_value = |
| 172 | 172 | ||