diff options
Diffstat (limited to 'src')
64 files changed, 1993 insertions, 2017 deletions
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 7213abe18..bf955386c 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 | } |
| @@ -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,71 @@ 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 GetPathWithoutTop(std::string path) { | ||
| 830 | if (path.empty()) | ||
| 831 | return ""; | ||
| 832 | while (path[0] == '\\' || path[0] == '/') { | ||
| 833 | path = path.substr(1); | ||
| 834 | if (path.empty()) | ||
| 835 | return ""; | ||
| 836 | } | ||
| 837 | const auto name_bck_index = path.find_first_of('\\'); | ||
| 838 | const auto name_fwd_index = path.find_first_of('/'); | ||
| 839 | return path.substr(std::min<size_t>(name_bck_index, name_fwd_index) + 1); | ||
| 840 | return path.substr(std::min<size_t>(name_bck_index, name_fwd_index) + 1); | ||
| 841 | } | ||
| 842 | |||
| 843 | std::string GetFilename(std::string path) { | ||
| 844 | std::replace(path.begin(), path.end(), '\\', '/'); | ||
| 845 | auto name_index = path.find_last_of('/'); | ||
| 846 | if (name_index == std::string::npos) | ||
| 847 | return ""; | ||
| 848 | return path.substr(name_index + 1); | ||
| 849 | } | ||
| 850 | |||
| 851 | std::string GetExtensionFromFilename(const std::string& name) { | ||
| 852 | size_t index = name.find_last_of('.'); | ||
| 853 | if (index == std::string::npos) | ||
| 854 | return ""; | ||
| 855 | |||
| 856 | return name.substr(index + 1); | ||
| 857 | } | ||
| 858 | |||
| 859 | std::string RemoveTrailingSlash(const std::string& path) { | ||
| 860 | if (path.empty()) | ||
| 861 | return path; | ||
| 862 | if (path.back() == '\\' || path.back() == '/') | ||
| 863 | return path.substr(0, path.size() - 1); | ||
| 864 | |||
| 865 | return path; | ||
| 866 | } | ||
| 867 | |||
| 802 | IOFile::IOFile() {} | 868 | IOFile::IOFile() {} |
| 803 | 869 | ||
| 804 | IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { | 870 | IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { |
| @@ -820,7 +886,6 @@ IOFile& IOFile::operator=(IOFile&& other) noexcept { | |||
| 820 | 886 | ||
| 821 | void IOFile::Swap(IOFile& other) noexcept { | 887 | void IOFile::Swap(IOFile& other) noexcept { |
| 822 | std::swap(m_file, other.m_file); | 888 | std::swap(m_file, other.m_file); |
| 823 | std::swap(m_good, other.m_good); | ||
| 824 | } | 889 | } |
| 825 | 890 | ||
| 826 | bool IOFile::Open(const std::string& filename, const char openmode[], int flags) { | 891 | bool IOFile::Open(const std::string& filename, const char openmode[], int flags) { |
| @@ -837,16 +902,15 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags) | |||
| 837 | m_file = fopen(filename.c_str(), openmode); | 902 | m_file = fopen(filename.c_str(), openmode); |
| 838 | #endif | 903 | #endif |
| 839 | 904 | ||
| 840 | m_good = IsOpen(); | 905 | return IsOpen(); |
| 841 | return m_good; | ||
| 842 | } | 906 | } |
| 843 | 907 | ||
| 844 | bool IOFile::Close() { | 908 | bool IOFile::Close() { |
| 845 | if (!IsOpen() || 0 != std::fclose(m_file)) | 909 | if (!IsOpen() || 0 != std::fclose(m_file)) |
| 846 | m_good = false; | 910 | return false; |
| 847 | 911 | ||
| 848 | m_file = nullptr; | 912 | m_file = nullptr; |
| 849 | return m_good; | 913 | return true; |
| 850 | } | 914 | } |
| 851 | 915 | ||
| 852 | u64 IOFile::GetSize() const { | 916 | u64 IOFile::GetSize() const { |
| @@ -856,11 +920,8 @@ u64 IOFile::GetSize() const { | |||
| 856 | return 0; | 920 | return 0; |
| 857 | } | 921 | } |
| 858 | 922 | ||
| 859 | bool IOFile::Seek(s64 off, int origin) { | 923 | bool IOFile::Seek(s64 off, int origin) const { |
| 860 | if (!IsOpen() || 0 != fseeko(m_file, off, origin)) | 924 | return IsOpen() && 0 == fseeko(m_file, off, origin); |
| 861 | m_good = false; | ||
| 862 | |||
| 863 | return m_good; | ||
| 864 | } | 925 | } |
| 865 | 926 | ||
| 866 | u64 IOFile::Tell() const { | 927 | u64 IOFile::Tell() const { |
| @@ -871,26 +932,20 @@ u64 IOFile::Tell() const { | |||
| 871 | } | 932 | } |
| 872 | 933 | ||
| 873 | bool IOFile::Flush() { | 934 | bool IOFile::Flush() { |
| 874 | if (!IsOpen() || 0 != std::fflush(m_file)) | 935 | return IsOpen() && 0 == std::fflush(m_file); |
| 875 | m_good = false; | ||
| 876 | |||
| 877 | return m_good; | ||
| 878 | } | 936 | } |
| 879 | 937 | ||
| 880 | bool IOFile::Resize(u64 size) { | 938 | bool IOFile::Resize(u64 size) { |
| 881 | if (!IsOpen() || 0 != | 939 | return IsOpen() && 0 == |
| 882 | #ifdef _WIN32 | 940 | #ifdef _WIN32 |
| 883 | // ector: _chsize sucks, not 64-bit safe | 941 | // ector: _chsize sucks, not 64-bit safe |
| 884 | // F|RES: changed to _chsize_s. i think it is 64-bit safe | 942 | // F|RES: changed to _chsize_s. i think it is 64-bit safe |
| 885 | _chsize_s(_fileno(m_file), size) | 943 | _chsize_s(_fileno(m_file), size) |
| 886 | #else | 944 | #else |
| 887 | // TODO: handle 64bit and growing | 945 | // TODO: handle 64bit and growing |
| 888 | ftruncate(fileno(m_file), size) | 946 | ftruncate(fileno(m_file), size) |
| 889 | #endif | 947 | #endif |
| 890 | ) | 948 | ; |
| 891 | m_good = false; | ||
| 892 | |||
| 893 | return m_good; | ||
| 894 | } | 949 | } |
| 895 | 950 | ||
| 896 | } // namespace FileUtil | 951 | } // namespace FileUtil |
diff --git a/src/common/file_util.h b/src/common/file_util.h index 5bc7fbf7c..026c84d94 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -150,6 +150,34 @@ 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 up to the last '/' or '\' in the path. | ||
| 158 | std::string GetParentPath(const std::string& path); | ||
| 159 | |||
| 160 | // Gets all of the text after the first '/' or '\' in the path. | ||
| 161 | std::string GetPathWithoutTop(std::string path); | ||
| 162 | |||
| 163 | // Gets the filename of the path | ||
| 164 | std::string GetFilename(std::string path); | ||
| 165 | |||
| 166 | // Gets the extension of the filename | ||
| 167 | std::string GetExtensionFromFilename(const std::string& name); | ||
| 168 | |||
| 169 | // Removes the final '/' or '\' if one exists | ||
| 170 | std::string RemoveTrailingSlash(const std::string& path); | ||
| 171 | |||
| 172 | // Creates a new vector containing indices [first, last) from the original. | ||
| 173 | template <typename T> | ||
| 174 | std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) { | ||
| 175 | if (first >= last) | ||
| 176 | return {}; | ||
| 177 | last = std::min<size_t>(last, vector.size()); | ||
| 178 | return std::vector<T>(vector.begin() + first, vector.begin() + first + last); | ||
| 179 | } | ||
| 180 | |||
| 153 | // simple wrapper for cstdlib file functions to | 181 | // simple wrapper for cstdlib file functions to |
| 154 | // hopefully will make error checking easier | 182 | // hopefully will make error checking easier |
| 155 | // and make forgetting an fclose() harder | 183 | // and make forgetting an fclose() harder |
| @@ -172,41 +200,27 @@ public: | |||
| 172 | bool Close(); | 200 | bool Close(); |
| 173 | 201 | ||
| 174 | template <typename T> | 202 | template <typename T> |
| 175 | size_t ReadArray(T* data, size_t length) { | 203 | size_t ReadArray(T* data, size_t length) const { |
| 176 | static_assert(std::is_trivially_copyable<T>(), | 204 | static_assert(std::is_trivially_copyable<T>(), |
| 177 | "Given array does not consist of trivially copyable objects"); | 205 | "Given array does not consist of trivially copyable objects"); |
| 178 | 206 | ||
| 179 | if (!IsOpen()) { | 207 | if (!IsOpen()) |
| 180 | m_good = false; | ||
| 181 | return -1; | 208 | return -1; |
| 182 | } | ||
| 183 | |||
| 184 | size_t items_read = std::fread(data, sizeof(T), length, m_file); | ||
| 185 | if (items_read != length) | ||
| 186 | m_good = false; | ||
| 187 | 209 | ||
| 188 | return items_read; | 210 | return std::fread(data, sizeof(T), length, m_file); |
| 189 | } | 211 | } |
| 190 | 212 | ||
| 191 | template <typename T> | 213 | template <typename T> |
| 192 | size_t WriteArray(const T* data, size_t length) { | 214 | size_t WriteArray(const T* data, size_t length) { |
| 193 | static_assert(std::is_trivially_copyable<T>(), | 215 | static_assert(std::is_trivially_copyable<T>(), |
| 194 | "Given array does not consist of trivially copyable objects"); | 216 | "Given array does not consist of trivially copyable objects"); |
| 195 | 217 | if (!IsOpen()) | |
| 196 | if (!IsOpen()) { | ||
| 197 | m_good = false; | ||
| 198 | return -1; | 218 | return -1; |
| 199 | } | 219 | 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 | } | 220 | } |
| 207 | 221 | ||
| 208 | template <typename T> | 222 | template <typename T> |
| 209 | size_t ReadBytes(T* data, size_t length) { | 223 | size_t ReadBytes(T* data, size_t length) const { |
| 210 | static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable"); | 224 | static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable"); |
| 211 | return ReadArray(reinterpret_cast<char*>(data), length); | 225 | return ReadArray(reinterpret_cast<char*>(data), length); |
| 212 | } | 226 | } |
| @@ -231,15 +245,7 @@ public: | |||
| 231 | return nullptr != m_file; | 245 | return nullptr != m_file; |
| 232 | } | 246 | } |
| 233 | 247 | ||
| 234 | // m_good is set to false when a read, write or other function fails | 248 | 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; | 249 | u64 Tell() const; |
| 244 | u64 GetSize() const; | 250 | u64 GetSize() const; |
| 245 | bool Resize(u64 size); | 251 | bool Resize(u64 size); |
| @@ -247,13 +253,11 @@ public: | |||
| 247 | 253 | ||
| 248 | // clear error state | 254 | // clear error state |
| 249 | void Clear() { | 255 | void Clear() { |
| 250 | m_good = true; | ||
| 251 | std::clearerr(m_file); | 256 | std::clearerr(m_file); |
| 252 | } | 257 | } |
| 253 | 258 | ||
| 254 | private: | 259 | private: |
| 255 | std::FILE* m_file = nullptr; | 260 | std::FILE* m_file = nullptr; |
| 256 | bool m_good = true; | ||
| 257 | }; | 261 | }; |
| 258 | 262 | ||
| 259 | } // namespace FileUtil | 263 | } // namespace FileUtil |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 3dff068df..0a5d58eea 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -8,27 +8,27 @@ 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/mode.h |
| 16 | file_sys/filesystem.h | ||
| 17 | file_sys/partition_filesystem.cpp | 16 | file_sys/partition_filesystem.cpp |
| 18 | file_sys/partition_filesystem.h | 17 | file_sys/partition_filesystem.h |
| 19 | file_sys/path_parser.cpp | ||
| 20 | file_sys/path_parser.h | ||
| 21 | file_sys/program_metadata.cpp | 18 | file_sys/program_metadata.cpp |
| 22 | file_sys/program_metadata.h | 19 | file_sys/program_metadata.h |
| 23 | file_sys/romfs_factory.cpp | 20 | file_sys/romfs_factory.cpp |
| 24 | file_sys/romfs_factory.h | 21 | file_sys/romfs_factory.h |
| 25 | file_sys/romfs_filesystem.cpp | ||
| 26 | file_sys/romfs_filesystem.h | ||
| 27 | file_sys/savedata_factory.cpp | 22 | file_sys/savedata_factory.cpp |
| 28 | file_sys/savedata_factory.h | 23 | file_sys/savedata_factory.h |
| 29 | file_sys/sdmc_factory.cpp | 24 | file_sys/sdmc_factory.cpp |
| 30 | file_sys/sdmc_factory.h | 25 | file_sys/sdmc_factory.h |
| 31 | file_sys/storage.h | 26 | file_sys/vfs.cpp |
| 27 | file_sys/vfs.h | ||
| 28 | file_sys/vfs_offset.cpp | ||
| 29 | file_sys/vfs_offset.h | ||
| 30 | file_sys/vfs_real.cpp | ||
| 31 | file_sys/vfs_real.h | ||
| 32 | frontend/emu_window.cpp | 32 | frontend/emu_window.cpp |
| 33 | frontend/emu_window.h | 33 | frontend/emu_window.h |
| 34 | frontend/framebuffer_layout.cpp | 34 | frontend/framebuffer_layout.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 8335d502e..9bd9f4bd9 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -19,17 +19,20 @@ | |||
| 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 { |
| 25 | 26 | ||
| 26 | /*static*/ System System::s_instance; | 27 | /*static*/ System System::s_instance; |
| 27 | 28 | ||
| 29 | System::System() = default; | ||
| 30 | |||
| 28 | System::~System() = default; | 31 | System::~System() = default; |
| 29 | 32 | ||
| 30 | /// Runs a CPU core while the system is powered on | 33 | /// Runs a CPU core while the system is powered on |
| 31 | static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) { | 34 | static void RunCpuCore(std::shared_ptr<Cpu> cpu_state) { |
| 32 | while (Core::System().GetInstance().IsPoweredOn()) { | 35 | while (Core::System::GetInstance().IsPoweredOn()) { |
| 33 | cpu_state->RunLoop(true); | 36 | cpu_state->RunLoop(true); |
| 34 | } | 37 | } |
| 35 | } | 38 | } |
| @@ -84,7 +87,7 @@ System::ResultStatus System::SingleStep() { | |||
| 84 | } | 87 | } |
| 85 | 88 | ||
| 86 | System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) { | 89 | System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) { |
| 87 | app_loader = Loader::GetLoader(filepath); | 90 | app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath)); |
| 88 | 91 | ||
| 89 | if (!app_loader) { | 92 | if (!app_loader) { |
| 90 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); | 93 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); |
diff --git a/src/core/core.h b/src/core/core.h index f90f085ad..c6f69f001 100644 --- a/src/core/core.h +++ b/src/core/core.h | |||
| @@ -168,6 +168,8 @@ public: | |||
| 168 | } | 168 | } |
| 169 | 169 | ||
| 170 | private: | 170 | private: |
| 171 | System(); | ||
| 172 | |||
| 171 | /// Returns the currently running CPU core | 173 | /// Returns the currently running CPU core |
| 172 | Cpu& CurrentCpuCore(); | 174 | Cpu& CurrentCpuCore(); |
| 173 | 175 | ||
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp new file mode 100644 index 000000000..6cfef774d --- /dev/null +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -0,0 +1,167 @@ | |||
| 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 { | ||
| 21 | PFS0 = 0x2, | ||
| 22 | ROMFS = 0x3, | ||
| 23 | }; | ||
| 24 | |||
| 25 | struct NCASectionHeaderBlock { | ||
| 26 | INSERT_PADDING_BYTES(3); | ||
| 27 | NCASectionFilesystemType filesystem_type; | ||
| 28 | u8 crypto_type; | ||
| 29 | INSERT_PADDING_BYTES(3); | ||
| 30 | }; | ||
| 31 | static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); | ||
| 32 | |||
| 33 | struct PFS0Superblock { | ||
| 34 | NCASectionHeaderBlock header_block; | ||
| 35 | std::array<u8, 0x20> hash; | ||
| 36 | u32_le size; | ||
| 37 | INSERT_PADDING_BYTES(4); | ||
| 38 | u64_le hash_table_offset; | ||
| 39 | u64_le hash_table_size; | ||
| 40 | u64_le pfs0_header_offset; | ||
| 41 | u64_le pfs0_size; | ||
| 42 | INSERT_PADDING_BYTES(432); | ||
| 43 | }; | ||
| 44 | static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); | ||
| 45 | |||
| 46 | struct IVFCLevel { | ||
| 47 | u64_le offset; | ||
| 48 | u64_le size; | ||
| 49 | u32_le block_size; | ||
| 50 | u32_le reserved; | ||
| 51 | }; | ||
| 52 | static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size."); | ||
| 53 | |||
| 54 | struct RomFSSuperblock { | ||
| 55 | NCASectionHeaderBlock header_block; | ||
| 56 | u32_le magic; | ||
| 57 | u32_le magic_number; | ||
| 58 | INSERT_PADDING_BYTES(8); | ||
| 59 | std::array<IVFCLevel, 6> levels; | ||
| 60 | INSERT_PADDING_BYTES(64); | ||
| 61 | }; | ||
| 62 | static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size."); | ||
| 63 | |||
| 64 | NCA::NCA(VirtualFile file_) : file(file_) { | ||
| 65 | if (sizeof(NCAHeader) != file->ReadObject(&header)) | ||
| 66 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 67 | |||
| 68 | if (!IsValidNCA(header)) { | ||
| 69 | status = Loader::ResultStatus::ErrorInvalidFormat; | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | |||
| 73 | std::ptrdiff_t number_sections = | ||
| 74 | std::count_if(std::begin(header.section_tables), std::end(header.section_tables), | ||
| 75 | [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); | ||
| 76 | |||
| 77 | for (std::ptrdiff_t i = 0; i < number_sections; ++i) { | ||
| 78 | // Seek to beginning of this section. | ||
| 79 | NCASectionHeaderBlock block{}; | ||
| 80 | if (sizeof(NCASectionHeaderBlock) != | ||
| 81 | file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) | ||
| 82 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 83 | |||
| 84 | if (block.filesystem_type == NCASectionFilesystemType::ROMFS) { | ||
| 85 | RomFSSuperblock sb{}; | ||
| 86 | if (sizeof(RomFSSuperblock) != | ||
| 87 | file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) | ||
| 88 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 89 | |||
| 90 | const size_t romfs_offset = | ||
| 91 | header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + | ||
| 92 | sb.levels[IVFC_MAX_LEVEL - 1].offset; | ||
| 93 | const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size; | ||
| 94 | files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset)); | ||
| 95 | romfs = files.back(); | ||
| 96 | } else if (block.filesystem_type == NCASectionFilesystemType::PFS0) { | ||
| 97 | PFS0Superblock sb{}; | ||
| 98 | // Seek back to beginning of this section. | ||
| 99 | if (sizeof(PFS0Superblock) != | ||
| 100 | file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) | ||
| 101 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 102 | |||
| 103 | u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * | ||
| 104 | MEDIA_OFFSET_MULTIPLIER) + | ||
| 105 | sb.pfs0_header_offset; | ||
| 106 | u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - | ||
| 107 | header.section_tables[i].media_offset); | ||
| 108 | auto npfs = std::make_shared<PartitionFilesystem>( | ||
| 109 | std::make_shared<OffsetVfsFile>(file, size, offset)); | ||
| 110 | |||
| 111 | if (npfs->GetStatus() == Loader::ResultStatus::Success) { | ||
| 112 | dirs.emplace_back(npfs); | ||
| 113 | if (IsDirectoryExeFS(dirs.back())) | ||
| 114 | exefs = dirs.back(); | ||
| 115 | } | ||
| 116 | } | ||
| 117 | } | ||
| 118 | |||
| 119 | status = Loader::ResultStatus::Success; | ||
| 120 | } | ||
| 121 | |||
| 122 | Loader::ResultStatus NCA::GetStatus() const { | ||
| 123 | return status; | ||
| 124 | } | ||
| 125 | |||
| 126 | std::vector<std::shared_ptr<VfsFile>> NCA::GetFiles() const { | ||
| 127 | if (status != Loader::ResultStatus::Success) | ||
| 128 | return {}; | ||
| 129 | return files; | ||
| 130 | } | ||
| 131 | |||
| 132 | std::vector<std::shared_ptr<VfsDirectory>> NCA::GetSubdirectories() const { | ||
| 133 | if (status != Loader::ResultStatus::Success) | ||
| 134 | return {}; | ||
| 135 | return dirs; | ||
| 136 | } | ||
| 137 | |||
| 138 | std::string NCA::GetName() const { | ||
| 139 | return file->GetName(); | ||
| 140 | } | ||
| 141 | |||
| 142 | std::shared_ptr<VfsDirectory> NCA::GetParentDirectory() const { | ||
| 143 | return file->GetContainingDirectory(); | ||
| 144 | } | ||
| 145 | |||
| 146 | NCAContentType NCA::GetType() const { | ||
| 147 | return header.content_type; | ||
| 148 | } | ||
| 149 | |||
| 150 | u64 NCA::GetTitleId() const { | ||
| 151 | if (status != Loader::ResultStatus::Success) | ||
| 152 | return {}; | ||
| 153 | return header.title_id; | ||
| 154 | } | ||
| 155 | |||
| 156 | VirtualFile NCA::GetRomFS() const { | ||
| 157 | return romfs; | ||
| 158 | } | ||
| 159 | |||
| 160 | VirtualDir NCA::GetExeFS() const { | ||
| 161 | return exefs; | ||
| 162 | } | ||
| 163 | |||
| 164 | bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 165 | return false; | ||
| 166 | } | ||
| 167 | } // 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..129a70b97 --- /dev/null +++ b/src/core/file_sys/content_archive.h | |||
| @@ -0,0 +1,95 @@ | |||
| 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 { | ||
| 15 | Program = 0, | ||
| 16 | Meta = 1, | ||
| 17 | Control = 2, | ||
| 18 | Manual = 3, | ||
| 19 | Data = 4, | ||
| 20 | }; | ||
| 21 | |||
| 22 | struct NCASectionTableEntry { | ||
| 23 | u32_le media_offset; | ||
| 24 | u32_le media_end_offset; | ||
| 25 | INSERT_PADDING_BYTES(0x8); | ||
| 26 | }; | ||
| 27 | static_assert(sizeof(NCASectionTableEntry) == 0x10, "NCASectionTableEntry has incorrect size."); | ||
| 28 | |||
| 29 | struct NCAHeader { | ||
| 30 | std::array<u8, 0x100> rsa_signature_1; | ||
| 31 | std::array<u8, 0x100> rsa_signature_2; | ||
| 32 | u32_le magic; | ||
| 33 | u8 is_system; | ||
| 34 | NCAContentType content_type; | ||
| 35 | u8 crypto_type; | ||
| 36 | u8 key_index; | ||
| 37 | u64_le size; | ||
| 38 | u64_le title_id; | ||
| 39 | INSERT_PADDING_BYTES(0x4); | ||
| 40 | u32_le sdk_version; | ||
| 41 | u8 crypto_type_2; | ||
| 42 | INSERT_PADDING_BYTES(15); | ||
| 43 | std::array<u8, 0x10> rights_id; | ||
| 44 | std::array<NCASectionTableEntry, 0x4> section_tables; | ||
| 45 | std::array<std::array<u8, 0x20>, 0x4> hash_tables; | ||
| 46 | std::array<std::array<u8, 0x10>, 0x4> key_area; | ||
| 47 | INSERT_PADDING_BYTES(0xC0); | ||
| 48 | }; | ||
| 49 | static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size."); | ||
| 50 | |||
| 51 | inline bool IsDirectoryExeFS(std::shared_ptr<FileSys::VfsDirectory> pfs) { | ||
| 52 | // According to switchbrew, an exefs must only contain these two files: | ||
| 53 | return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; | ||
| 54 | } | ||
| 55 | |||
| 56 | inline bool IsValidNCA(const NCAHeader& header) { | ||
| 57 | return header.magic == Common::MakeMagic('N', 'C', 'A', '2') || | ||
| 58 | header.magic == Common::MakeMagic('N', 'C', 'A', '3'); | ||
| 59 | } | ||
| 60 | |||
| 61 | // An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. | ||
| 62 | // After construction, use GetStatus to determine if the file is valid and ready to be used. | ||
| 63 | class NCA : public ReadOnlyVfsDirectory { | ||
| 64 | public: | ||
| 65 | explicit NCA(VirtualFile file); | ||
| 66 | Loader::ResultStatus GetStatus() const; | ||
| 67 | |||
| 68 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | ||
| 69 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | ||
| 70 | std::string GetName() const override; | ||
| 71 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | ||
| 72 | |||
| 73 | NCAContentType GetType() const; | ||
| 74 | u64 GetTitleId() const; | ||
| 75 | |||
| 76 | VirtualFile GetRomFS() const; | ||
| 77 | VirtualDir GetExeFS() const; | ||
| 78 | |||
| 79 | protected: | ||
| 80 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 81 | |||
| 82 | private: | ||
| 83 | std::vector<VirtualDir> dirs; | ||
| 84 | std::vector<VirtualFile> files; | ||
| 85 | |||
| 86 | VirtualFile romfs = nullptr; | ||
| 87 | VirtualDir exefs = nullptr; | ||
| 88 | VirtualFile file; | ||
| 89 | |||
| 90 | NCAHeader header{}; | ||
| 91 | |||
| 92 | Loader::ResultStatus status{}; | ||
| 93 | }; | ||
| 94 | |||
| 95 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h index c7639795e..213ce1826 100644 --- a/src/core/file_sys/directory.h +++ b/src/core/file_sys/directory.h | |||
| @@ -8,13 +8,17 @@ | |||
| 8 | #include <cstddef> | 8 | #include <cstddef> |
| 9 | #include "common/common_funcs.h" | 9 | #include "common/common_funcs.h" |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| 11 | #include "core/file_sys/filesystem.h" | ||
| 12 | 11 | ||
| 13 | //////////////////////////////////////////////////////////////////////////////////////////////////// | 12 | //////////////////////////////////////////////////////////////////////////////////////////////////// |
| 14 | // FileSys namespace | 13 | // FileSys namespace |
| 15 | 14 | ||
| 16 | namespace FileSys { | 15 | namespace FileSys { |
| 17 | 16 | ||
| 17 | enum EntryType : u8 { | ||
| 18 | Directory = 0, | ||
| 19 | File = 1, | ||
| 20 | }; | ||
| 21 | |||
| 18 | // Structure of a directory entry, from | 22 | // Structure of a directory entry, from |
| 19 | // http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry | 23 | // http://switchbrew.org/index.php?title=Filesystem_services#DirectoryEntry |
| 20 | const size_t FILENAME_LENGTH = 0x300; | 24 | const size_t FILENAME_LENGTH = 0x300; |
diff --git a/src/core/file_sys/disk_filesystem.cpp b/src/core/file_sys/disk_filesystem.cpp deleted file mode 100644 index d248c2df4..000000000 --- a/src/core/file_sys/disk_filesystem.cpp +++ /dev/null | |||
| @@ -1,239 +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 | std::string full_path = base_directory + path; | ||
| 62 | |||
| 63 | if (!FileUtil::Exists(full_path)) { | ||
| 64 | return ERROR_PATH_NOT_FOUND; | ||
| 65 | } | ||
| 66 | |||
| 67 | FileUtil::Delete(full_path); | ||
| 68 | |||
| 69 | return RESULT_SUCCESS; | ||
| 70 | } | ||
| 71 | |||
| 72 | ResultCode Disk_FileSystem::RenameFile(const std::string& src_path, | ||
| 73 | const std::string& dest_path) const { | ||
| 74 | const std::string full_src_path = base_directory + src_path; | ||
| 75 | const std::string full_dest_path = base_directory + dest_path; | ||
| 76 | |||
| 77 | if (!FileUtil::Exists(full_src_path)) { | ||
| 78 | return ERROR_PATH_NOT_FOUND; | ||
| 79 | } | ||
| 80 | // TODO(wwylele): Use correct error code | ||
| 81 | return FileUtil::Rename(full_src_path, full_dest_path) ? RESULT_SUCCESS : ResultCode(-1); | ||
| 82 | } | ||
| 83 | |||
| 84 | ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const { | ||
| 85 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 86 | // TODO(wwylele): Use correct error code | ||
| 87 | return ResultCode(-1); | ||
| 88 | } | ||
| 89 | |||
| 90 | ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const { | ||
| 91 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 92 | // TODO(wwylele): Use correct error code | ||
| 93 | return ResultCode(-1); | ||
| 94 | } | ||
| 95 | |||
| 96 | ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const { | ||
| 97 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 98 | |||
| 99 | std::string full_path = base_directory + path; | ||
| 100 | if (size == 0) { | ||
| 101 | FileUtil::CreateEmptyFile(full_path); | ||
| 102 | return RESULT_SUCCESS; | ||
| 103 | } | ||
| 104 | |||
| 105 | FileUtil::IOFile file(full_path, "wb"); | ||
| 106 | // Creates a sparse file (or a normal file on filesystems without the concept of sparse files) | ||
| 107 | // We do this by seeking to the right size, then writing a single null byte. | ||
| 108 | if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) { | ||
| 109 | return RESULT_SUCCESS; | ||
| 110 | } | ||
| 111 | |||
| 112 | LOG_ERROR(Service_FS, "Too large file"); | ||
| 113 | // TODO(Subv): Find out the correct error code | ||
| 114 | return ResultCode(-1); | ||
| 115 | } | ||
| 116 | |||
| 117 | ResultCode Disk_FileSystem::CreateDirectory(const std::string& path) const { | ||
| 118 | // TODO(Subv): Perform path validation to prevent escaping the emulator sandbox. | ||
| 119 | std::string full_path = base_directory + path; | ||
| 120 | |||
| 121 | if (FileUtil::CreateDir(full_path)) { | ||
| 122 | return RESULT_SUCCESS; | ||
| 123 | } | ||
| 124 | |||
| 125 | LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating {}", full_path); | ||
| 126 | // TODO(wwylele): Use correct error code | ||
| 127 | return ResultCode(-1); | ||
| 128 | } | ||
| 129 | |||
| 130 | ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { | ||
| 131 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 132 | // TODO(wwylele): Use correct error code | ||
| 133 | return ResultCode(-1); | ||
| 134 | } | ||
| 135 | |||
| 136 | ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory( | ||
| 137 | const std::string& path) const { | ||
| 138 | |||
| 139 | std::string full_path = base_directory + path; | ||
| 140 | |||
| 141 | if (!FileUtil::IsDirectory(full_path)) { | ||
| 142 | // TODO(Subv): Find the correct error code for this. | ||
| 143 | return ResultCode(-1); | ||
| 144 | } | ||
| 145 | |||
| 146 | auto directory = std::make_unique<Disk_Directory>(full_path); | ||
| 147 | return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory)); | ||
| 148 | } | ||
| 149 | |||
| 150 | u64 Disk_FileSystem::GetFreeSpaceSize() const { | ||
| 151 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 152 | return 0; | ||
| 153 | } | ||
| 154 | |||
| 155 | ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& path) const { | ||
| 156 | std::string full_path = base_directory + path; | ||
| 157 | if (!FileUtil::Exists(full_path)) { | ||
| 158 | return ERROR_PATH_NOT_FOUND; | ||
| 159 | } | ||
| 160 | |||
| 161 | if (FileUtil::IsDirectory(full_path)) | ||
| 162 | return MakeResult(EntryType::Directory); | ||
| 163 | |||
| 164 | return MakeResult(EntryType::File); | ||
| 165 | } | ||
| 166 | |||
| 167 | ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { | ||
| 168 | LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); | ||
| 169 | file->Seek(offset, SEEK_SET); | ||
| 170 | return MakeResult<size_t>(file->ReadBytes(buffer, length)); | ||
| 171 | } | ||
| 172 | |||
| 173 | ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush, | ||
| 174 | const u8* buffer) const { | ||
| 175 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 176 | file->Seek(offset, SEEK_SET); | ||
| 177 | size_t written = file->WriteBytes(buffer, length); | ||
| 178 | if (flush) { | ||
| 179 | file->Flush(); | ||
| 180 | } | ||
| 181 | return MakeResult<size_t>(written); | ||
| 182 | } | ||
| 183 | |||
| 184 | u64 Disk_Storage::GetSize() const { | ||
| 185 | return file->GetSize(); | ||
| 186 | } | ||
| 187 | |||
| 188 | bool Disk_Storage::SetSize(const u64 size) const { | ||
| 189 | file->Resize(size); | ||
| 190 | file->Flush(); | ||
| 191 | return true; | ||
| 192 | } | ||
| 193 | |||
| 194 | Disk_Directory::Disk_Directory(const std::string& path) { | ||
| 195 | unsigned size = FileUtil::ScanDirectoryTree(path, directory); | ||
| 196 | directory.size = size; | ||
| 197 | directory.isDirectory = true; | ||
| 198 | children_iterator = directory.children.begin(); | ||
| 199 | } | ||
| 200 | |||
| 201 | u64 Disk_Directory::Read(const u64 count, Entry* entries) { | ||
| 202 | u64 entries_read = 0; | ||
| 203 | |||
| 204 | while (entries_read < count && children_iterator != directory.children.cend()) { | ||
| 205 | const FileUtil::FSTEntry& file = *children_iterator; | ||
| 206 | const std::string& filename = file.virtualName; | ||
| 207 | Entry& entry = entries[entries_read]; | ||
| 208 | |||
| 209 | LOG_TRACE(Service_FS, "File {}: size={} dir={}", filename, file.size, file.isDirectory); | ||
| 210 | |||
| 211 | // TODO(Link Mauve): use a proper conversion to UTF-16. | ||
| 212 | for (size_t j = 0; j < FILENAME_LENGTH; ++j) { | ||
| 213 | entry.filename[j] = filename[j]; | ||
| 214 | if (!filename[j]) | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | |||
| 218 | if (file.isDirectory) { | ||
| 219 | entry.file_size = 0; | ||
| 220 | entry.type = EntryType::Directory; | ||
| 221 | } else { | ||
| 222 | entry.file_size = file.size; | ||
| 223 | entry.type = EntryType::File; | ||
| 224 | } | ||
| 225 | |||
| 226 | ++entries_read; | ||
| 227 | ++children_iterator; | ||
| 228 | } | ||
| 229 | return entries_read; | ||
| 230 | } | ||
| 231 | |||
| 232 | u64 Disk_Directory::GetEntryCount() const { | ||
| 233 | // We convert the children iterator into a const_iterator to allow template argument deduction | ||
| 234 | // in std::distance. | ||
| 235 | std::vector<FileUtil::FSTEntry>::const_iterator current = children_iterator; | ||
| 236 | return std::distance(current, directory.children.end()); | ||
| 237 | } | ||
| 238 | |||
| 239 | } // 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/errors.h b/src/core/file_sys/errors.h index 1f3b8fa84..a152dbd33 100644 --- a/src/core/file_sys/errors.h +++ b/src/core/file_sys/errors.h | |||
| @@ -11,7 +11,7 @@ namespace FileSys { | |||
| 11 | namespace ErrCodes { | 11 | namespace ErrCodes { |
| 12 | enum { | 12 | enum { |
| 13 | NotFound = 1, | 13 | NotFound = 1, |
| 14 | SaveDataNotFound = 1002, | 14 | TitleNotFound = 1002, |
| 15 | SdCardNotFound = 2001, | 15 | SdCardNotFound = 2001, |
| 16 | RomFSNotFound = 2520, | 16 | RomFSNotFound = 2520, |
| 17 | }; | 17 | }; |
diff --git a/src/core/file_sys/filesystem.cpp b/src/core/file_sys/filesystem.cpp deleted file mode 100644 index 82fdb3c46..000000000 --- a/src/core/file_sys/filesystem.cpp +++ /dev/null | |||
| @@ -1,122 +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 <cstddef> | ||
| 6 | #include <iomanip> | ||
| 7 | #include <sstream> | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "common/string_util.h" | ||
| 10 | #include "core/file_sys/filesystem.h" | ||
| 11 | #include "core/memory.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | Path::Path(LowPathType type, u32 size, u32 pointer) : type(type) { | ||
| 16 | switch (type) { | ||
| 17 | case Binary: { | ||
| 18 | binary.resize(size); | ||
| 19 | Memory::ReadBlock(pointer, binary.data(), binary.size()); | ||
| 20 | break; | ||
| 21 | } | ||
| 22 | |||
| 23 | case Char: { | ||
| 24 | string.resize(size - 1); // Data is always null-terminated. | ||
| 25 | Memory::ReadBlock(pointer, &string[0], string.size()); | ||
| 26 | break; | ||
| 27 | } | ||
| 28 | |||
| 29 | case Wchar: { | ||
| 30 | u16str.resize(size / 2 - 1); // Data is always null-terminated. | ||
| 31 | Memory::ReadBlock(pointer, &u16str[0], u16str.size() * sizeof(char16_t)); | ||
| 32 | break; | ||
| 33 | } | ||
| 34 | |||
| 35 | default: | ||
| 36 | break; | ||
| 37 | } | ||
| 38 | } | ||
| 39 | |||
| 40 | std::string Path::DebugStr() const { | ||
| 41 | switch (GetType()) { | ||
| 42 | case Invalid: | ||
| 43 | default: | ||
| 44 | return "[Invalid]"; | ||
| 45 | case Empty: | ||
| 46 | return "[Empty]"; | ||
| 47 | case Binary: { | ||
| 48 | std::stringstream res; | ||
| 49 | res << "[Binary: "; | ||
| 50 | for (unsigned byte : binary) | ||
| 51 | res << std::hex << std::setw(2) << std::setfill('0') << byte; | ||
| 52 | res << ']'; | ||
| 53 | return res.str(); | ||
| 54 | } | ||
| 55 | case Char: | ||
| 56 | return "[Char: " + AsString() + ']'; | ||
| 57 | case Wchar: | ||
| 58 | return "[Wchar: " + AsString() + ']'; | ||
| 59 | } | ||
| 60 | } | ||
| 61 | |||
| 62 | std::string Path::AsString() const { | ||
| 63 | switch (GetType()) { | ||
| 64 | case Char: | ||
| 65 | return string; | ||
| 66 | case Wchar: | ||
| 67 | return Common::UTF16ToUTF8(u16str); | ||
| 68 | case Empty: | ||
| 69 | return {}; | ||
| 70 | case Invalid: | ||
| 71 | case Binary: | ||
| 72 | default: | ||
| 73 | // TODO(yuriks): Add assert | ||
| 74 | LOG_ERROR(Service_FS, "LowPathType cannot be converted to string!"); | ||
| 75 | return {}; | ||
| 76 | } | ||
| 77 | } | ||
| 78 | |||
| 79 | std::u16string Path::AsU16Str() const { | ||
| 80 | switch (GetType()) { | ||
| 81 | case Char: | ||
| 82 | return Common::UTF8ToUTF16(string); | ||
| 83 | case Wchar: | ||
| 84 | return u16str; | ||
| 85 | case Empty: | ||
| 86 | return {}; | ||
| 87 | case Invalid: | ||
| 88 | case Binary: | ||
| 89 | // TODO(yuriks): Add assert | ||
| 90 | LOG_ERROR(Service_FS, "LowPathType cannot be converted to u16string!"); | ||
| 91 | return {}; | ||
| 92 | } | ||
| 93 | |||
| 94 | UNREACHABLE(); | ||
| 95 | } | ||
| 96 | |||
| 97 | std::vector<u8> Path::AsBinary() const { | ||
| 98 | switch (GetType()) { | ||
| 99 | case Binary: | ||
| 100 | return binary; | ||
| 101 | case Char: | ||
| 102 | return std::vector<u8>(string.begin(), string.end()); | ||
| 103 | case Wchar: { | ||
| 104 | // use two u8 for each character of u16str | ||
| 105 | std::vector<u8> to_return(u16str.size() * 2); | ||
| 106 | for (size_t i = 0; i < u16str.size(); ++i) { | ||
| 107 | u16 tmp_char = u16str.at(i); | ||
| 108 | to_return[i * 2] = (tmp_char & 0xFF00) >> 8; | ||
| 109 | to_return[i * 2 + 1] = (tmp_char & 0x00FF); | ||
| 110 | } | ||
| 111 | return to_return; | ||
| 112 | } | ||
| 113 | case Empty: | ||
| 114 | return {}; | ||
| 115 | case Invalid: | ||
| 116 | default: | ||
| 117 | // TODO(yuriks): Add assert | ||
| 118 | LOG_ERROR(Service_FS, "LowPathType cannot be converted to binary!"); | ||
| 119 | return {}; | ||
| 120 | } | ||
| 121 | } | ||
| 122 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/filesystem.h b/src/core/file_sys/filesystem.h deleted file mode 100644 index 1a32a373b..000000000 --- a/src/core/file_sys/filesystem.h +++ /dev/null | |||
| @@ -1,170 +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 <utility> | ||
| 10 | #include <vector> | ||
| 11 | #include "common/bit_field.h" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/swap.h" | ||
| 14 | #include "core/hle/result.h" | ||
| 15 | |||
| 16 | namespace FileSys { | ||
| 17 | |||
| 18 | class StorageBackend; | ||
| 19 | class DirectoryBackend; | ||
| 20 | |||
| 21 | // Path string type | ||
| 22 | enum LowPathType : u32 { | ||
| 23 | Invalid = 0, | ||
| 24 | Empty = 1, | ||
| 25 | Binary = 2, | ||
| 26 | Char = 3, | ||
| 27 | Wchar = 4, | ||
| 28 | }; | ||
| 29 | |||
| 30 | enum EntryType : u8 { | ||
| 31 | Directory = 0, | ||
| 32 | File = 1, | ||
| 33 | }; | ||
| 34 | |||
| 35 | enum class Mode : u32 { | ||
| 36 | Read = 1, | ||
| 37 | Write = 2, | ||
| 38 | Append = 4, | ||
| 39 | }; | ||
| 40 | |||
| 41 | class Path { | ||
| 42 | public: | ||
| 43 | Path() : type(Invalid) {} | ||
| 44 | Path(const char* path) : type(Char), string(path) {} | ||
| 45 | Path(std::vector<u8> binary_data) : type(Binary), binary(std::move(binary_data)) {} | ||
| 46 | Path(LowPathType type, u32 size, u32 pointer); | ||
| 47 | |||
| 48 | LowPathType GetType() const { | ||
| 49 | return type; | ||
| 50 | } | ||
| 51 | |||
| 52 | /** | ||
| 53 | * Gets the string representation of the path for debugging | ||
| 54 | * @return String representation of the path for debugging | ||
| 55 | */ | ||
| 56 | std::string DebugStr() const; | ||
| 57 | |||
| 58 | std::string AsString() const; | ||
| 59 | std::u16string AsU16Str() const; | ||
| 60 | std::vector<u8> AsBinary() const; | ||
| 61 | |||
| 62 | private: | ||
| 63 | LowPathType type; | ||
| 64 | std::vector<u8> binary; | ||
| 65 | std::string string; | ||
| 66 | std::u16string u16str; | ||
| 67 | }; | ||
| 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 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/mode.h b/src/core/file_sys/mode.h new file mode 100644 index 000000000..b4363152a --- /dev/null +++ b/src/core/file_sys/mode.h | |||
| @@ -0,0 +1,17 @@ | |||
| 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_types.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | enum class Mode : u32 { | ||
| 12 | Read = 1, | ||
| 13 | Write = 2, | ||
| 14 | Append = 4, | ||
| 15 | }; | ||
| 16 | |||
| 17 | } // 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/path_parser.cpp b/src/core/file_sys/path_parser.cpp deleted file mode 100644 index 5a89b02b8..000000000 --- a/src/core/file_sys/path_parser.cpp +++ /dev/null | |||
| @@ -1,98 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <set> | ||
| 7 | #include "common/file_util.h" | ||
| 8 | #include "common/string_util.h" | ||
| 9 | #include "core/file_sys/path_parser.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | PathParser::PathParser(const Path& path) { | ||
| 14 | if (path.GetType() != LowPathType::Char && path.GetType() != LowPathType::Wchar) { | ||
| 15 | is_valid = false; | ||
| 16 | return; | ||
| 17 | } | ||
| 18 | |||
| 19 | auto path_string = path.AsString(); | ||
| 20 | if (path_string.size() == 0 || path_string[0] != '/') { | ||
| 21 | is_valid = false; | ||
| 22 | return; | ||
| 23 | } | ||
| 24 | |||
| 25 | // Filter out invalid characters for the host system. | ||
| 26 | // Although some of these characters are valid on 3DS, they are unlikely to be used by games. | ||
| 27 | if (std::find_if(path_string.begin(), path_string.end(), [](char c) { | ||
| 28 | static const std::set<char> invalid_chars{'<', '>', '\\', '|', ':', '\"', '*', '?'}; | ||
| 29 | return invalid_chars.find(c) != invalid_chars.end(); | ||
| 30 | }) != path_string.end()) { | ||
| 31 | is_valid = false; | ||
| 32 | return; | ||
| 33 | } | ||
| 34 | |||
| 35 | Common::SplitString(path_string, '/', path_sequence); | ||
| 36 | |||
| 37 | auto begin = path_sequence.begin(); | ||
| 38 | auto end = path_sequence.end(); | ||
| 39 | end = std::remove_if(begin, end, [](std::string& str) { return str == "" || str == "."; }); | ||
| 40 | path_sequence = std::vector<std::string>(begin, end); | ||
| 41 | |||
| 42 | // checks if the path is out of bounds. | ||
| 43 | int level = 0; | ||
| 44 | for (auto& node : path_sequence) { | ||
| 45 | if (node == "..") { | ||
| 46 | --level; | ||
| 47 | if (level < 0) { | ||
| 48 | is_valid = false; | ||
| 49 | return; | ||
| 50 | } | ||
| 51 | } else { | ||
| 52 | ++level; | ||
| 53 | } | ||
| 54 | } | ||
| 55 | |||
| 56 | is_valid = true; | ||
| 57 | is_root = level == 0; | ||
| 58 | } | ||
| 59 | |||
| 60 | PathParser::HostStatus PathParser::GetHostStatus(const std::string& mount_point) const { | ||
| 61 | auto path = mount_point; | ||
| 62 | if (!FileUtil::IsDirectory(path)) | ||
| 63 | return InvalidMountPoint; | ||
| 64 | if (path_sequence.empty()) { | ||
| 65 | return DirectoryFound; | ||
| 66 | } | ||
| 67 | |||
| 68 | for (auto iter = path_sequence.begin(); iter != path_sequence.end() - 1; iter++) { | ||
| 69 | if (path.back() != '/') | ||
| 70 | path += '/'; | ||
| 71 | path += *iter; | ||
| 72 | |||
| 73 | if (!FileUtil::Exists(path)) | ||
| 74 | return PathNotFound; | ||
| 75 | if (FileUtil::IsDirectory(path)) | ||
| 76 | continue; | ||
| 77 | return FileInPath; | ||
| 78 | } | ||
| 79 | |||
| 80 | path += "/" + path_sequence.back(); | ||
| 81 | if (!FileUtil::Exists(path)) | ||
| 82 | return NotFound; | ||
| 83 | if (FileUtil::IsDirectory(path)) | ||
| 84 | return DirectoryFound; | ||
| 85 | return FileFound; | ||
| 86 | } | ||
| 87 | |||
| 88 | std::string PathParser::BuildHostPath(const std::string& mount_point) const { | ||
| 89 | std::string path = mount_point; | ||
| 90 | for (auto& node : path_sequence) { | ||
| 91 | if (path.back() != '/') | ||
| 92 | path += '/'; | ||
| 93 | path += node; | ||
| 94 | } | ||
| 95 | return path; | ||
| 96 | } | ||
| 97 | |||
| 98 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/path_parser.h b/src/core/file_sys/path_parser.h deleted file mode 100644 index 184f59d55..000000000 --- a/src/core/file_sys/path_parser.h +++ /dev/null | |||
| @@ -1,61 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <string> | ||
| 8 | #include <vector> | ||
| 9 | #include "core/file_sys/filesystem.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | /** | ||
| 14 | * A helper class parsing and verifying a string-type Path. | ||
| 15 | * Every archives with a sub file system should use this class to parse the path argument and check | ||
| 16 | * the status of the file / directory in question on the host file system. | ||
| 17 | */ | ||
| 18 | class PathParser { | ||
| 19 | public: | ||
| 20 | explicit PathParser(const Path& path); | ||
| 21 | |||
| 22 | /** | ||
| 23 | * Checks if the Path is valid. | ||
| 24 | * This function should be called once a PathParser is constructed. | ||
| 25 | * A Path is valid if: | ||
| 26 | * - it is a string path (with type LowPathType::Char or LowPathType::Wchar), | ||
| 27 | * - it starts with "/" (this seems a hard requirement in real 3DS), | ||
| 28 | * - it doesn't contain invalid characters, and | ||
| 29 | * - it doesn't go out of the root directory using "..". | ||
| 30 | */ | ||
| 31 | bool IsValid() const { | ||
| 32 | return is_valid; | ||
| 33 | } | ||
| 34 | |||
| 35 | /// Checks if the Path represents the root directory. | ||
| 36 | bool IsRootDirectory() const { | ||
| 37 | return is_root; | ||
| 38 | } | ||
| 39 | |||
| 40 | enum HostStatus { | ||
| 41 | InvalidMountPoint, | ||
| 42 | PathNotFound, // "/a/b/c" when "a" doesn't exist | ||
| 43 | FileInPath, // "/a/b/c" when "a" is a file | ||
| 44 | FileFound, // "/a/b/c" when "c" is a file | ||
| 45 | DirectoryFound, // "/a/b/c" when "c" is a directory | ||
| 46 | NotFound // "/a/b/c" when "a/b/" exists but "c" doesn't exist | ||
| 47 | }; | ||
| 48 | |||
| 49 | /// Checks the status of the specified file / directory by the Path on the host file system. | ||
| 50 | HostStatus GetHostStatus(const std::string& mount_point) const; | ||
| 51 | |||
| 52 | /// Builds a full path on the host file system. | ||
| 53 | std::string BuildHostPath(const std::string& mount_point) const; | ||
| 54 | |||
| 55 | private: | ||
| 56 | std::vector<std::string> path_sequence; | ||
| 57 | bool is_valid{}; | ||
| 58 | bool is_root{}; | ||
| 59 | }; | ||
| 60 | |||
| 61 | } // 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 index 946fc0452..54fbd3267 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp | |||
| @@ -7,21 +7,19 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/file_sys/romfs_factory.h" | 9 | #include "core/file_sys/romfs_factory.h" |
| 10 | #include "core/file_sys/romfs_filesystem.h" | ||
| 11 | 10 | ||
| 12 | namespace FileSys { | 11 | namespace FileSys { |
| 13 | 12 | ||
| 14 | RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { | 13 | RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) { |
| 15 | // Load the RomFS from the app | 14 | // Load the RomFS from the app |
| 16 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { | 15 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(file)) { |
| 17 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); | 16 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); |
| 18 | } | 17 | } |
| 19 | } | 18 | } |
| 20 | 19 | ||
| 21 | ResultVal<std::unique_ptr<FileSystemBackend>> RomFSFactory::Open(u64 title_id) { | 20 | ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id) { |
| 22 | // TODO(DarkLordZach): Use title id. | 21 | // TODO(DarkLordZach): Use title id. |
| 23 | auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size); | 22 | return MakeResult<VirtualFile>(file); |
| 24 | return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); | ||
| 25 | } | 23 | } |
| 26 | 24 | ||
| 27 | } // namespace FileSys | 25 | } // namespace FileSys |
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index c9e20c3ab..c19787cd4 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h | |||
| @@ -5,10 +5,7 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 11 | #include "core/file_sys/filesystem.h" | ||
| 12 | #include "core/hle/result.h" | 9 | #include "core/hle/result.h" |
| 13 | #include "core/loader/loader.h" | 10 | #include "core/loader/loader.h" |
| 14 | 11 | ||
| @@ -19,12 +16,10 @@ class RomFSFactory { | |||
| 19 | public: | 16 | public: |
| 20 | explicit RomFSFactory(Loader::AppLoader& app_loader); | 17 | explicit RomFSFactory(Loader::AppLoader& app_loader); |
| 21 | 18 | ||
| 22 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(u64 title_id); | 19 | ResultVal<VirtualFile> Open(u64 title_id); |
| 23 | 20 | ||
| 24 | private: | 21 | private: |
| 25 | std::shared_ptr<FileUtil::IOFile> romfs_file; | 22 | VirtualFile file; |
| 26 | u64 data_offset; | ||
| 27 | u64 data_size; | ||
| 28 | }; | 23 | }; |
| 29 | 24 | ||
| 30 | } // namespace FileSys | 25 | } // 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 index 3ad37b28c..6a53b2b10 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp | |||
| @@ -6,7 +6,6 @@ | |||
| 6 | #include "common/common_types.h" | 6 | #include "common/common_types.h" |
| 7 | #include "common/logging/log.h" | 7 | #include "common/logging/log.h" |
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/file_sys/disk_filesystem.h" | ||
| 10 | #include "core/file_sys/savedata_factory.h" | 9 | #include "core/file_sys/savedata_factory.h" |
| 11 | #include "core/hle/kernel/process.h" | 10 | #include "core/hle/kernel/process.h" |
| 12 | 11 | ||
| @@ -17,11 +16,9 @@ std::string SaveDataDescriptor::DebugInfo() { | |||
| 17 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id); | 16 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id); |
| 18 | } | 17 | } |
| 19 | 18 | ||
| 20 | SaveDataFactory::SaveDataFactory(std::string nand_directory) | 19 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {} |
| 21 | : nand_directory(std::move(nand_directory)) {} | ||
| 22 | 20 | ||
| 23 | ResultVal<std::unique_ptr<FileSystemBackend>> SaveDataFactory::Open(SaveDataSpaceId space, | 21 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) { |
| 24 | SaveDataDescriptor meta) { | ||
| 25 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { | 22 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { |
| 26 | if (meta.zero_1 != 0) { | 23 | if (meta.zero_1 != 0) { |
| 27 | LOG_WARNING(Service_FS, | 24 | LOG_WARNING(Service_FS, |
| @@ -56,28 +53,23 @@ ResultVal<std::unique_ptr<FileSystemBackend>> SaveDataFactory::Open(SaveDataSpac | |||
| 56 | // TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods. | 53 | // TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods. |
| 57 | // But, user_ids don't match so this works for now. | 54 | // But, user_ids don't match so this works for now. |
| 58 | 55 | ||
| 59 | if (!FileUtil::Exists(save_directory)) { | 56 | auto out = dir->GetDirectoryRelative(save_directory); |
| 57 | |||
| 58 | if (out == nullptr) { | ||
| 60 | // TODO(bunnei): This is a work-around to always create a save data directory if it does not | 59 | // TODO(bunnei): This is a work-around to always create a save data directory if it does not |
| 61 | // already exist. This is a hack, as we do not understand yet how this works on hardware. | 60 | // already exist. This is a hack, as we do not understand yet how this works on hardware. |
| 62 | // Without a save data directory, many games will assert on boot. This should not have any | 61 | // Without a save data directory, many games will assert on boot. This should not have any |
| 63 | // bad side-effects. | 62 | // bad side-effects. |
| 64 | FileUtil::CreateFullPath(save_directory); | 63 | out = dir->CreateDirectoryRelative(save_directory); |
| 65 | } | ||
| 66 | |||
| 67 | // TODO(DarkLordZach): For some reason, CreateFullPath doesn't create the last bit. Should be | ||
| 68 | // fixed with VFS. | ||
| 69 | if (!FileUtil::IsDirectory(save_directory)) { | ||
| 70 | FileUtil::CreateDir(save_directory); | ||
| 71 | } | 64 | } |
| 72 | 65 | ||
| 73 | // Return an error if the save data doesn't actually exist. | 66 | // Return an error if the save data doesn't actually exist. |
| 74 | if (!FileUtil::IsDirectory(save_directory)) { | 67 | if (out == nullptr) { |
| 75 | // TODO(Subv): Find out correct error code. | 68 | // TODO(Subv): Find out correct error code. |
| 76 | return ResultCode(-1); | 69 | return ResultCode(-1); |
| 77 | } | 70 | } |
| 78 | 71 | ||
| 79 | auto archive = std::make_unique<Disk_FileSystem>(save_directory); | 72 | return MakeResult<VirtualDir>(std::move(out)); |
| 80 | return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); | ||
| 81 | } | 73 | } |
| 82 | 74 | ||
| 83 | std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, | 75 | std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, |
| @@ -87,14 +79,14 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ | |||
| 87 | if (type == SaveDataType::SaveData && title_id == 0) | 79 | if (type == SaveDataType::SaveData && title_id == 0) |
| 88 | title_id = Core::CurrentProcess()->program_id; | 80 | title_id = Core::CurrentProcess()->program_id; |
| 89 | 81 | ||
| 90 | std::string prefix; | 82 | std::string out; |
| 91 | 83 | ||
| 92 | switch (space) { | 84 | switch (space) { |
| 93 | case SaveDataSpaceId::NandSystem: | 85 | case SaveDataSpaceId::NandSystem: |
| 94 | prefix = nand_directory + "system/save/"; | 86 | out = "/system/save/"; |
| 95 | break; | 87 | break; |
| 96 | case SaveDataSpaceId::NandUser: | 88 | case SaveDataSpaceId::NandUser: |
| 97 | prefix = nand_directory + "user/save/"; | 89 | out = "/user/save/"; |
| 98 | break; | 90 | break; |
| 99 | default: | 91 | default: |
| 100 | ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); | 92 | ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space)); |
| @@ -102,9 +94,9 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ | |||
| 102 | 94 | ||
| 103 | switch (type) { | 95 | switch (type) { |
| 104 | case SaveDataType::SystemSaveData: | 96 | case SaveDataType::SystemSaveData: |
| 105 | return fmt::format("{}{:016X}/{:016X}{:016X}", prefix, save_id, user_id[1], user_id[0]); | 97 | return fmt::format("{}{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]); |
| 106 | case SaveDataType::SaveData: | 98 | case SaveDataType::SaveData: |
| 107 | return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", prefix, 0, user_id[1], user_id[0], | 99 | return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0], |
| 108 | title_id); | 100 | title_id); |
| 109 | default: | 101 | default: |
| 110 | ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); | 102 | ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type)); |
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index b96721ac0..53c69876f 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h | |||
| @@ -7,7 +7,6 @@ | |||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include <string> | 8 | #include <string> |
| 9 | #include "common/common_types.h" | 9 | #include "common/common_types.h" |
| 10 | #include "core/file_sys/filesystem.h" | ||
| 11 | #include "core/hle/result.h" | 10 | #include "core/hle/result.h" |
| 12 | 11 | ||
| 13 | namespace FileSys { | 12 | namespace FileSys { |
| @@ -45,14 +44,12 @@ static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorr | |||
| 45 | /// File system interface to the SaveData archive | 44 | /// File system interface to the SaveData archive |
| 46 | class SaveDataFactory { | 45 | class SaveDataFactory { |
| 47 | public: | 46 | public: |
| 48 | explicit SaveDataFactory(std::string nand_directory); | 47 | explicit SaveDataFactory(VirtualDir dir); |
| 49 | 48 | ||
| 50 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(SaveDataSpaceId space, | 49 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta); |
| 51 | SaveDataDescriptor meta); | ||
| 52 | 50 | ||
| 53 | private: | 51 | private: |
| 54 | std::string nand_directory; | 52 | VirtualDir dir; |
| 55 | std::string sd_directory; | ||
| 56 | 53 | ||
| 57 | std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, | 54 | std::string GetFullPath(SaveDataSpaceId space, SaveDataType type, u64 title_id, u128 user_id, |
| 58 | u64 save_id) const; | 55 | u64 save_id) const; |
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index ac6f2f971..c1edfcef3 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp | |||
| @@ -3,25 +3,15 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <memory> | 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" | 6 | #include "core/core.h" |
| 10 | #include "core/file_sys/disk_filesystem.h" | ||
| 11 | #include "core/file_sys/sdmc_factory.h" | 7 | #include "core/file_sys/sdmc_factory.h" |
| 12 | 8 | ||
| 13 | namespace FileSys { | 9 | namespace FileSys { |
| 14 | 10 | ||
| 15 | SDMCFactory::SDMCFactory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {} | 11 | SDMCFactory::SDMCFactory(VirtualDir dir) : dir(std::move(dir)) {} |
| 16 | 12 | ||
| 17 | ResultVal<std::unique_ptr<FileSystemBackend>> SDMCFactory::Open() { | 13 | ResultVal<VirtualDir> SDMCFactory::Open() { |
| 18 | // Create the SD Card directory if it doesn't already exist. | 14 | return MakeResult<VirtualDir>(dir); |
| 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 | } | 15 | } |
| 26 | 16 | ||
| 27 | } // namespace FileSys | 17 | } // namespace FileSys |
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h index 09bec7fce..9f0c75e84 100644 --- a/src/core/file_sys/sdmc_factory.h +++ b/src/core/file_sys/sdmc_factory.h | |||
| @@ -4,10 +4,6 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 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" | 7 | #include "core/hle/result.h" |
| 12 | 8 | ||
| 13 | namespace FileSys { | 9 | namespace FileSys { |
| @@ -15,12 +11,12 @@ namespace FileSys { | |||
| 15 | /// File system interface to the SDCard archive | 11 | /// File system interface to the SDCard archive |
| 16 | class SDMCFactory { | 12 | class SDMCFactory { |
| 17 | public: | 13 | public: |
| 18 | explicit SDMCFactory(std::string sd_directory); | 14 | explicit SDMCFactory(VirtualDir dir); |
| 19 | 15 | ||
| 20 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(); | 16 | ResultVal<VirtualDir> Open(); |
| 21 | 17 | ||
| 22 | private: | 18 | private: |
| 23 | std::string sd_directory; | 19 | VirtualDir dir; |
| 24 | }; | 20 | }; |
| 25 | 21 | ||
| 26 | } // namespace FileSys | 22 | } // namespace FileSys |
diff --git a/src/core/file_sys/storage.h b/src/core/file_sys/storage.h deleted file mode 100644 index 2a6811831..000000000 --- a/src/core/file_sys/storage.h +++ /dev/null | |||
| @@ -1,63 +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 "common/common_types.h" | ||
| 9 | #include "core/hle/result.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | class StorageBackend : NonCopyable { | ||
| 14 | public: | ||
| 15 | StorageBackend() {} | ||
| 16 | virtual ~StorageBackend() {} | ||
| 17 | |||
| 18 | /** | ||
| 19 | * Read data from the file | ||
| 20 | * @param offset Offset in bytes to start reading data from | ||
| 21 | * @param length Length in bytes of data to read from file | ||
| 22 | * @param buffer Buffer to read data into | ||
| 23 | * @return Number of bytes read, or error code | ||
| 24 | */ | ||
| 25 | virtual ResultVal<size_t> Read(u64 offset, size_t length, u8* buffer) const = 0; | ||
| 26 | |||
| 27 | /** | ||
| 28 | * Write data to the file | ||
| 29 | * @param offset Offset in bytes to start writing data to | ||
| 30 | * @param length Length in bytes of data to write to file | ||
| 31 | * @param flush The flush parameters (0 == do not flush) | ||
| 32 | * @param buffer Buffer to read data from | ||
| 33 | * @return Number of bytes written, or error code | ||
| 34 | */ | ||
| 35 | virtual ResultVal<size_t> Write(u64 offset, size_t length, bool flush, | ||
| 36 | const u8* buffer) const = 0; | ||
| 37 | |||
| 38 | /** | ||
| 39 | * Flushes the file | ||
| 40 | */ | ||
| 41 | virtual void Flush() const = 0; | ||
| 42 | |||
| 43 | /** | ||
| 44 | * Set the size of the file in bytes | ||
| 45 | * @param size New size of the file | ||
| 46 | * @return true if successful | ||
| 47 | */ | ||
| 48 | virtual bool SetSize(u64 size) const = 0; | ||
| 49 | |||
| 50 | /** | ||
| 51 | * Get the size of the file in bytes | ||
| 52 | * @return Size of the file in bytes | ||
| 53 | */ | ||
| 54 | virtual u64 GetSize() const = 0; | ||
| 55 | |||
| 56 | /** | ||
| 57 | * Close the file | ||
| 58 | * @return true if the file closed correctly | ||
| 59 | */ | ||
| 60 | virtual bool Close() const = 0; | ||
| 61 | }; | ||
| 62 | |||
| 63 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp new file mode 100644 index 000000000..f859ef33f --- /dev/null +++ b/src/core/file_sys/vfs.cpp | |||
| @@ -0,0 +1,238 @@ | |||
| 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 "common/logging/backend.h" | ||
| 9 | #include "core/file_sys/vfs.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | VfsFile::~VfsFile() = default; | ||
| 14 | |||
| 15 | std::string VfsFile::GetExtension() const { | ||
| 16 | return FileUtil::GetExtensionFromFilename(GetName()); | ||
| 17 | } | ||
| 18 | |||
| 19 | VfsDirectory::~VfsDirectory() = default; | ||
| 20 | |||
| 21 | boost::optional<u8> VfsFile::ReadByte(size_t offset) const { | ||
| 22 | u8 out{}; | ||
| 23 | size_t size = Read(&out, 1, offset); | ||
| 24 | if (size == 1) | ||
| 25 | return out; | ||
| 26 | |||
| 27 | return boost::none; | ||
| 28 | } | ||
| 29 | |||
| 30 | std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const { | ||
| 31 | std::vector<u8> out(size); | ||
| 32 | size_t read_size = Read(out.data(), size, offset); | ||
| 33 | out.resize(read_size); | ||
| 34 | return out; | ||
| 35 | } | ||
| 36 | |||
| 37 | std::vector<u8> VfsFile::ReadAllBytes() const { | ||
| 38 | return ReadBytes(GetSize()); | ||
| 39 | } | ||
| 40 | |||
| 41 | bool VfsFile::WriteByte(u8 data, size_t offset) { | ||
| 42 | return Write(&data, 1, offset) == 1; | ||
| 43 | } | ||
| 44 | |||
| 45 | size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) { | ||
| 46 | return Write(data.data(), data.size(), offset); | ||
| 47 | } | ||
| 48 | |||
| 49 | std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& path) const { | ||
| 50 | auto vec = FileUtil::SplitPathComponents(path); | ||
| 51 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | ||
| 52 | vec.end()); | ||
| 53 | if (vec.empty()) | ||
| 54 | return nullptr; | ||
| 55 | if (vec.size() == 1) | ||
| 56 | return GetFile(vec[0]); | ||
| 57 | auto dir = GetSubdirectory(vec[0]); | ||
| 58 | for (size_t component = 1; component < vec.size() - 1; ++component) { | ||
| 59 | if (dir == nullptr) | ||
| 60 | return nullptr; | ||
| 61 | dir = dir->GetSubdirectory(vec[component]); | ||
| 62 | } | ||
| 63 | if (dir == nullptr) | ||
| 64 | return nullptr; | ||
| 65 | return dir->GetFile(vec.back()); | ||
| 66 | } | ||
| 67 | |||
| 68 | std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const std::string& path) const { | ||
| 69 | if (IsRoot()) | ||
| 70 | return GetFileRelative(path); | ||
| 71 | |||
| 72 | return GetParentDirectory()->GetFileAbsolute(path); | ||
| 73 | } | ||
| 74 | |||
| 75 | std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::string& path) const { | ||
| 76 | auto vec = FileUtil::SplitPathComponents(path); | ||
| 77 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | ||
| 78 | vec.end()); | ||
| 79 | if (vec.empty()) | ||
| 80 | // TODO(DarkLordZach): Return this directory if path is '/' or similar. Can't currently | ||
| 81 | // because of const-ness | ||
| 82 | return nullptr; | ||
| 83 | auto dir = GetSubdirectory(vec[0]); | ||
| 84 | for (size_t component = 1; component < vec.size(); ++component) { | ||
| 85 | if (dir == nullptr) | ||
| 86 | return nullptr; | ||
| 87 | dir = dir->GetSubdirectory(vec[component]); | ||
| 88 | } | ||
| 89 | return dir; | ||
| 90 | } | ||
| 91 | |||
| 92 | std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(const std::string& path) const { | ||
| 93 | if (IsRoot()) | ||
| 94 | return GetDirectoryRelative(path); | ||
| 95 | |||
| 96 | return GetParentDirectory()->GetDirectoryAbsolute(path); | ||
| 97 | } | ||
| 98 | |||
| 99 | std::shared_ptr<VfsFile> VfsDirectory::GetFile(const std::string& name) const { | ||
| 100 | const auto& files = GetFiles(); | ||
| 101 | const auto iter = std::find_if(files.begin(), files.end(), | ||
| 102 | [&name](const auto& file1) { return name == file1->GetName(); }); | ||
| 103 | return iter == files.end() ? nullptr : *iter; | ||
| 104 | } | ||
| 105 | |||
| 106 | std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(const std::string& name) const { | ||
| 107 | const auto& subs = GetSubdirectories(); | ||
| 108 | const auto iter = std::find_if(subs.begin(), subs.end(), | ||
| 109 | [&name](const auto& file1) { return name == file1->GetName(); }); | ||
| 110 | return iter == subs.end() ? nullptr : *iter; | ||
| 111 | } | ||
| 112 | |||
| 113 | bool VfsDirectory::IsRoot() const { | ||
| 114 | return GetParentDirectory() == nullptr; | ||
| 115 | } | ||
| 116 | |||
| 117 | size_t VfsDirectory::GetSize() const { | ||
| 118 | const auto& files = GetFiles(); | ||
| 119 | const auto file_total = | ||
| 120 | std::accumulate(files.begin(), files.end(), 0ull, | ||
| 121 | [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); | ||
| 122 | |||
| 123 | const auto& sub_dir = GetSubdirectories(); | ||
| 124 | const auto subdir_total = | ||
| 125 | std::accumulate(sub_dir.begin(), sub_dir.end(), 0ull, | ||
| 126 | [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); | ||
| 127 | |||
| 128 | return file_total + subdir_total; | ||
| 129 | } | ||
| 130 | |||
| 131 | std::shared_ptr<VfsFile> VfsDirectory::CreateFileRelative(const std::string& path) { | ||
| 132 | auto vec = FileUtil::SplitPathComponents(path); | ||
| 133 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | ||
| 134 | vec.end()); | ||
| 135 | if (vec.empty()) | ||
| 136 | return nullptr; | ||
| 137 | if (vec.size() == 1) | ||
| 138 | return CreateFile(vec[0]); | ||
| 139 | auto dir = GetSubdirectory(vec[0]); | ||
| 140 | if (dir == nullptr) { | ||
| 141 | dir = CreateSubdirectory(vec[0]); | ||
| 142 | if (dir == nullptr) | ||
| 143 | return nullptr; | ||
| 144 | } | ||
| 145 | |||
| 146 | return dir->CreateFileRelative(FileUtil::GetPathWithoutTop(path)); | ||
| 147 | } | ||
| 148 | |||
| 149 | std::shared_ptr<VfsFile> VfsDirectory::CreateFileAbsolute(const std::string& path) { | ||
| 150 | if (IsRoot()) | ||
| 151 | return CreateFileRelative(path); | ||
| 152 | return GetParentDirectory()->CreateFileAbsolute(path); | ||
| 153 | } | ||
| 154 | |||
| 155 | std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryRelative(const std::string& path) { | ||
| 156 | auto vec = FileUtil::SplitPathComponents(path); | ||
| 157 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | ||
| 158 | vec.end()); | ||
| 159 | if (vec.empty()) | ||
| 160 | return nullptr; | ||
| 161 | if (vec.size() == 1) | ||
| 162 | return CreateSubdirectory(vec[0]); | ||
| 163 | auto dir = GetSubdirectory(vec[0]); | ||
| 164 | if (dir == nullptr) { | ||
| 165 | dir = CreateSubdirectory(vec[0]); | ||
| 166 | if (dir == nullptr) | ||
| 167 | return nullptr; | ||
| 168 | } | ||
| 169 | return dir->CreateDirectoryRelative(FileUtil::GetPathWithoutTop(path)); | ||
| 170 | } | ||
| 171 | |||
| 172 | std::shared_ptr<VfsDirectory> VfsDirectory::CreateDirectoryAbsolute(const std::string& path) { | ||
| 173 | if (IsRoot()) | ||
| 174 | return CreateDirectoryRelative(path); | ||
| 175 | return GetParentDirectory()->CreateDirectoryAbsolute(path); | ||
| 176 | } | ||
| 177 | |||
| 178 | bool VfsDirectory::DeleteSubdirectoryRecursive(const std::string& name) { | ||
| 179 | auto dir = GetSubdirectory(name); | ||
| 180 | if (dir == nullptr) | ||
| 181 | return false; | ||
| 182 | |||
| 183 | bool success = true; | ||
| 184 | for (const auto& file : dir->GetFiles()) { | ||
| 185 | if (!DeleteFile(file->GetName())) | ||
| 186 | success = false; | ||
| 187 | } | ||
| 188 | |||
| 189 | for (const auto& sdir : dir->GetSubdirectories()) { | ||
| 190 | if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) | ||
| 191 | success = false; | ||
| 192 | } | ||
| 193 | |||
| 194 | return success; | ||
| 195 | } | ||
| 196 | |||
| 197 | bool VfsDirectory::Copy(const std::string& src, const std::string& dest) { | ||
| 198 | const auto f1 = GetFile(src); | ||
| 199 | auto f2 = CreateFile(dest); | ||
| 200 | if (f1 == nullptr || f2 == nullptr) | ||
| 201 | return false; | ||
| 202 | |||
| 203 | if (!f2->Resize(f1->GetSize())) { | ||
| 204 | DeleteFile(dest); | ||
| 205 | return false; | ||
| 206 | } | ||
| 207 | |||
| 208 | return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); | ||
| 209 | } | ||
| 210 | |||
| 211 | bool ReadOnlyVfsDirectory::IsWritable() const { | ||
| 212 | return false; | ||
| 213 | } | ||
| 214 | |||
| 215 | bool ReadOnlyVfsDirectory::IsReadable() const { | ||
| 216 | return true; | ||
| 217 | } | ||
| 218 | |||
| 219 | std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(const std::string& name) { | ||
| 220 | return nullptr; | ||
| 221 | } | ||
| 222 | |||
| 223 | std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(const std::string& name) { | ||
| 224 | return nullptr; | ||
| 225 | } | ||
| 226 | |||
| 227 | bool ReadOnlyVfsDirectory::DeleteSubdirectory(const std::string& name) { | ||
| 228 | return false; | ||
| 229 | } | ||
| 230 | |||
| 231 | bool ReadOnlyVfsDirectory::DeleteFile(const std::string& name) { | ||
| 232 | return false; | ||
| 233 | } | ||
| 234 | |||
| 235 | bool ReadOnlyVfsDirectory::Rename(const std::string& name) { | ||
| 236 | return false; | ||
| 237 | } | ||
| 238 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h new file mode 100644 index 000000000..a5213e0cc --- /dev/null +++ b/src/core/file_sys/vfs.h | |||
| @@ -0,0 +1,237 @@ | |||
| 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 | // Creates a new file at the path relative to this directory. Also creates directories if | ||
| 177 | // they do not exist and is supported by this implementation. Returns nullptr on any failure. | ||
| 178 | virtual std::shared_ptr<VfsFile> CreateFileRelative(const std::string& path); | ||
| 179 | |||
| 180 | // Creates a new file at the path relative to root of this directory. Also creates directories | ||
| 181 | // if they do not exist and is supported by this implementation. Returns nullptr on any failure. | ||
| 182 | virtual std::shared_ptr<VfsFile> CreateFileAbsolute(const std::string& path); | ||
| 183 | |||
| 184 | // Creates a new directory at the path relative to this directory. Also creates directories if | ||
| 185 | // they do not exist and is supported by this implementation. Returns nullptr on any failure. | ||
| 186 | virtual std::shared_ptr<VfsDirectory> CreateDirectoryRelative(const std::string& path); | ||
| 187 | |||
| 188 | // Creates a new directory at the path relative to root of this directory. Also creates | ||
| 189 | // directories if they do not exist and is supported by this implementation. Returns nullptr on | ||
| 190 | // any failure. | ||
| 191 | virtual std::shared_ptr<VfsDirectory> CreateDirectoryAbsolute(const std::string& path); | ||
| 192 | |||
| 193 | // Deletes the subdirectory with name and returns true on success. | ||
| 194 | virtual bool DeleteSubdirectory(const std::string& name) = 0; | ||
| 195 | // Deletes all subdirectories and files of subdirectory with name recirsively and then deletes | ||
| 196 | // the subdirectory. Returns true on success. | ||
| 197 | virtual bool DeleteSubdirectoryRecursive(const std::string& name); | ||
| 198 | // Returnes whether or not the file with name name was deleted successfully. | ||
| 199 | virtual bool DeleteFile(const std::string& name) = 0; | ||
| 200 | |||
| 201 | // Returns whether or not this directory was renamed to name. | ||
| 202 | virtual bool Rename(const std::string& name) = 0; | ||
| 203 | |||
| 204 | // Returns whether or not the file with name src was successfully copied to a new file with name | ||
| 205 | // dest. | ||
| 206 | virtual bool Copy(const std::string& src, const std::string& dest); | ||
| 207 | |||
| 208 | // Interprets the file with name file instead as a directory of type directory. | ||
| 209 | // The directory must have a constructor that takes a single argument of type | ||
| 210 | // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a | ||
| 211 | // subdirectory in one call. | ||
| 212 | template <typename Directory> | ||
| 213 | bool InterpretAsDirectory(const std::string& file) { | ||
| 214 | auto file_p = GetFile(file); | ||
| 215 | if (file_p == nullptr) | ||
| 216 | return false; | ||
| 217 | return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file_p)); | ||
| 218 | } | ||
| 219 | |||
| 220 | protected: | ||
| 221 | // Backend for InterpretAsDirectory. | ||
| 222 | // Removes all references to file and adds a reference to dir in the directory's implementation. | ||
| 223 | virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0; | ||
| 224 | }; | ||
| 225 | |||
| 226 | // A convenience partial-implementation of VfsDirectory that stubs out methods that should only work | ||
| 227 | // if writable. This is to avoid redundant empty methods everywhere. | ||
| 228 | struct ReadOnlyVfsDirectory : public VfsDirectory { | ||
| 229 | bool IsWritable() const override; | ||
| 230 | bool IsReadable() const override; | ||
| 231 | std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override; | ||
| 232 | std::shared_ptr<VfsFile> CreateFile(const std::string& name) override; | ||
| 233 | bool DeleteSubdirectory(const std::string& name) override; | ||
| 234 | bool DeleteFile(const std::string& name) override; | ||
| 235 | bool Rename(const std::string& name) override; | ||
| 236 | }; | ||
| 237 | } // 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..22c858e0d --- /dev/null +++ b/src/core/file_sys/vfs_real.cpp | |||
| @@ -0,0 +1,177 @@ | |||
| 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 | bool RealVfsFile::Close() { | ||
| 80 | return backing.Close(); | ||
| 81 | } | ||
| 82 | |||
| 83 | RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_) | ||
| 84 | : path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)), | ||
| 85 | path_components(FileUtil::SplitPathComponents(path)), | ||
| 86 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | ||
| 87 | perms(perms_) { | ||
| 88 | if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append)) | ||
| 89 | FileUtil::CreateDir(path); | ||
| 90 | unsigned size; | ||
| 91 | if (perms == Mode::Append) | ||
| 92 | return; | ||
| 93 | |||
| 94 | FileUtil::ForeachDirectoryEntry( | ||
| 95 | &size, path, | ||
| 96 | [this](unsigned* entries_out, const std::string& directory, const std::string& filename) { | ||
| 97 | std::string full_path = directory + DIR_SEP + filename; | ||
| 98 | if (FileUtil::IsDirectory(full_path)) | ||
| 99 | subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(full_path, perms)); | ||
| 100 | else | ||
| 101 | files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms)); | ||
| 102 | return true; | ||
| 103 | }); | ||
| 104 | } | ||
| 105 | |||
| 106 | std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const { | ||
| 107 | return std::vector<std::shared_ptr<VfsFile>>(files); | ||
| 108 | } | ||
| 109 | |||
| 110 | std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const { | ||
| 111 | return std::vector<std::shared_ptr<VfsDirectory>>(subdirectories); | ||
| 112 | } | ||
| 113 | |||
| 114 | bool RealVfsDirectory::IsWritable() const { | ||
| 115 | return perms == Mode::Write || perms == Mode::Append; | ||
| 116 | } | ||
| 117 | |||
| 118 | bool RealVfsDirectory::IsReadable() const { | ||
| 119 | return perms == Mode::Read || perms == Mode::Write; | ||
| 120 | } | ||
| 121 | |||
| 122 | std::string RealVfsDirectory::GetName() const { | ||
| 123 | return path_components.back(); | ||
| 124 | } | ||
| 125 | |||
| 126 | std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { | ||
| 127 | if (path_components.size() <= 1) | ||
| 128 | return nullptr; | ||
| 129 | |||
| 130 | return std::make_shared<RealVfsDirectory>(parent_path, perms); | ||
| 131 | } | ||
| 132 | |||
| 133 | std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(const std::string& name) { | ||
| 134 | if (!FileUtil::CreateDir(path + DIR_SEP + name)) | ||
| 135 | return nullptr; | ||
| 136 | subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path + DIR_SEP + name, perms)); | ||
| 137 | return subdirectories.back(); | ||
| 138 | } | ||
| 139 | |||
| 140 | std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(const std::string& name) { | ||
| 141 | if (!FileUtil::CreateEmptyFile(path + DIR_SEP + name)) | ||
| 142 | return nullptr; | ||
| 143 | files.emplace_back(std::make_shared<RealVfsFile>(path + DIR_SEP + name, perms)); | ||
| 144 | return files.back(); | ||
| 145 | } | ||
| 146 | |||
| 147 | bool RealVfsDirectory::DeleteSubdirectory(const std::string& name) { | ||
| 148 | return FileUtil::DeleteDirRecursively(path + DIR_SEP + name); | ||
| 149 | } | ||
| 150 | |||
| 151 | bool RealVfsDirectory::DeleteFile(const std::string& name) { | ||
| 152 | auto file = GetFile(name); | ||
| 153 | if (file == nullptr) | ||
| 154 | return false; | ||
| 155 | files.erase(std::find(files.begin(), files.end(), file)); | ||
| 156 | auto real_file = std::static_pointer_cast<RealVfsFile>(file); | ||
| 157 | real_file->Close(); | ||
| 158 | return FileUtil::Delete(path + DIR_SEP + name); | ||
| 159 | } | ||
| 160 | |||
| 161 | bool RealVfsDirectory::Rename(const std::string& name) { | ||
| 162 | return FileUtil::Rename(path, parent_path + DIR_SEP + name); | ||
| 163 | } | ||
| 164 | |||
| 165 | bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 166 | auto iter = std::find(files.begin(), files.end(), file); | ||
| 167 | if (iter == files.end()) | ||
| 168 | return false; | ||
| 169 | |||
| 170 | files[iter - files.begin()] = files.back(); | ||
| 171 | files.pop_back(); | ||
| 172 | |||
| 173 | subdirectories.emplace_back(dir); | ||
| 174 | |||
| 175 | return true; | ||
| 176 | } | ||
| 177 | } // 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..5b765a552 --- /dev/null +++ b/src/core/file_sys/vfs_real.h | |||
| @@ -0,0 +1,69 @@ | |||
| 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/mode.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 | friend struct RealVfsDirectory; | ||
| 16 | |||
| 17 | RealVfsFile(const std::string& name, Mode perms = Mode::Read); | ||
| 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 | bool Rename(const std::string& name) override; | ||
| 28 | |||
| 29 | private: | ||
| 30 | bool Close(); | ||
| 31 | |||
| 32 | FileUtil::IOFile backing; | ||
| 33 | std::string path; | ||
| 34 | std::string parent_path; | ||
| 35 | std::vector<std::string> path_components; | ||
| 36 | std::vector<std::string> parent_components; | ||
| 37 | Mode perms; | ||
| 38 | }; | ||
| 39 | |||
| 40 | // An implementation of VfsDirectory that represents a directory on the user's computer. | ||
| 41 | struct RealVfsDirectory : public VfsDirectory { | ||
| 42 | RealVfsDirectory(const std::string& path, Mode perms); | ||
| 43 | |||
| 44 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | ||
| 45 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | ||
| 46 | bool IsWritable() const override; | ||
| 47 | bool IsReadable() const override; | ||
| 48 | std::string GetName() const override; | ||
| 49 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | ||
| 50 | std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override; | ||
| 51 | std::shared_ptr<VfsFile> CreateFile(const std::string& name) override; | ||
| 52 | bool DeleteSubdirectory(const std::string& name) override; | ||
| 53 | bool DeleteFile(const std::string& name) override; | ||
| 54 | bool Rename(const std::string& name) override; | ||
| 55 | |||
| 56 | protected: | ||
| 57 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 58 | |||
| 59 | private: | ||
| 60 | std::string path; | ||
| 61 | std::string parent_path; | ||
| 62 | std::vector<std::string> path_components; | ||
| 63 | std::vector<std::string> parent_components; | ||
| 64 | Mode perms; | ||
| 65 | std::vector<std::shared_ptr<VfsFile>> files; | ||
| 66 | std::vector<std::shared_ptr<VfsDirectory>> subdirectories; | ||
| 67 | }; | ||
| 68 | |||
| 69 | } // namespace FileSys | ||
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp index 9a9746585..0b3c66428 100644 --- a/src/core/hle/kernel/thread.cpp +++ b/src/core/hle/kernel/thread.cpp | |||
| @@ -165,7 +165,7 @@ void Thread::CancelWakeupTimer() { | |||
| 165 | static boost::optional<s32> GetNextProcessorId(u64 mask) { | 165 | static boost::optional<s32> GetNextProcessorId(u64 mask) { |
| 166 | for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) { | 166 | for (s32 index = 0; index < Core::NUM_CPU_CORES; ++index) { |
| 167 | if (mask & (1ULL << index)) { | 167 | if (mask & (1ULL << index)) { |
| 168 | if (!Core::System().GetInstance().Scheduler(index)->GetCurrentThread()) { | 168 | if (!Core::System::GetInstance().Scheduler(index)->GetCurrentThread()) { |
| 169 | // Core is enabled and not running any threads, use this one | 169 | // Core is enabled and not running any threads, use this one |
| 170 | return index; | 170 | return index; |
| 171 | } | 171 | } |
| @@ -215,14 +215,14 @@ void Thread::ResumeFromWait() { | |||
| 215 | new_processor_id = processor_id; | 215 | new_processor_id = processor_id; |
| 216 | } | 216 | } |
| 217 | if (ideal_core != -1 && | 217 | if (ideal_core != -1 && |
| 218 | Core::System().GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { | 218 | Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { |
| 219 | new_processor_id = ideal_core; | 219 | new_processor_id = ideal_core; |
| 220 | } | 220 | } |
| 221 | 221 | ||
| 222 | ASSERT(*new_processor_id < 4); | 222 | ASSERT(*new_processor_id < 4); |
| 223 | 223 | ||
| 224 | // Add thread to new core's scheduler | 224 | // Add thread to new core's scheduler |
| 225 | auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id); | 225 | auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); |
| 226 | 226 | ||
| 227 | if (*new_processor_id != processor_id) { | 227 | if (*new_processor_id != processor_id) { |
| 228 | // Remove thread from previous core's scheduler | 228 | // Remove thread from previous core's scheduler |
| @@ -325,7 +325,7 @@ ResultVal<SharedPtr<Thread>> Thread::Create(std::string name, VAddr entry_point, | |||
| 325 | thread->name = std::move(name); | 325 | thread->name = std::move(name); |
| 326 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); | 326 | thread->callback_handle = wakeup_callback_handle_table.Create(thread).Unwrap(); |
| 327 | thread->owner_process = owner_process; | 327 | thread->owner_process = owner_process; |
| 328 | thread->scheduler = Core::System().GetInstance().Scheduler(processor_id); | 328 | thread->scheduler = Core::System::GetInstance().Scheduler(processor_id); |
| 329 | thread->scheduler->AddThread(thread, priority); | 329 | thread->scheduler->AddThread(thread, priority); |
| 330 | 330 | ||
| 331 | // Find the next available TLS index, and mark it as used | 331 | // Find the next available TLS index, and mark it as used |
| @@ -481,14 +481,14 @@ void Thread::ChangeCore(u32 core, u64 mask) { | |||
| 481 | new_processor_id = processor_id; | 481 | new_processor_id = processor_id; |
| 482 | } | 482 | } |
| 483 | if (ideal_core != -1 && | 483 | if (ideal_core != -1 && |
| 484 | Core::System().GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { | 484 | Core::System::GetInstance().Scheduler(ideal_core)->GetCurrentThread() == nullptr) { |
| 485 | new_processor_id = ideal_core; | 485 | new_processor_id = ideal_core; |
| 486 | } | 486 | } |
| 487 | 487 | ||
| 488 | ASSERT(*new_processor_id < 4); | 488 | ASSERT(*new_processor_id < 4); |
| 489 | 489 | ||
| 490 | // Add thread to new core's scheduler | 490 | // Add thread to new core's scheduler |
| 491 | auto& next_scheduler = Core::System().GetInstance().Scheduler(*new_processor_id); | 491 | auto& next_scheduler = Core::System::GetInstance().Scheduler(*new_processor_id); |
| 492 | 492 | ||
| 493 | if (*new_processor_id != processor_id) { | 493 | if (*new_processor_id != processor_id) { |
| 494 | // Remove thread from previous core's scheduler | 494 | // Remove thread from previous core's scheduler |
diff --git a/src/core/hle/kernel/vm_manager.cpp b/src/core/hle/kernel/vm_manager.cpp index 034dd490e..10b3ebaca 100644 --- a/src/core/hle/kernel/vm_manager.cpp +++ b/src/core/hle/kernel/vm_manager.cpp | |||
| @@ -16,30 +16,20 @@ | |||
| 16 | namespace Kernel { | 16 | namespace Kernel { |
| 17 | 17 | ||
| 18 | static const char* GetMemoryStateName(MemoryState state) { | 18 | static const char* GetMemoryStateName(MemoryState state) { |
| 19 | static const char* names[] = { | 19 | static constexpr const char* names[] = { |
| 20 | "Unmapped", | 20 | "Unmapped", "Io", |
| 21 | "Io", | 21 | "Normal", "CodeStatic", |
| 22 | "Normal", | 22 | "CodeMutable", "Heap", |
| 23 | "CodeStatic", | 23 | "Shared", "Unknown1", |
| 24 | "CodeMutable", | 24 | "ModuleCodeStatic", "ModuleCodeMutable", |
| 25 | "Heap", | 25 | "IpcBuffer0", "Mapped", |
| 26 | "Shared", | 26 | "ThreadLocal", "TransferMemoryIsolated", |
| 27 | "Unknown1" | 27 | "TransferMemory", "ProcessMemory", |
| 28 | "ModuleCodeStatic", | 28 | "Unknown2", "IpcBuffer1", |
| 29 | "ModuleCodeMutable", | 29 | "IpcBuffer3", "KernelStack", |
| 30 | "IpcBuffer0", | ||
| 31 | "Mapped", | ||
| 32 | "ThreadLocal", | ||
| 33 | "TransferMemoryIsolated", | ||
| 34 | "TransferMemory", | ||
| 35 | "ProcessMemory", | ||
| 36 | "Unknown2" | ||
| 37 | "IpcBuffer1", | ||
| 38 | "IpcBuffer3", | ||
| 39 | "KernelStack", | ||
| 40 | }; | 30 | }; |
| 41 | 31 | ||
| 42 | return names[(int)state]; | 32 | return names[static_cast<int>(state)]; |
| 43 | } | 33 | } |
| 44 | 34 | ||
| 45 | bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { | 35 | bool VirtualMemoryArea::CanBeMergedWith(const VirtualMemoryArea& next) const { |
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 0f0ab1e6a..97ef07bf9 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -5,7 +5,6 @@ | |||
| 5 | #include <cinttypes> | 5 | #include <cinttypes> |
| 6 | #include <stack> | 6 | #include <stack> |
| 7 | #include "core/core.h" | 7 | #include "core/core.h" |
| 8 | #include "core/file_sys/filesystem.h" | ||
| 9 | #include "core/hle/ipc_helpers.h" | 8 | #include "core/hle/ipc_helpers.h" |
| 10 | #include "core/hle/kernel/event.h" | 9 | #include "core/hle/kernel/event.h" |
| 11 | #include "core/hle/kernel/process.h" | 10 | #include "core/hle/kernel/process.h" |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 902256757..ec528ef40 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -2,17 +2,204 @@ | |||
| 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 | #pragma optimize("", off) |
| 6 | |||
| 7 | #include "common/assert.h" | ||
| 6 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 9 | #include "core/core.h" | ||
| 7 | #include "core/file_sys/errors.h" | 10 | #include "core/file_sys/errors.h" |
| 8 | #include "core/file_sys/filesystem.h" | ||
| 9 | #include "core/file_sys/savedata_factory.h" | 11 | #include "core/file_sys/savedata_factory.h" |
| 10 | #include "core/file_sys/sdmc_factory.h" | 12 | #include "core/file_sys/sdmc_factory.h" |
| 13 | #include "core/file_sys/vfs.h" | ||
| 14 | #include "core/file_sys/vfs_offset.h" | ||
| 15 | #include "core/file_sys/vfs_real.h" | ||
| 11 | #include "core/hle/service/filesystem/filesystem.h" | 16 | #include "core/hle/service/filesystem/filesystem.h" |
| 12 | #include "core/hle/service/filesystem/fsp_srv.h" | 17 | #include "core/hle/service/filesystem/fsp_srv.h" |
| 13 | 18 | ||
| 14 | namespace Service::FileSystem { | 19 | namespace Service::FileSystem { |
| 15 | 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 | // TODO(DarkLordZach): Find a better error code for this | ||
| 46 | return ResultCode(-1); | ||
| 47 | } | ||
| 48 | if (!file->Resize(size)) { | ||
| 49 | // TODO(DarkLordZach): Find a better error code for this | ||
| 50 | return ResultCode(-1); | ||
| 51 | } | ||
| 52 | return RESULT_SUCCESS; | ||
| 53 | } | ||
| 54 | |||
| 55 | ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const { | ||
| 56 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 57 | if (path == "/" || path == "\\") { | ||
| 58 | // TODO(DarkLordZach): Why do games call this and what should it do? Works as is but... | ||
| 59 | return RESULT_SUCCESS; | ||
| 60 | } | ||
| 61 | if (dir->GetFile(FileUtil::GetFilename(path)) == nullptr) | ||
| 62 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 63 | if (!backing->DeleteFile(FileUtil::GetFilename(path))) { | ||
| 64 | // TODO(DarkLordZach): Find a better error code for this | ||
| 65 | return ResultCode(-1); | ||
| 66 | } | ||
| 67 | return RESULT_SUCCESS; | ||
| 68 | } | ||
| 69 | |||
| 70 | ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const { | ||
| 71 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 72 | if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty()) | ||
| 73 | dir = backing; | ||
| 74 | auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path)); | ||
| 75 | if (new_dir == nullptr) { | ||
| 76 | // TODO(DarkLordZach): Find a better error code for this | ||
| 77 | return ResultCode(-1); | ||
| 78 | } | ||
| 79 | return RESULT_SUCCESS; | ||
| 80 | } | ||
| 81 | |||
| 82 | ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const { | ||
| 83 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 84 | if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) { | ||
| 85 | // TODO(DarkLordZach): Find a better error code for this | ||
| 86 | return ResultCode(-1); | ||
| 87 | } | ||
| 88 | return RESULT_SUCCESS; | ||
| 89 | } | ||
| 90 | |||
| 91 | ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const { | ||
| 92 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 93 | if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) { | ||
| 94 | // TODO(DarkLordZach): Find a better error code for this | ||
| 95 | return ResultCode(-1); | ||
| 96 | } | ||
| 97 | return RESULT_SUCCESS; | ||
| 98 | } | ||
| 99 | |||
| 100 | ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path, | ||
| 101 | const std::string& dest_path) const { | ||
| 102 | auto src = backing->GetFileRelative(src_path); | ||
| 103 | if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { | ||
| 104 | // Use more-optimized vfs implementation rename. | ||
| 105 | if (src == nullptr) | ||
| 106 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 107 | if (!src->Rename(FileUtil::GetFilename(dest_path))) { | ||
| 108 | // TODO(DarkLordZach): Find a better error code for this | ||
| 109 | return ResultCode(-1); | ||
| 110 | } | ||
| 111 | return RESULT_SUCCESS; | ||
| 112 | } | ||
| 113 | |||
| 114 | // Move by hand -- TODO(DarkLordZach): Optimize | ||
| 115 | auto c_res = CreateFile(dest_path, src->GetSize()); | ||
| 116 | if (c_res != RESULT_SUCCESS) | ||
| 117 | return c_res; | ||
| 118 | |||
| 119 | auto dest = backing->GetFileRelative(dest_path); | ||
| 120 | ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found."); | ||
| 121 | |||
| 122 | ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), | ||
| 123 | "Could not write all of the bytes but everything else has succeded."); | ||
| 124 | |||
| 125 | if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) { | ||
| 126 | // TODO(DarkLordZach): Find a better error code for this | ||
| 127 | return ResultCode(-1); | ||
| 128 | } | ||
| 129 | |||
| 130 | return RESULT_SUCCESS; | ||
| 131 | } | ||
| 132 | |||
| 133 | ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path, | ||
| 134 | const std::string& dest_path) const { | ||
| 135 | auto src = GetDirectoryRelativeWrapped(backing, src_path); | ||
| 136 | if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { | ||
| 137 | // Use more-optimized vfs implementation rename. | ||
| 138 | if (src == nullptr) | ||
| 139 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 140 | if (!src->Rename(FileUtil::GetFilename(dest_path))) { | ||
| 141 | // TODO(DarkLordZach): Find a better error code for this | ||
| 142 | return ResultCode(-1); | ||
| 143 | } | ||
| 144 | return RESULT_SUCCESS; | ||
| 145 | } | ||
| 146 | |||
| 147 | // TODO(DarkLordZach): Implement renaming across the tree (move). | ||
| 148 | ASSERT_MSG(false, | ||
| 149 | "Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs " | ||
| 150 | "don't match -- UNIMPLEMENTED", | ||
| 151 | src_path, dest_path); | ||
| 152 | |||
| 153 | // TODO(DarkLordZach): Find a better error code for this | ||
| 154 | return ResultCode(-1); | ||
| 155 | } | ||
| 156 | |||
| 157 | ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path, | ||
| 158 | FileSys::Mode mode) const { | ||
| 159 | auto npath = path; | ||
| 160 | while (npath.size() > 0 && (npath[0] == '/' || npath[0] == '\\')) | ||
| 161 | npath = npath.substr(1); | ||
| 162 | auto file = backing->GetFileRelative(npath); | ||
| 163 | if (file == nullptr) | ||
| 164 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 165 | |||
| 166 | if (mode == FileSys::Mode::Append) { | ||
| 167 | return MakeResult<FileSys::VirtualFile>( | ||
| 168 | std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize())); | ||
| 169 | } | ||
| 170 | |||
| 171 | return MakeResult<FileSys::VirtualFile>(file); | ||
| 172 | } | ||
| 173 | |||
| 174 | ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) { | ||
| 175 | auto dir = GetDirectoryRelativeWrapped(backing, path); | ||
| 176 | if (dir == nullptr) { | ||
| 177 | // TODO(DarkLordZach): Find a better error code for this | ||
| 178 | return ResultCode(-1); | ||
| 179 | } | ||
| 180 | return MakeResult(dir); | ||
| 181 | } | ||
| 182 | |||
| 183 | u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const { | ||
| 184 | if (backing->IsWritable()) | ||
| 185 | return EMULATED_SD_REPORTED_SIZE; | ||
| 186 | |||
| 187 | return 0; | ||
| 188 | } | ||
| 189 | |||
| 190 | ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( | ||
| 191 | const std::string& path) const { | ||
| 192 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 193 | if (dir == nullptr) | ||
| 194 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 195 | auto filename = FileUtil::GetFilename(path); | ||
| 196 | if (dir->GetFile(filename) != nullptr) | ||
| 197 | return MakeResult(FileSys::EntryType::File); | ||
| 198 | if (dir->GetSubdirectory(filename) != nullptr) | ||
| 199 | return MakeResult(FileSys::EntryType::Directory); | ||
| 200 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 201 | } | ||
| 202 | |||
| 16 | /** | 203 | /** |
| 17 | * Map of registered file systems, identified by type. Once an file system is registered here, it | 204 | * Map of registered file systems, identified by type. Once an file system is registered here, it |
| 18 | * is never removed until UnregisterFileSystems is called. | 205 | * is never removed until UnregisterFileSystems is called. |
| @@ -42,7 +229,7 @@ ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { | |||
| 42 | return RESULT_SUCCESS; | 229 | return RESULT_SUCCESS; |
| 43 | } | 230 | } |
| 44 | 231 | ||
| 45 | ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenRomFS(u64 title_id) { | 232 | ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id) { |
| 46 | LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}", title_id); | 233 | LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}", title_id); |
| 47 | 234 | ||
| 48 | if (romfs_factory == nullptr) { | 235 | if (romfs_factory == nullptr) { |
| @@ -53,19 +240,19 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenRomFS(u64 title_id) { | |||
| 53 | return romfs_factory->Open(title_id); | 240 | return romfs_factory->Open(title_id); |
| 54 | } | 241 | } |
| 55 | 242 | ||
| 56 | ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSaveData( | 243 | ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, |
| 57 | FileSys::SaveDataSpaceId space, FileSys::SaveDataDescriptor save_struct) { | 244 | FileSys::SaveDataDescriptor save_struct) { |
| 58 | LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", | 245 | LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", |
| 59 | static_cast<u8>(space), SaveStructDebugInfo(save_struct)); | 246 | static_cast<u8>(space), save_struct.DebugInfo()); |
| 60 | 247 | ||
| 61 | if (save_data_factory == nullptr) { | 248 | if (save_data_factory == nullptr) { |
| 62 | return ResultCode(ErrorModule::FS, FileSys::ErrCodes::SaveDataNotFound); | 249 | return ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound); |
| 63 | } | 250 | } |
| 64 | 251 | ||
| 65 | return save_data_factory->Open(space, save_struct); | 252 | return save_data_factory->Open(space, save_struct); |
| 66 | } | 253 | } |
| 67 | 254 | ||
| 68 | ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSDMC() { | 255 | ResultVal<FileSys::VirtualDir> OpenSDMC() { |
| 69 | LOG_TRACE(Service_FS, "Opening SDMC"); | 256 | LOG_TRACE(Service_FS, "Opening SDMC"); |
| 70 | 257 | ||
| 71 | if (sdmc_factory == nullptr) { | 258 | if (sdmc_factory == nullptr) { |
| @@ -80,8 +267,10 @@ void RegisterFileSystems() { | |||
| 80 | save_data_factory = nullptr; | 267 | save_data_factory = nullptr; |
| 81 | sdmc_factory = nullptr; | 268 | sdmc_factory = nullptr; |
| 82 | 269 | ||
| 83 | std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); | 270 | auto nand_directory = std::make_shared<FileSys::RealVfsDirectory>( |
| 84 | std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX); | 271 | FileUtil::GetUserPath(D_NAND_IDX), FileSys::Mode::Write); |
| 272 | auto sd_directory = std::make_shared<FileSys::RealVfsDirectory>( | ||
| 273 | FileUtil::GetUserPath(D_SDMC_IDX), FileSys::Mode::Write); | ||
| 85 | 274 | ||
| 86 | auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); | 275 | auto savedata = std::make_unique<FileSys::SaveDataFactory>(std::move(nand_directory)); |
| 87 | save_data_factory = std::move(savedata); | 276 | save_data_factory = std::move(savedata); |
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index 45272d326..d4483daa5 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h | |||
| @@ -6,15 +6,13 @@ | |||
| 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/directory.h" | ||
| 10 | #include "core/file_sys/mode.h" | ||
| 9 | #include "core/file_sys/romfs_factory.h" | 11 | #include "core/file_sys/romfs_factory.h" |
| 10 | #include "core/file_sys/savedata_factory.h" | 12 | #include "core/file_sys/savedata_factory.h" |
| 11 | #include "core/file_sys/sdmc_factory.h" | 13 | #include "core/file_sys/sdmc_factory.h" |
| 12 | #include "core/hle/result.h" | 14 | #include "core/hle/result.h" |
| 13 | 15 | ||
| 14 | namespace FileSys { | ||
| 15 | class FileSystemBackend; | ||
| 16 | } // namespace FileSys | ||
| 17 | |||
| 18 | namespace Service { | 16 | namespace Service { |
| 19 | 17 | ||
| 20 | namespace SM { | 18 | namespace SM { |
| @@ -29,11 +27,10 @@ ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); | |||
| 29 | 27 | ||
| 30 | // TODO(DarkLordZach): BIS Filesystem | 28 | // TODO(DarkLordZach): BIS Filesystem |
| 31 | // ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); | 29 | // ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); |
| 32 | 30 | ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id); | |
| 33 | ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenRomFS(u64 title_id); | 31 | ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, |
| 34 | ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSaveData( | 32 | FileSys::SaveDataDescriptor save_struct); |
| 35 | FileSys::SaveDataSpaceId space, FileSys::SaveDataDescriptor save_struct); | 33 | ResultVal<FileSys::VirtualDir> OpenSDMC(); |
| 36 | ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSDMC(); | ||
| 37 | 34 | ||
| 38 | // TODO(DarkLordZach): BIS Filesystem | 35 | // TODO(DarkLordZach): BIS Filesystem |
| 39 | // ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS(); | 36 | // ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenBIS(); |
| @@ -41,5 +38,100 @@ ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenSDMC(); | |||
| 41 | /// Registers all Filesystem services with the specified service manager. | 38 | /// Registers all Filesystem services with the specified service manager. |
| 42 | void InstallInterfaces(SM::ServiceManager& service_manager); | 39 | void InstallInterfaces(SM::ServiceManager& service_manager); |
| 43 | 40 | ||
| 41 | // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of | ||
| 42 | // pointers and booleans. This makes using a VfsDirectory with switch services much easier and | ||
| 43 | // avoids repetitive code. | ||
| 44 | class VfsDirectoryServiceWrapper { | ||
| 45 | public: | ||
| 46 | explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing); | ||
| 47 | |||
| 48 | /** | ||
| 49 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||
| 50 | */ | ||
| 51 | std::string GetName() const; | ||
| 52 | |||
| 53 | /** | ||
| 54 | * Create a file specified by its path | ||
| 55 | * @param path Path relative to the Archive | ||
| 56 | * @param size The size of the new file, filled with zeroes | ||
| 57 | * @return Result of the operation | ||
| 58 | */ | ||
| 59 | ResultCode CreateFile(const std::string& path, u64 size) const; | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Delete a file specified by its path | ||
| 63 | * @param path Path relative to the archive | ||
| 64 | * @return Result of the operation | ||
| 65 | */ | ||
| 66 | ResultCode DeleteFile(const std::string& path) const; | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Create a directory specified by its path | ||
| 70 | * @param path Path relative to the archive | ||
| 71 | * @return Result of the operation | ||
| 72 | */ | ||
| 73 | ResultCode CreateDirectory(const std::string& path) const; | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Delete a directory specified by its path | ||
| 77 | * @param path Path relative to the archive | ||
| 78 | * @return Result of the operation | ||
| 79 | */ | ||
| 80 | ResultCode DeleteDirectory(const std::string& path) const; | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Delete a directory specified by its path and anything under it | ||
| 84 | * @param path Path relative to the archive | ||
| 85 | * @return Result of the operation | ||
| 86 | */ | ||
| 87 | ResultCode DeleteDirectoryRecursively(const std::string& path) const; | ||
| 88 | |||
| 89 | /** | ||
| 90 | * Rename a File specified by its path | ||
| 91 | * @param src_path Source path relative to the archive | ||
| 92 | * @param dest_path Destination path relative to the archive | ||
| 93 | * @return Result of the operation | ||
| 94 | */ | ||
| 95 | ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const; | ||
| 96 | |||
| 97 | /** | ||
| 98 | * Rename a Directory specified by its path | ||
| 99 | * @param src_path Source path relative to the archive | ||
| 100 | * @param dest_path Destination path relative to the archive | ||
| 101 | * @return Result of the operation | ||
| 102 | */ | ||
| 103 | ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const; | ||
| 104 | |||
| 105 | /** | ||
| 106 | * Open a file specified by its path, using the specified mode | ||
| 107 | * @param path Path relative to the archive | ||
| 108 | * @param mode Mode to open the file with | ||
| 109 | * @return Opened file, or error code | ||
| 110 | */ | ||
| 111 | ResultVal<FileSys::VirtualFile> OpenFile(const std::string& path, FileSys::Mode mode) const; | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Open a directory specified by its path | ||
| 115 | * @param path Path relative to the archive | ||
| 116 | * @return Opened directory, or error code | ||
| 117 | */ | ||
| 118 | ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path); | ||
| 119 | |||
| 120 | /** | ||
| 121 | * Get the free space | ||
| 122 | * @return The number of free bytes in the archive | ||
| 123 | */ | ||
| 124 | u64 GetFreeSpaceSize() const; | ||
| 125 | |||
| 126 | /** | ||
| 127 | * Get the type of the specified path | ||
| 128 | * @return The type of the specified path or error code | ||
| 129 | */ | ||
| 130 | ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const; | ||
| 131 | |||
| 132 | private: | ||
| 133 | FileSys::VirtualDir backing; | ||
| 134 | }; | ||
| 135 | |||
| 44 | } // namespace FileSystem | 136 | } // namespace FileSystem |
| 45 | } // namespace Service | 137 | } // namespace Service |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 22d3e645d..1b003bd84 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -8,11 +8,7 @@ | |||
| 8 | #include "core/core.h" | 8 | #include "core/core.h" |
| 9 | #include "core/file_sys/directory.h" | 9 | #include "core/file_sys/directory.h" |
| 10 | #include "core/file_sys/errors.h" | 10 | #include "core/file_sys/errors.h" |
| 11 | #include "core/file_sys/filesystem.h" | ||
| 12 | #include "core/file_sys/storage.h" | ||
| 13 | #include "core/hle/ipc_helpers.h" | 11 | #include "core/hle/ipc_helpers.h" |
| 14 | #include "core/hle/kernel/client_port.h" | ||
| 15 | #include "core/hle/kernel/client_session.h" | ||
| 16 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 17 | #include "core/hle/service/filesystem/filesystem.h" | 13 | #include "core/hle/service/filesystem/filesystem.h" |
| 18 | #include "core/hle/service/filesystem/fsp_srv.h" | 14 | #include "core/hle/service/filesystem/fsp_srv.h" |
| @@ -25,13 +21,13 @@ enum class StorageId : u8 { | |||
| 25 | GameCard = 2, | 21 | GameCard = 2, |
| 26 | NandSystem = 3, | 22 | NandSystem = 3, |
| 27 | NandUser = 4, | 23 | NandUser = 4, |
| 28 | SdCard = 5 | 24 | SdCard = 5, |
| 29 | }; | 25 | }; |
| 30 | 26 | ||
| 31 | class IStorage final : public ServiceFramework<IStorage> { | 27 | class IStorage final : public ServiceFramework<IStorage> { |
| 32 | public: | 28 | public: |
| 33 | IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend) | 29 | IStorage(FileSys::VirtualFile backend_) |
| 34 | : ServiceFramework("IStorage"), backend(std::move(backend)) { | 30 | : ServiceFramework("IStorage"), backend(std::move(backend_)) { |
| 35 | static const FunctionInfo functions[] = { | 31 | static const FunctionInfo functions[] = { |
| 36 | {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, | 32 | {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, |
| 37 | {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"}, | 33 | {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"}, |
| @@ -40,7 +36,7 @@ public: | |||
| 40 | } | 36 | } |
| 41 | 37 | ||
| 42 | private: | 38 | private: |
| 43 | std::unique_ptr<FileSys::StorageBackend> backend; | 39 | FileSys::VirtualFile backend; |
| 44 | 40 | ||
| 45 | void Read(Kernel::HLERequestContext& ctx) { | 41 | void Read(Kernel::HLERequestContext& ctx) { |
| 46 | IPC::RequestParser rp{ctx}; | 42 | IPC::RequestParser rp{ctx}; |
| @@ -62,14 +58,7 @@ private: | |||
| 62 | } | 58 | } |
| 63 | 59 | ||
| 64 | // Read the data from the Storage backend | 60 | // Read the data from the Storage backend |
| 65 | std::vector<u8> output(length); | 61 | std::vector<u8> output = backend->ReadBytes(length, offset); |
| 66 | ResultVal<size_t> res = backend->Read(offset, length, output.data()); | ||
| 67 | if (res.Failed()) { | ||
| 68 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 69 | rb.Push(res.Code()); | ||
| 70 | return; | ||
| 71 | } | ||
| 72 | |||
| 73 | // Write the data to memory | 62 | // Write the data to memory |
| 74 | ctx.WriteBuffer(output); | 63 | ctx.WriteBuffer(output); |
| 75 | 64 | ||
| @@ -80,8 +69,8 @@ private: | |||
| 80 | 69 | ||
| 81 | class IFile final : public ServiceFramework<IFile> { | 70 | class IFile final : public ServiceFramework<IFile> { |
| 82 | public: | 71 | public: |
| 83 | explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend) | 72 | explicit IFile(FileSys::VirtualFile backend_) |
| 84 | : ServiceFramework("IFile"), backend(std::move(backend)) { | 73 | : ServiceFramework("IFile"), backend(std::move(backend_)) { |
| 85 | static const FunctionInfo functions[] = { | 74 | static const FunctionInfo functions[] = { |
| 86 | {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, | 75 | {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, |
| 87 | {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"}, | 76 | {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"}, |
| @@ -91,7 +80,7 @@ public: | |||
| 91 | } | 80 | } |
| 92 | 81 | ||
| 93 | private: | 82 | private: |
| 94 | std::unique_ptr<FileSys::StorageBackend> backend; | 83 | FileSys::VirtualFile backend; |
| 95 | 84 | ||
| 96 | void Read(Kernel::HLERequestContext& ctx) { | 85 | void Read(Kernel::HLERequestContext& ctx) { |
| 97 | IPC::RequestParser rp{ctx}; | 86 | IPC::RequestParser rp{ctx}; |
| @@ -114,20 +103,14 @@ private: | |||
| 114 | } | 103 | } |
| 115 | 104 | ||
| 116 | // Read the data from the Storage backend | 105 | // Read the data from the Storage backend |
| 117 | std::vector<u8> output(length); | 106 | std::vector<u8> output = backend->ReadBytes(length, offset); |
| 118 | ResultVal<size_t> res = backend->Read(offset, length, output.data()); | ||
| 119 | if (res.Failed()) { | ||
| 120 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 121 | rb.Push(res.Code()); | ||
| 122 | return; | ||
| 123 | } | ||
| 124 | 107 | ||
| 125 | // Write the data to memory | 108 | // Write the data to memory |
| 126 | ctx.WriteBuffer(output); | 109 | ctx.WriteBuffer(output); |
| 127 | 110 | ||
| 128 | IPC::ResponseBuilder rb{ctx, 4}; | 111 | IPC::ResponseBuilder rb{ctx, 4}; |
| 129 | rb.Push(RESULT_SUCCESS); | 112 | rb.Push(RESULT_SUCCESS); |
| 130 | rb.Push(static_cast<u64>(*res)); | 113 | rb.Push(static_cast<u64>(output.size())); |
| 131 | } | 114 | } |
| 132 | 115 | ||
| 133 | void Write(Kernel::HLERequestContext& ctx) { | 116 | void Write(Kernel::HLERequestContext& ctx) { |
| @@ -150,14 +133,21 @@ private: | |||
| 150 | return; | 133 | return; |
| 151 | } | 134 | } |
| 152 | 135 | ||
| 153 | // Write the data to the Storage backend | ||
| 154 | std::vector<u8> data = ctx.ReadBuffer(); | 136 | std::vector<u8> data = ctx.ReadBuffer(); |
| 155 | ResultVal<size_t> res = backend->Write(offset, length, true, data.data()); | 137 | std::vector<u8> actual_data(length); |
| 156 | if (res.Failed()) { | 138 | |
| 157 | IPC::ResponseBuilder rb{ctx, 2}; | 139 | ASSERT_MSG( |
| 158 | rb.Push(res.Code()); | 140 | data.size() <= length, |
| 159 | return; | 141 | "Attempting to write more data than requested (requested={:016X}, actual={:016X}).", |
| 160 | } | 142 | length, data.size()); |
| 143 | |||
| 144 | std::copy(data.begin(), data.end(), actual_data.begin()); | ||
| 145 | // Write the data to the Storage backend | ||
| 146 | auto written = backend->WriteBytes(data, offset); | ||
| 147 | |||
| 148 | ASSERT_MSG(written == length, | ||
| 149 | "Could not write all bytes to file (requested={:016X}, actual={:016X}).", length, | ||
| 150 | written); | ||
| 161 | 151 | ||
| 162 | IPC::ResponseBuilder rb{ctx, 2}; | 152 | IPC::ResponseBuilder rb{ctx, 2}; |
| 163 | rb.Push(RESULT_SUCCESS); | 153 | rb.Push(RESULT_SUCCESS); |
| @@ -165,7 +155,8 @@ private: | |||
| 165 | 155 | ||
| 166 | void Flush(Kernel::HLERequestContext& ctx) { | 156 | void Flush(Kernel::HLERequestContext& ctx) { |
| 167 | LOG_DEBUG(Service_FS, "called"); | 157 | LOG_DEBUG(Service_FS, "called"); |
| 168 | backend->Flush(); | 158 | |
| 159 | // Exists for SDK compatibiltity -- No need to flush file. | ||
| 169 | 160 | ||
| 170 | IPC::ResponseBuilder rb{ctx, 2}; | 161 | IPC::ResponseBuilder rb{ctx, 2}; |
| 171 | rb.Push(RESULT_SUCCESS); | 162 | rb.Push(RESULT_SUCCESS); |
| @@ -174,7 +165,7 @@ private: | |||
| 174 | void SetSize(Kernel::HLERequestContext& ctx) { | 165 | void SetSize(Kernel::HLERequestContext& ctx) { |
| 175 | IPC::RequestParser rp{ctx}; | 166 | IPC::RequestParser rp{ctx}; |
| 176 | const u64 size = rp.Pop<u64>(); | 167 | const u64 size = rp.Pop<u64>(); |
| 177 | backend->SetSize(size); | 168 | backend->Resize(size); |
| 178 | LOG_DEBUG(Service_FS, "called, size={}", size); | 169 | LOG_DEBUG(Service_FS, "called, size={}", size); |
| 179 | 170 | ||
| 180 | IPC::ResponseBuilder rb{ctx, 2}; | 171 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -191,19 +182,39 @@ private: | |||
| 191 | } | 182 | } |
| 192 | }; | 183 | }; |
| 193 | 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 | |||
| 194 | class IDirectory final : public ServiceFramework<IDirectory> { | 198 | class IDirectory final : public ServiceFramework<IDirectory> { |
| 195 | public: | 199 | public: |
| 196 | explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend) | 200 | explicit IDirectory(FileSys::VirtualDir backend_) |
| 197 | : ServiceFramework("IDirectory"), backend(std::move(backend)) { | 201 | : ServiceFramework("IDirectory"), backend(std::move(backend_)) { |
| 198 | static const FunctionInfo functions[] = { | 202 | static const FunctionInfo functions[] = { |
| 199 | {0, &IDirectory::Read, "Read"}, | 203 | {0, &IDirectory::Read, "Read"}, |
| 200 | {1, &IDirectory::GetEntryCount, "GetEntryCount"}, | 204 | {1, &IDirectory::GetEntryCount, "GetEntryCount"}, |
| 201 | }; | 205 | }; |
| 202 | RegisterHandlers(functions); | 206 | RegisterHandlers(functions); |
| 207 | |||
| 208 | // TODO(DarkLordZach): Verify that this is the correct behavior. | ||
| 209 | // Build entry index now to save time later. | ||
| 210 | BuildEntryIndex(entries, backend->GetFiles(), FileSys::File); | ||
| 211 | BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::Directory); | ||
| 203 | } | 212 | } |
| 204 | 213 | ||
| 205 | private: | 214 | private: |
| 206 | std::unique_ptr<FileSys::DirectoryBackend> backend; | 215 | FileSys::VirtualDir backend; |
| 216 | std::vector<FileSys::Entry> entries; | ||
| 217 | u64 next_entry_index = 0; | ||
| 207 | 218 | ||
| 208 | void Read(Kernel::HLERequestContext& ctx) { | 219 | void Read(Kernel::HLERequestContext& ctx) { |
| 209 | IPC::RequestParser rp{ctx}; | 220 | IPC::RequestParser rp{ctx}; |
| @@ -214,26 +225,31 @@ private: | |||
| 214 | // Calculate how many entries we can fit in the output buffer | 225 | // Calculate how many entries we can fit in the output buffer |
| 215 | u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); | 226 | u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); |
| 216 | 227 | ||
| 228 | // Cap at total number of entries. | ||
| 229 | u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index); | ||
| 230 | |||
| 217 | // Read the data from the Directory backend | 231 | // Read the data from the Directory backend |
| 218 | std::vector<FileSys::Entry> entries(count_entries); | 232 | std::vector<FileSys::Entry> entry_data(entries.begin() + next_entry_index, |
| 219 | u64 read_entries = backend->Read(count_entries, entries.data()); | 233 | entries.begin() + next_entry_index + actual_entries); |
| 234 | |||
| 235 | next_entry_index += actual_entries; | ||
| 220 | 236 | ||
| 221 | // Convert the data into a byte array | 237 | // Convert the data into a byte array |
| 222 | std::vector<u8> output(entries.size() * sizeof(FileSys::Entry)); | 238 | std::vector<u8> output(entry_data.size() * sizeof(FileSys::Entry)); |
| 223 | std::memcpy(output.data(), entries.data(), output.size()); | 239 | std::memcpy(output.data(), entry_data.data(), output.size()); |
| 224 | 240 | ||
| 225 | // Write the data to memory | 241 | // Write the data to memory |
| 226 | ctx.WriteBuffer(output); | 242 | ctx.WriteBuffer(output); |
| 227 | 243 | ||
| 228 | IPC::ResponseBuilder rb{ctx, 4}; | 244 | IPC::ResponseBuilder rb{ctx, 4}; |
| 229 | rb.Push(RESULT_SUCCESS); | 245 | rb.Push(RESULT_SUCCESS); |
| 230 | rb.Push(read_entries); | 246 | rb.Push(actual_entries); |
| 231 | } | 247 | } |
| 232 | 248 | ||
| 233 | void GetEntryCount(Kernel::HLERequestContext& ctx) { | 249 | void GetEntryCount(Kernel::HLERequestContext& ctx) { |
| 234 | LOG_DEBUG(Service_FS, "called"); | 250 | LOG_DEBUG(Service_FS, "called"); |
| 235 | 251 | ||
| 236 | u64 count = backend->GetEntryCount(); | 252 | u64 count = entries.size() - next_entry_index; |
| 237 | 253 | ||
| 238 | IPC::ResponseBuilder rb{ctx, 4}; | 254 | IPC::ResponseBuilder rb{ctx, 4}; |
| 239 | rb.Push(RESULT_SUCCESS); | 255 | rb.Push(RESULT_SUCCESS); |
| @@ -243,7 +259,7 @@ private: | |||
| 243 | 259 | ||
| 244 | class IFileSystem final : public ServiceFramework<IFileSystem> { | 260 | class IFileSystem final : public ServiceFramework<IFileSystem> { |
| 245 | public: | 261 | public: |
| 246 | explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend) | 262 | explicit IFileSystem(FileSys::VirtualDir backend) |
| 247 | : ServiceFramework("IFileSystem"), backend(std::move(backend)) { | 263 | : ServiceFramework("IFileSystem"), backend(std::move(backend)) { |
| 248 | static const FunctionInfo functions[] = { | 264 | static const FunctionInfo functions[] = { |
| 249 | {0, &IFileSystem::CreateFile, "CreateFile"}, | 265 | {0, &IFileSystem::CreateFile, "CreateFile"}, |
| @@ -278,7 +294,7 @@ public: | |||
| 278 | LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); | 294 | LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); |
| 279 | 295 | ||
| 280 | IPC::ResponseBuilder rb{ctx, 2}; | 296 | IPC::ResponseBuilder rb{ctx, 2}; |
| 281 | rb.Push(backend->CreateFile(name, size)); | 297 | rb.Push(backend.CreateFile(name, size)); |
| 282 | } | 298 | } |
| 283 | 299 | ||
| 284 | void DeleteFile(Kernel::HLERequestContext& ctx) { | 300 | void DeleteFile(Kernel::HLERequestContext& ctx) { |
| @@ -290,7 +306,7 @@ public: | |||
| 290 | LOG_DEBUG(Service_FS, "called file {}", name); | 306 | LOG_DEBUG(Service_FS, "called file {}", name); |
| 291 | 307 | ||
| 292 | IPC::ResponseBuilder rb{ctx, 2}; | 308 | IPC::ResponseBuilder rb{ctx, 2}; |
| 293 | rb.Push(backend->DeleteFile(name)); | 309 | rb.Push(backend.DeleteFile(name)); |
| 294 | } | 310 | } |
| 295 | 311 | ||
| 296 | void CreateDirectory(Kernel::HLERequestContext& ctx) { | 312 | void CreateDirectory(Kernel::HLERequestContext& ctx) { |
| @@ -302,7 +318,7 @@ public: | |||
| 302 | LOG_DEBUG(Service_FS, "called directory {}", name); | 318 | LOG_DEBUG(Service_FS, "called directory {}", name); |
| 303 | 319 | ||
| 304 | IPC::ResponseBuilder rb{ctx, 2}; | 320 | IPC::ResponseBuilder rb{ctx, 2}; |
| 305 | rb.Push(backend->CreateDirectory(name)); | 321 | rb.Push(backend.CreateDirectory(name)); |
| 306 | } | 322 | } |
| 307 | 323 | ||
| 308 | void RenameFile(Kernel::HLERequestContext& ctx) { | 324 | void RenameFile(Kernel::HLERequestContext& ctx) { |
| @@ -320,7 +336,7 @@ public: | |||
| 320 | LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); | 336 | LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); |
| 321 | 337 | ||
| 322 | IPC::ResponseBuilder rb{ctx, 2}; | 338 | IPC::ResponseBuilder rb{ctx, 2}; |
| 323 | rb.Push(backend->RenameFile(src_name, dst_name)); | 339 | rb.Push(backend.RenameFile(src_name, dst_name)); |
| 324 | } | 340 | } |
| 325 | 341 | ||
| 326 | void OpenFile(Kernel::HLERequestContext& ctx) { | 342 | void OpenFile(Kernel::HLERequestContext& ctx) { |
| @@ -333,14 +349,14 @@ public: | |||
| 333 | 349 | ||
| 334 | LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); | 350 | LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); |
| 335 | 351 | ||
| 336 | auto result = backend->OpenFile(name, mode); | 352 | auto result = backend.OpenFile(name, mode); |
| 337 | if (result.Failed()) { | 353 | if (result.Failed()) { |
| 338 | IPC::ResponseBuilder rb{ctx, 2}; | 354 | IPC::ResponseBuilder rb{ctx, 2}; |
| 339 | rb.Push(result.Code()); | 355 | rb.Push(result.Code()); |
| 340 | return; | 356 | return; |
| 341 | } | 357 | } |
| 342 | 358 | ||
| 343 | auto file = std::move(result.Unwrap()); | 359 | IFile file(result.Unwrap()); |
| 344 | 360 | ||
| 345 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 361 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 346 | rb.Push(RESULT_SUCCESS); | 362 | rb.Push(RESULT_SUCCESS); |
| @@ -358,14 +374,14 @@ public: | |||
| 358 | 374 | ||
| 359 | LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); | 375 | LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); |
| 360 | 376 | ||
| 361 | auto result = backend->OpenDirectory(name); | 377 | auto result = backend.OpenDirectory(name); |
| 362 | if (result.Failed()) { | 378 | if (result.Failed()) { |
| 363 | IPC::ResponseBuilder rb{ctx, 2}; | 379 | IPC::ResponseBuilder rb{ctx, 2}; |
| 364 | rb.Push(result.Code()); | 380 | rb.Push(result.Code()); |
| 365 | return; | 381 | return; |
| 366 | } | 382 | } |
| 367 | 383 | ||
| 368 | auto directory = std::move(result.Unwrap()); | 384 | IDirectory directory(result.Unwrap()); |
| 369 | 385 | ||
| 370 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 386 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 371 | rb.Push(RESULT_SUCCESS); | 387 | rb.Push(RESULT_SUCCESS); |
| @@ -380,7 +396,7 @@ public: | |||
| 380 | 396 | ||
| 381 | LOG_DEBUG(Service_FS, "called file {}", name); | 397 | LOG_DEBUG(Service_FS, "called file {}", name); |
| 382 | 398 | ||
| 383 | auto result = backend->GetEntryType(name); | 399 | auto result = backend.GetEntryType(name); |
| 384 | if (result.Failed()) { | 400 | if (result.Failed()) { |
| 385 | IPC::ResponseBuilder rb{ctx, 2}; | 401 | IPC::ResponseBuilder rb{ctx, 2}; |
| 386 | rb.Push(result.Code()); | 402 | rb.Push(result.Code()); |
| @@ -400,7 +416,7 @@ public: | |||
| 400 | } | 416 | } |
| 401 | 417 | ||
| 402 | private: | 418 | private: |
| 403 | std::unique_ptr<FileSys::FileSystemBackend> backend; | 419 | VfsDirectoryServiceWrapper backend; |
| 404 | }; | 420 | }; |
| 405 | 421 | ||
| 406 | FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | 422 | FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { |
| @@ -536,17 +552,19 @@ void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { | |||
| 536 | LOG_INFO(Service_FS, "called with unknown={:08X}", unk); | 552 | LOG_INFO(Service_FS, "called with unknown={:08X}", unk); |
| 537 | auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); | 553 | auto save_struct = rp.PopRaw<FileSys::SaveDataDescriptor>(); |
| 538 | 554 | ||
| 539 | auto filesystem = OpenSaveData(space_id, save_struct); | 555 | auto dir = OpenSaveData(space_id, save_struct); |
| 540 | 556 | ||
| 541 | if (filesystem.Failed()) { | 557 | if (dir.Failed()) { |
| 542 | IPC::ResponseBuilder rb{ctx, 2, 0, 0}; | 558 | IPC::ResponseBuilder rb{ctx, 2, 0, 0}; |
| 543 | rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::SaveDataNotFound)); | 559 | rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); |
| 544 | return; | 560 | return; |
| 545 | } | 561 | } |
| 546 | 562 | ||
| 563 | IFileSystem filesystem(std::move(dir.Unwrap())); | ||
| 564 | |||
| 547 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 565 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 548 | rb.Push(RESULT_SUCCESS); | 566 | rb.Push(RESULT_SUCCESS); |
| 549 | rb.PushIpcInterface<IFileSystem>(std::move(filesystem.Unwrap())); | 567 | rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); |
| 550 | } | 568 | } |
| 551 | 569 | ||
| 552 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | 570 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { |
| @@ -569,18 +587,11 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { | |||
| 569 | return; | 587 | return; |
| 570 | } | 588 | } |
| 571 | 589 | ||
| 572 | auto storage = romfs.Unwrap()->OpenFile({}, {}); | 590 | IStorage storage(std::move(romfs.Unwrap())); |
| 573 | |||
| 574 | if (storage.Failed()) { | ||
| 575 | LOG_CRITICAL(Service_FS, "no storage interface available!"); | ||
| 576 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 577 | rb.Push(storage.Code()); | ||
| 578 | return; | ||
| 579 | } | ||
| 580 | 591 | ||
| 581 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 592 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 582 | rb.Push(RESULT_SUCCESS); | 593 | rb.Push(RESULT_SUCCESS); |
| 583 | rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap())); | 594 | rb.PushIpcInterface<IStorage>(std::move(storage)); |
| 584 | } | 595 | } |
| 585 | 596 | ||
| 586 | void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { | 597 | void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { |
| @@ -591,33 +602,9 @@ void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { | |||
| 591 | 602 | ||
| 592 | LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}", | 603 | LOG_DEBUG(Service_FS, "called with storage_id={:02X}, title_id={:016X}", |
| 593 | static_cast<u8>(storage_id), title_id); | 604 | static_cast<u8>(storage_id), title_id); |
| 594 | if (title_id != Core::System::GetInstance().CurrentProcess()->program_id) { | ||
| 595 | LOG_CRITICAL( | ||
| 596 | Service_FS, | ||
| 597 | "Attempting to access RomFS of another title id (current={:016X}, requested={:016X}).", | ||
| 598 | Core::System::GetInstance().CurrentProcess()->program_id, title_id); | ||
| 599 | } | ||
| 600 | 605 | ||
| 601 | auto romfs = OpenRomFS(title_id); | 606 | IPC::ResponseBuilder rb{ctx, 2}; |
| 602 | if (romfs.Failed()) { | 607 | rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::TitleNotFound)); |
| 603 | LOG_CRITICAL(Service_FS, "no file system interface available!"); | ||
| 604 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 605 | rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::RomFSNotFound)); | ||
| 606 | return; | ||
| 607 | } | ||
| 608 | |||
| 609 | auto storage = romfs.Unwrap()->OpenFile({}, {}); | ||
| 610 | |||
| 611 | if (storage.Failed()) { | ||
| 612 | LOG_CRITICAL(Service_FS, "no storage interface available!"); | ||
| 613 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 614 | rb.Push(storage.Code()); | ||
| 615 | return; | ||
| 616 | } | ||
| 617 | |||
| 618 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | ||
| 619 | rb.Push(RESULT_SUCCESS); | ||
| 620 | rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap())); | ||
| 621 | } | 608 | } |
| 622 | 609 | ||
| 623 | } // namespace Service::FileSystem | 610 | } // namespace Service::FileSystem |
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index 4653eee4e..07f99c93d 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h | |||
| @@ -27,7 +27,7 @@ private: | |||
| 27 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); | 27 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); |
| 28 | void OpenRomStorage(Kernel::HLERequestContext& ctx); | 28 | void OpenRomStorage(Kernel::HLERequestContext& ctx); |
| 29 | 29 | ||
| 30 | std::unique_ptr<FileSys::FileSystemBackend> romfs; | 30 | FileSys::VirtualFile romfs; |
| 31 | }; | 31 | }; |
| 32 | 32 | ||
| 33 | } // namespace Service::FileSystem | 33 | } // namespace Service::FileSystem |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index 6b11830f1..4f18c0fd3 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | #include <atomic> | 5 | #include <atomic> |
| 6 | #include "common/logging/log.h" | 6 | #include "common/logging/log.h" |
| 7 | #include "core/core_timing.h" | 7 | #include "core/core_timing.h" |
| 8 | #include "core/frontend/emu_window.h" | ||
| 8 | #include "core/frontend/input.h" | 9 | #include "core/frontend/input.h" |
| 9 | #include "core/hle/ipc_helpers.h" | 10 | #include "core/hle/ipc_helpers.h" |
| 10 | #include "core/hle/kernel/client_port.h" | 11 | #include "core/hle/kernel/client_port.h" |
| @@ -63,7 +64,8 @@ private: | |||
| 63 | std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, | 64 | std::transform(Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN, |
| 64 | Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END, | 65 | Settings::values.analogs.begin() + Settings::NativeAnalog::STICK_HID_END, |
| 65 | sticks.begin(), Input::CreateDevice<Input::AnalogDevice>); | 66 | sticks.begin(), Input::CreateDevice<Input::AnalogDevice>); |
| 66 | // TODO(shinyquagsire23): gyro, touch, mouse, keyboard | 67 | touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touch_device); |
| 68 | // TODO(shinyquagsire23): gyro, mouse, keyboard | ||
| 67 | } | 69 | } |
| 68 | 70 | ||
| 69 | void UpdatePadCallback(u64 userdata, int cycles_late) { | 71 | void UpdatePadCallback(u64 userdata, int cycles_late) { |
| @@ -151,8 +153,6 @@ private: | |||
| 151 | } | 153 | } |
| 152 | } | 154 | } |
| 153 | 155 | ||
| 154 | // TODO(bunnei): Properly implement the touch screen, the below will just write empty data | ||
| 155 | |||
| 156 | TouchScreen& touchscreen = mem.touchscreen; | 156 | TouchScreen& touchscreen = mem.touchscreen; |
| 157 | const u64 last_entry = touchscreen.header.latest_entry; | 157 | const u64 last_entry = touchscreen.header.latest_entry; |
| 158 | const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size(); | 158 | const u64 curr_entry = (last_entry + 1) % touchscreen.entries.size(); |
| @@ -164,7 +164,26 @@ private: | |||
| 164 | touchscreen.header.max_entry_index = touchscreen.entries.size(); | 164 | touchscreen.header.max_entry_index = touchscreen.entries.size(); |
| 165 | touchscreen.header.timestamp = timestamp; | 165 | touchscreen.header.timestamp = timestamp; |
| 166 | touchscreen.entries[curr_entry].header.timestamp = sample_counter; | 166 | touchscreen.entries[curr_entry].header.timestamp = sample_counter; |
| 167 | touchscreen.entries[curr_entry].header.num_touches = 0; | 167 | |
| 168 | TouchScreenEntryTouch touch_entry{}; | ||
| 169 | auto [x, y, pressed] = touch_device->GetStatus(); | ||
| 170 | touch_entry.timestamp = timestamp; | ||
| 171 | touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width); | ||
| 172 | touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height); | ||
| 173 | touch_entry.touch_index = 0; | ||
| 174 | |||
| 175 | // TODO(DarkLordZach): Maybe try to derive these from EmuWindow? | ||
| 176 | touch_entry.diameter_x = 15; | ||
| 177 | touch_entry.diameter_y = 15; | ||
| 178 | touch_entry.angle = 0; | ||
| 179 | |||
| 180 | // TODO(DarkLordZach): Implement multi-touch support | ||
| 181 | if (pressed) { | ||
| 182 | touchscreen.entries[curr_entry].header.num_touches = 1; | ||
| 183 | touchscreen.entries[curr_entry].touches[0] = touch_entry; | ||
| 184 | } else { | ||
| 185 | touchscreen.entries[curr_entry].header.num_touches = 0; | ||
| 186 | } | ||
| 168 | 187 | ||
| 169 | // TODO(shinyquagsire23): Properly implement mouse | 188 | // TODO(shinyquagsire23): Properly implement mouse |
| 170 | Mouse& mouse = mem.mouse; | 189 | Mouse& mouse = mem.mouse; |
| @@ -250,6 +269,7 @@ private: | |||
| 250 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> | 269 | std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID> |
| 251 | buttons; | 270 | buttons; |
| 252 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks; | 271 | std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID> sticks; |
| 272 | std::unique_ptr<Input::TouchDevice> touch_device; | ||
| 253 | }; | 273 | }; |
| 254 | 274 | ||
| 255 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { | 275 | class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> { |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 0b11bf4f3..19b8667ba 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/gdbstub/gdbstub.h" | 11 | #include "core/gdbstub/gdbstub.h" |
| 13 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 14 | #include "core/hle/kernel/resource_limit.h" | 13 | #include "core/hle/kernel/resource_limit.h" |
| @@ -47,55 +46,11 @@ static std::string FindRomFS(const std::string& directory) { | |||
| 47 | return filepath_romfs; | 46 | return filepath_romfs; |
| 48 | } | 47 | } |
| 49 | 48 | ||
| 50 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, | 49 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file) |
| 51 | std::string filepath) | 50 | : AppLoader(std::move(file)) {} |
| 52 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 53 | |||
| 54 | FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& file, | ||
| 55 | const std::string& filepath) { | ||
| 56 | bool is_main_found{}; | ||
| 57 | bool is_npdm_found{}; | ||
| 58 | bool is_rtld_found{}; | ||
| 59 | bool is_sdk_found{}; | ||
| 60 | |||
| 61 | const auto callback = [&](unsigned* num_entries_out, const std::string& directory, | ||
| 62 | const std::string& virtual_name) -> bool { | ||
| 63 | // Skip directories | ||
| 64 | std::string physical_name = directory + virtual_name; | ||
| 65 | if (FileUtil::IsDirectory(physical_name)) { | ||
| 66 | return true; | ||
| 67 | } | ||
| 68 | |||
| 69 | // Verify filename | ||
| 70 | if (Common::ToLower(virtual_name) == "main") { | ||
| 71 | is_main_found = true; | ||
| 72 | } else if (Common::ToLower(virtual_name) == "main.npdm") { | ||
| 73 | is_npdm_found = true; | ||
| 74 | return true; | ||
| 75 | } else if (Common::ToLower(virtual_name) == "rtld") { | ||
| 76 | is_rtld_found = true; | ||
| 77 | } else if (Common::ToLower(virtual_name) == "sdk") { | ||
| 78 | is_sdk_found = true; | ||
| 79 | } else { | ||
| 80 | // Continue searching | ||
| 81 | return true; | ||
| 82 | } | ||
| 83 | |||
| 84 | // Verify file is an NSO | ||
| 85 | FileUtil::IOFile file(physical_name, "rb"); | ||
| 86 | if (AppLoader_NSO::IdentifyType(file, physical_name) != FileType::NSO) { | ||
| 87 | return false; | ||
| 88 | } | ||
| 89 | |||
| 90 | // We are done if we've found and verified all required NSOs | ||
| 91 | return !(is_main_found && is_npdm_found && is_rtld_found && is_sdk_found); | ||
| 92 | }; | ||
| 93 | 51 | ||
| 94 | // Search the directory recursively, looking for the required modules | 52 | FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) { |
| 95 | const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; | 53 | if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) { |
| 96 | FileUtil::ForeachDirectoryEntry(nullptr, directory, callback); | ||
| 97 | |||
| 98 | if (is_main_found && is_npdm_found && is_rtld_found && is_sdk_found) { | ||
| 99 | return FileType::DeconstructedRomDirectory; | 54 | return FileType::DeconstructedRomDirectory; |
| 100 | } | 55 | } |
| 101 | 56 | ||
| @@ -107,14 +62,13 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 107 | if (is_loaded) { | 62 | if (is_loaded) { |
| 108 | return ResultStatus::ErrorAlreadyLoaded; | 63 | return ResultStatus::ErrorAlreadyLoaded; |
| 109 | } | 64 | } |
| 110 | if (!file.IsOpen()) { | ||
| 111 | return ResultStatus::Error; | ||
| 112 | } | ||
| 113 | 65 | ||
| 114 | const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; | 66 | const FileSys::VirtualDir dir = file->GetContainingDirectory(); |
| 115 | const std::string npdm_path = directory + DIR_SEP + "main.npdm"; | 67 | const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); |
| 68 | if (npdm == nullptr) | ||
| 69 | return ResultStatus::ErrorInvalidFormat; | ||
| 116 | 70 | ||
| 117 | ResultStatus result = metadata.Load(npdm_path); | 71 | ResultStatus result = metadata.Load(npdm); |
| 118 | if (result != ResultStatus::Success) { | 72 | if (result != ResultStatus::Success) { |
| 119 | return result; | 73 | return result; |
| 120 | } | 74 | } |
| @@ -129,9 +83,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 129 | VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; | 83 | VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; |
| 130 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", | 84 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", |
| 131 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { | 85 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { |
| 132 | const std::string path = directory + DIR_SEP + module; | ||
| 133 | const VAddr load_addr = next_load_addr; | 86 | const VAddr load_addr = next_load_addr; |
| 134 | next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); | 87 | const FileSys::VirtualFile module_file = dir->GetFile(module); |
| 88 | if (module_file != nullptr) | ||
| 89 | next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr); | ||
| 135 | if (next_load_addr) { | 90 | if (next_load_addr) { |
| 136 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); | 91 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); |
| 137 | // Register module with GDBStub | 92 | // Register module with GDBStub |
| @@ -150,10 +105,15 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 150 | metadata.GetMainThreadStackSize()); | 105 | metadata.GetMainThreadStackSize()); |
| 151 | 106 | ||
| 152 | // Find the RomFS by searching for a ".romfs" file in this directory | 107 | // Find the RomFS by searching for a ".romfs" file in this directory |
| 153 | filepath_romfs = FindRomFS(directory); | 108 | const auto& files = dir->GetFiles(); |
| 109 | const auto romfs_iter = | ||
| 110 | std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { | ||
| 111 | return file->GetName().find(".romfs") != std::string::npos; | ||
| 112 | }); | ||
| 154 | 113 | ||
| 155 | // Register the RomFS if a ".romfs" file was found | 114 | // Register the RomFS if a ".romfs" file was found |
| 156 | if (!filepath_romfs.empty()) { | 115 | if (romfs_iter != files.end() && *romfs_iter != nullptr) { |
| 116 | romfs = *romfs_iter; | ||
| 157 | Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); | 117 | Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); |
| 158 | } | 118 | } |
| 159 | 119 | ||
| @@ -161,29 +121,10 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 161 | return ResultStatus::Success; | 121 | return ResultStatus::Success; |
| 162 | } | 122 | } |
| 163 | 123 | ||
| 164 | ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS( | 124 | ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS(FileSys::VirtualFile& dir) { |
| 165 | std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { | 125 | if (romfs == nullptr) |
| 166 | |||
| 167 | if (filepath_romfs.empty()) { | ||
| 168 | LOG_DEBUG(Loader, "No RomFS available"); | ||
| 169 | return ResultStatus::ErrorNotUsed; | 126 | return ResultStatus::ErrorNotUsed; |
| 170 | } | 127 | dir = romfs; |
| 171 | |||
| 172 | // We reopen the file, to allow its position to be independent | ||
| 173 | romfs_file = std::make_shared<FileUtil::IOFile>(filepath_romfs, "rb"); | ||
| 174 | if (!romfs_file->IsOpen()) { | ||
| 175 | return ResultStatus::Error; | ||
| 176 | } | ||
| 177 | |||
| 178 | offset = 0; | ||
| 179 | size = romfs_file->GetSize(); | ||
| 180 | |||
| 181 | LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset); | ||
| 182 | LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size); | ||
| 183 | |||
| 184 | // Reset read pointer | ||
| 185 | file.Seek(0, SEEK_SET); | ||
| 186 | |||
| 187 | return ResultStatus::Success; | 128 | return ResultStatus::Success; |
| 188 | } | 129 | } |
| 189 | 130 | ||
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 23295d911..982a037f7 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h | |||
| @@ -20,29 +20,26 @@ 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, | 38 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 40 | u64& size) override; | ||
| 41 | 39 | ||
| 42 | private: | 40 | private: |
| 43 | std::string filepath_romfs; | ||
| 44 | std::string filepath; | ||
| 45 | FileSys::ProgramMetadata metadata; | 41 | FileSys::ProgramMetadata metadata; |
| 42 | FileSys::VirtualFile romfs; | ||
| 46 | }; | 43 | }; |
| 47 | 44 | ||
| 48 | } // namespace Loader | 45 | } // namespace Loader |
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 b463f369c..e73b253b2 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp | |||
| @@ -4,14 +4,13 @@ | |||
| 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/string_util.h" | 9 | #include "common/string_util.h" |
| 11 | #include "common/swap.h" | 10 | #include "common/swap.h" |
| 12 | #include "core/core.h" | 11 | #include "core/core.h" |
| 12 | #include "core/file_sys/content_archive.h" | ||
| 13 | #include "core/file_sys/program_metadata.h" | 13 | #include "core/file_sys/program_metadata.h" |
| 14 | #include "core/file_sys/romfs_factory.h" | ||
| 15 | #include "core/gdbstub/gdbstub.h" | 14 | #include "core/gdbstub/gdbstub.h" |
| 16 | #include "core/hle/kernel/process.h" | 15 | #include "core/hle/kernel/process.h" |
| 17 | #include "core/hle/kernel/resource_limit.h" | 16 | #include "core/hle/kernel/resource_limit.h" |
| @@ -22,208 +21,15 @@ | |||
| 22 | 21 | ||
| 23 | namespace Loader { | 22 | namespace Loader { |
| 24 | 23 | ||
| 25 | // Media offsets in headers are stored divided by 512. Mult. by this to get real offset. | 24 | AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(file) {} |
| 26 | constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200; | ||
| 27 | |||
| 28 | constexpr u64 SECTION_HEADER_SIZE = 0x200; | ||
| 29 | constexpr u64 SECTION_HEADER_OFFSET = 0x400; | ||
| 30 | |||
| 31 | enum class NcaContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 }; | ||
| 32 | |||
| 33 | enum class NcaSectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 }; | ||
| 34 | |||
| 35 | struct NcaSectionTableEntry { | ||
| 36 | u32_le media_offset; | ||
| 37 | u32_le media_end_offset; | ||
| 38 | INSERT_PADDING_BYTES(0x8); | ||
| 39 | }; | ||
| 40 | static_assert(sizeof(NcaSectionTableEntry) == 0x10, "NcaSectionTableEntry has incorrect size."); | ||
| 41 | |||
| 42 | struct NcaHeader { | ||
| 43 | std::array<u8, 0x100> rsa_signature_1; | ||
| 44 | std::array<u8, 0x100> rsa_signature_2; | ||
| 45 | u32_le magic; | ||
| 46 | u8 is_system; | ||
| 47 | NcaContentType content_type; | ||
| 48 | u8 crypto_type; | ||
| 49 | u8 key_index; | ||
| 50 | u64_le size; | ||
| 51 | u64_le title_id; | ||
| 52 | INSERT_PADDING_BYTES(0x4); | ||
| 53 | u32_le sdk_version; | ||
| 54 | u8 crypto_type_2; | ||
| 55 | INSERT_PADDING_BYTES(15); | ||
| 56 | std::array<u8, 0x10> rights_id; | ||
| 57 | std::array<NcaSectionTableEntry, 0x4> section_tables; | ||
| 58 | std::array<std::array<u8, 0x20>, 0x4> hash_tables; | ||
| 59 | std::array<std::array<u8, 0x10>, 0x4> key_area; | ||
| 60 | INSERT_PADDING_BYTES(0xC0); | ||
| 61 | }; | ||
| 62 | static_assert(sizeof(NcaHeader) == 0x400, "NcaHeader has incorrect size."); | ||
| 63 | |||
| 64 | struct NcaSectionHeaderBlock { | ||
| 65 | INSERT_PADDING_BYTES(3); | ||
| 66 | NcaSectionFilesystemType filesystem_type; | ||
| 67 | u8 crypto_type; | ||
| 68 | INSERT_PADDING_BYTES(3); | ||
| 69 | }; | ||
| 70 | static_assert(sizeof(NcaSectionHeaderBlock) == 0x8, "NcaSectionHeaderBlock has incorrect size."); | ||
| 71 | |||
| 72 | struct Pfs0Superblock { | ||
| 73 | NcaSectionHeaderBlock header_block; | ||
| 74 | std::array<u8, 0x20> hash; | ||
| 75 | u32_le size; | ||
| 76 | INSERT_PADDING_BYTES(4); | ||
| 77 | u64_le hash_table_offset; | ||
| 78 | u64_le hash_table_size; | ||
| 79 | u64_le pfs0_header_offset; | ||
| 80 | u64_le pfs0_size; | ||
| 81 | INSERT_PADDING_BYTES(432); | ||
| 82 | }; | ||
| 83 | static_assert(sizeof(Pfs0Superblock) == 0x200, "Pfs0Superblock has incorrect size."); | ||
| 84 | |||
| 85 | static bool IsValidNca(const NcaHeader& header) { | ||
| 86 | return header.magic == Common::MakeMagic('N', 'C', 'A', '2') || | ||
| 87 | header.magic == Common::MakeMagic('N', 'C', 'A', '3'); | ||
| 88 | } | ||
| 89 | |||
| 90 | // TODO(DarkLordZach): Add support for encrypted. | ||
| 91 | class Nca final { | ||
| 92 | std::vector<FileSys::PartitionFilesystem> pfs; | ||
| 93 | std::vector<u64> pfs_offset; | ||
| 94 | |||
| 95 | u64 romfs_offset = 0; | ||
| 96 | u64 romfs_size = 0; | ||
| 97 | |||
| 98 | boost::optional<u8> exefs_id = boost::none; | ||
| 99 | |||
| 100 | FileUtil::IOFile file; | ||
| 101 | std::string path; | ||
| 102 | |||
| 103 | u64 GetExeFsFileOffset(const std::string& file_name) const; | ||
| 104 | u64 GetExeFsFileSize(const std::string& file_name) const; | ||
| 105 | |||
| 106 | public: | ||
| 107 | ResultStatus Load(FileUtil::IOFile&& file, std::string path); | ||
| 108 | |||
| 109 | FileSys::PartitionFilesystem GetPfs(u8 id) const; | ||
| 110 | |||
| 111 | u64 GetRomFsOffset() const; | ||
| 112 | u64 GetRomFsSize() const; | ||
| 113 | |||
| 114 | std::vector<u8> GetExeFsFile(const std::string& file_name); | ||
| 115 | }; | ||
| 116 | |||
| 117 | static bool IsPfsExeFs(const FileSys::PartitionFilesystem& pfs) { | ||
| 118 | // According to switchbrew, an exefs must only contain these two files: | ||
| 119 | return pfs.GetFileSize("main") > 0 && pfs.GetFileSize("main.npdm") > 0; | ||
| 120 | } | ||
| 121 | |||
| 122 | ResultStatus Nca::Load(FileUtil::IOFile&& in_file, std::string in_path) { | ||
| 123 | file = std::move(in_file); | ||
| 124 | path = in_path; | ||
| 125 | file.Seek(0, SEEK_SET); | ||
| 126 | std::array<u8, sizeof(NcaHeader)> header_array{}; | ||
| 127 | if (sizeof(NcaHeader) != file.ReadBytes(header_array.data(), sizeof(NcaHeader))) | ||
| 128 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 129 | |||
| 130 | NcaHeader header{}; | ||
| 131 | std::memcpy(&header, header_array.data(), sizeof(NcaHeader)); | ||
| 132 | if (!IsValidNca(header)) | ||
| 133 | return ResultStatus::ErrorInvalidFormat; | ||
| 134 | |||
| 135 | int number_sections = | ||
| 136 | std::count_if(std::begin(header.section_tables), std::end(header.section_tables), | ||
| 137 | [](NcaSectionTableEntry entry) { return entry.media_offset > 0; }); | ||
| 138 | |||
| 139 | for (int i = 0; i < number_sections; ++i) { | ||
| 140 | // Seek to beginning of this section. | ||
| 141 | file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET); | ||
| 142 | std::array<u8, sizeof(NcaSectionHeaderBlock)> array{}; | ||
| 143 | if (sizeof(NcaSectionHeaderBlock) != | ||
| 144 | file.ReadBytes(array.data(), sizeof(NcaSectionHeaderBlock))) | ||
| 145 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 146 | |||
| 147 | NcaSectionHeaderBlock block{}; | ||
| 148 | std::memcpy(&block, array.data(), sizeof(NcaSectionHeaderBlock)); | ||
| 149 | |||
| 150 | if (block.filesystem_type == NcaSectionFilesystemType::ROMFS) { | ||
| 151 | romfs_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER; | ||
| 152 | romfs_size = | ||
| 153 | header.section_tables[i].media_end_offset * MEDIA_OFFSET_MULTIPLIER - romfs_offset; | ||
| 154 | } else if (block.filesystem_type == NcaSectionFilesystemType::PFS0) { | ||
| 155 | Pfs0Superblock sb{}; | ||
| 156 | // Seek back to beginning of this section. | ||
| 157 | file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET); | ||
| 158 | if (sizeof(Pfs0Superblock) != file.ReadBytes(&sb, sizeof(Pfs0Superblock))) | ||
| 159 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 160 | |||
| 161 | u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * | ||
| 162 | MEDIA_OFFSET_MULTIPLIER) + | ||
| 163 | sb.pfs0_header_offset; | ||
| 164 | FileSys::PartitionFilesystem npfs{}; | ||
| 165 | ResultStatus status = npfs.Load(path, offset); | ||
| 166 | |||
| 167 | if (status == ResultStatus::Success) { | ||
| 168 | pfs.emplace_back(std::move(npfs)); | ||
| 169 | pfs_offset.emplace_back(offset); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | for (size_t i = 0; i < pfs.size(); ++i) { | ||
| 175 | if (IsPfsExeFs(pfs[i])) | ||
| 176 | exefs_id = i; | ||
| 177 | } | ||
| 178 | |||
| 179 | return ResultStatus::Success; | ||
| 180 | } | ||
| 181 | |||
| 182 | FileSys::PartitionFilesystem Nca::GetPfs(u8 id) const { | ||
| 183 | return pfs[id]; | ||
| 184 | } | ||
| 185 | |||
| 186 | u64 Nca::GetExeFsFileOffset(const std::string& file_name) const { | ||
| 187 | if (exefs_id == boost::none) | ||
| 188 | return 0; | ||
| 189 | return pfs[*exefs_id].GetFileOffset(file_name) + pfs_offset[*exefs_id]; | ||
| 190 | } | ||
| 191 | |||
| 192 | u64 Nca::GetExeFsFileSize(const std::string& file_name) const { | ||
| 193 | if (exefs_id == boost::none) | ||
| 194 | return 0; | ||
| 195 | return pfs[*exefs_id].GetFileSize(file_name); | ||
| 196 | } | ||
| 197 | |||
| 198 | u64 Nca::GetRomFsOffset() const { | ||
| 199 | return romfs_offset; | ||
| 200 | } | ||
| 201 | |||
| 202 | u64 Nca::GetRomFsSize() const { | ||
| 203 | return romfs_size; | ||
| 204 | } | ||
| 205 | |||
| 206 | std::vector<u8> Nca::GetExeFsFile(const std::string& file_name) { | ||
| 207 | std::vector<u8> out(GetExeFsFileSize(file_name)); | ||
| 208 | file.Seek(GetExeFsFileOffset(file_name), SEEK_SET); | ||
| 209 | file.ReadBytes(out.data(), GetExeFsFileSize(file_name)); | ||
| 210 | return out; | ||
| 211 | } | ||
| 212 | |||
| 213 | AppLoader_NCA::AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath) | ||
| 214 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 215 | |||
| 216 | FileType AppLoader_NCA::IdentifyType(FileUtil::IOFile& file, const std::string&) { | ||
| 217 | file.Seek(0, SEEK_SET); | ||
| 218 | std::array<u8, 0x400> header_enc_array{}; | ||
| 219 | if (0x400 != file.ReadBytes(header_enc_array.data(), 0x400)) | ||
| 220 | return FileType::Error; | ||
| 221 | 25 | ||
| 26 | FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) { | ||
| 222 | // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support. | 27 | // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support. |
| 223 | NcaHeader header{}; | 28 | FileSys::NCAHeader header{}; |
| 224 | std::memcpy(&header, header_enc_array.data(), sizeof(NcaHeader)); | 29 | if (sizeof(FileSys::NCAHeader) != file->ReadObject(&header)) |
| 30 | return FileType::Error; | ||
| 225 | 31 | ||
| 226 | if (IsValidNca(header) && header.content_type == NcaContentType::Program) | 32 | if (IsValidNCA(header) && header.content_type == FileSys::NCAContentType::Program) |
| 227 | return FileType::NCA; | 33 | return FileType::NCA; |
| 228 | 34 | ||
| 229 | return FileType::Error; | 35 | return FileType::Error; |
| @@ -233,17 +39,22 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 233 | if (is_loaded) { | 39 | if (is_loaded) { |
| 234 | return ResultStatus::ErrorAlreadyLoaded; | 40 | return ResultStatus::ErrorAlreadyLoaded; |
| 235 | } | 41 | } |
| 236 | if (!file.IsOpen()) { | ||
| 237 | return ResultStatus::Error; | ||
| 238 | } | ||
| 239 | 42 | ||
| 240 | nca = std::make_unique<Nca>(); | 43 | nca = std::make_unique<FileSys::NCA>(file); |
| 241 | ResultStatus result = nca->Load(std::move(file), filepath); | 44 | ResultStatus result = nca->GetStatus(); |
| 242 | if (result != ResultStatus::Success) { | 45 | if (result != ResultStatus::Success) { |
| 243 | return result; | 46 | return result; |
| 244 | } | 47 | } |
| 245 | 48 | ||
| 246 | result = metadata.Load(nca->GetExeFsFile("main.npdm")); | 49 | if (nca->GetType() != FileSys::NCAContentType::Program) |
| 50 | return ResultStatus::ErrorInvalidFormat; | ||
| 51 | |||
| 52 | auto exefs = nca->GetExeFS(); | ||
| 53 | |||
| 54 | if (exefs == nullptr) | ||
| 55 | return ResultStatus::ErrorInvalidFormat; | ||
| 56 | |||
| 57 | result = metadata.Load(exefs->GetFile("main.npdm")); | ||
| 247 | if (result != ResultStatus::Success) { | 58 | if (result != ResultStatus::Success) { |
| 248 | return result; | 59 | return result; |
| 249 | } | 60 | } |
| @@ -258,7 +69,8 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 258 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", | 69 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", |
| 259 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { | 70 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { |
| 260 | const VAddr load_addr = next_load_addr; | 71 | const VAddr load_addr = next_load_addr; |
| 261 | next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr); | 72 | |
| 73 | next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr); | ||
| 262 | if (next_load_addr) { | 74 | if (next_load_addr) { |
| 263 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); | 75 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); |
| 264 | // Register module with GDBStub | 76 | // Register module with GDBStub |
| @@ -276,28 +88,18 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 276 | process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), | 88 | process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), |
| 277 | metadata.GetMainThreadStackSize()); | 89 | metadata.GetMainThreadStackSize()); |
| 278 | 90 | ||
| 279 | if (nca->GetRomFsSize() > 0) | 91 | if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) |
| 280 | Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); | 92 | Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); |
| 281 | 93 | ||
| 282 | is_loaded = true; | 94 | is_loaded = true; |
| 95 | |||
| 283 | return ResultStatus::Success; | 96 | return ResultStatus::Success; |
| 284 | } | 97 | } |
| 285 | 98 | ||
| 286 | ResultStatus AppLoader_NCA::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | 99 | ResultStatus AppLoader_NCA::ReadRomFS(FileSys::VirtualFile& dir) { |
| 287 | u64& size) { | 100 | if (nca == nullptr || nca->GetRomFS() == nullptr || nca->GetRomFS()->GetSize() == 0) |
| 288 | if (nca->GetRomFsSize() == 0) { | ||
| 289 | LOG_DEBUG(Loader, "No RomFS available"); | ||
| 290 | return ResultStatus::ErrorNotUsed; | 101 | return ResultStatus::ErrorNotUsed; |
| 291 | } | 102 | dir = nca->GetRomFS(); |
| 292 | |||
| 293 | romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb"); | ||
| 294 | |||
| 295 | offset = nca->GetRomFsOffset(); | ||
| 296 | size = nca->GetRomFsSize(); | ||
| 297 | |||
| 298 | LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset); | ||
| 299 | LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size); | ||
| 300 | |||
| 301 | return ResultStatus::Success; | 103 | return ResultStatus::Success; |
| 302 | } | 104 | } |
| 303 | 105 | ||
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index 3b6c451d0..52c95953a 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h | |||
| @@ -6,44 +6,39 @@ | |||
| 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, | 34 | ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; |
| 38 | u64& size) override; | ||
| 39 | 35 | ||
| 40 | ~AppLoader_NCA(); | 36 | ~AppLoader_NCA(); |
| 41 | 37 | ||
| 42 | private: | 38 | private: |
| 43 | std::string filepath; | ||
| 44 | FileSys::ProgramMetadata metadata; | 39 | FileSys::ProgramMetadata metadata; |
| 45 | 40 | ||
| 46 | std::unique_ptr<Nca> nca; | 41 | std::unique_ptr<FileSys::NCA> nca; |
| 47 | }; | 42 | }; |
| 48 | 43 | ||
| 49 | } // namespace Loader | 44 | } // namespace Loader |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 4d7c69a22..a007d3e6e 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -48,14 +48,12 @@ struct ModHeader { | |||
| 48 | }; | 48 | }; |
| 49 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); | 49 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); |
| 50 | 50 | ||
| 51 | AppLoader_NRO::AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath) | 51 | AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(file) {} |
| 52 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 53 | 52 | ||
| 54 | FileType AppLoader_NRO::IdentifyType(FileUtil::IOFile& file, const std::string&) { | 53 | FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) { |
| 55 | // Read NSO header | 54 | // Read NSO header |
| 56 | NroHeader nro_header{}; | 55 | NroHeader nro_header{}; |
| 57 | file.Seek(0, SEEK_SET); | 56 | if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { |
| 58 | if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) { | ||
| 59 | return FileType::Error; | 57 | return FileType::Error; |
| 60 | } | 58 | } |
| 61 | if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) { | 59 | if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) { |
| @@ -68,16 +66,10 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 68 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | 66 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; |
| 69 | } | 67 | } |
| 70 | 68 | ||
| 71 | bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) { | 69 | bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { |
| 72 | FileUtil::IOFile file(path, "rb"); | ||
| 73 | if (!file.IsOpen()) { | ||
| 74 | return {}; | ||
| 75 | } | ||
| 76 | |||
| 77 | // Read NSO header | 70 | // Read NSO header |
| 78 | NroHeader nro_header{}; | 71 | NroHeader nro_header{}; |
| 79 | file.Seek(0, SEEK_SET); | 72 | if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { |
| 80 | if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) { | ||
| 81 | return {}; | 73 | return {}; |
| 82 | } | 74 | } |
| 83 | if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { | 75 | if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { |
| @@ -86,10 +78,9 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) { | |||
| 86 | 78 | ||
| 87 | // Build program image | 79 | // Build program image |
| 88 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); | 80 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); |
| 89 | std::vector<u8> program_image; | 81 | std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size)); |
| 90 | program_image.resize(PageAlignSize(nro_header.file_size)); | 82 | if (program_image.size() != PageAlignSize(nro_header.file_size)) |
| 91 | file.Seek(0, SEEK_SET); | 83 | return {}; |
| 92 | file.ReadBytes(program_image.data(), nro_header.file_size); | ||
| 93 | 84 | ||
| 94 | for (int i = 0; i < nro_header.segments.size(); ++i) { | 85 | for (int i = 0; i < nro_header.segments.size(); ++i) { |
| 95 | codeset->segments[i].addr = nro_header.segments[i].offset; | 86 | codeset->segments[i].addr = nro_header.segments[i].offset; |
| @@ -112,7 +103,7 @@ bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) { | |||
| 112 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); | 103 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); |
| 113 | 104 | ||
| 114 | // Load codeset for current process | 105 | // Load codeset for current process |
| 115 | codeset->name = path; | 106 | codeset->name = file->GetName(); |
| 116 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | 107 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); |
| 117 | Core::CurrentProcess()->LoadModule(codeset, load_base); | 108 | Core::CurrentProcess()->LoadModule(codeset, load_base); |
| 118 | 109 | ||
| @@ -126,14 +117,11 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 126 | if (is_loaded) { | 117 | if (is_loaded) { |
| 127 | return ResultStatus::ErrorAlreadyLoaded; | 118 | return ResultStatus::ErrorAlreadyLoaded; |
| 128 | } | 119 | } |
| 129 | if (!file.IsOpen()) { | ||
| 130 | return ResultStatus::Error; | ||
| 131 | } | ||
| 132 | 120 | ||
| 133 | // Load NRO | 121 | // Load NRO |
| 134 | static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; | 122 | static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; |
| 135 | 123 | ||
| 136 | if (!LoadNro(filepath, base_addr)) { | 124 | if (!LoadNro(file, base_addr)) { |
| 137 | return ResultStatus::ErrorInvalidFormat; | 125 | return ResultStatus::ErrorInvalidFormat; |
| 138 | } | 126 | } |
| 139 | 127 | ||
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 7b3d6b837..2beb85fbf 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -38,6 +38,7 @@ struct NsoHeader { | |||
| 38 | std::array<u32_le, 3> segments_compressed_size; | 38 | std::array<u32_le, 3> segments_compressed_size; |
| 39 | }; | 39 | }; |
| 40 | static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); | 40 | static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); |
| 41 | static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable."); | ||
| 41 | 42 | ||
| 42 | struct ModHeader { | 43 | struct ModHeader { |
| 43 | u32_le magic; | 44 | u32_le magic; |
| @@ -50,15 +51,11 @@ struct ModHeader { | |||
| 50 | }; | 51 | }; |
| 51 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); | 52 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); |
| 52 | 53 | ||
| 53 | AppLoader_NSO::AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath) | 54 | AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} |
| 54 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 55 | 55 | ||
| 56 | FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file, const std::string&) { | 56 | FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) { |
| 57 | u32 magic = 0; | 57 | u32 magic = 0; |
| 58 | file.Seek(0, SEEK_SET); | 58 | file->ReadObject(&magic); |
| 59 | if (1 != file.ReadArray<u32>(&magic, 1)) { | ||
| 60 | return FileType::Error; | ||
| 61 | } | ||
| 62 | 59 | ||
| 63 | if (Common::MakeMagic('N', 'S', 'O', '0') == magic) { | 60 | if (Common::MakeMagic('N', 'S', 'O', '0') == magic) { |
| 64 | return FileType::NSO; | 61 | return FileType::NSO; |
| @@ -99,13 +96,16 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 99 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | 96 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; |
| 100 | } | 97 | } |
| 101 | 98 | ||
| 102 | VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& file_data, | 99 | VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { |
| 103 | VAddr load_base) { | 100 | if (file == nullptr) |
| 104 | if (file_data.size() < sizeof(NsoHeader)) | ||
| 105 | return {}; | 101 | return {}; |
| 106 | 102 | ||
| 107 | NsoHeader nso_header; | 103 | if (file->GetSize() < sizeof(NsoHeader)) |
| 108 | std::memcpy(&nso_header, file_data.data(), sizeof(NsoHeader)); | 104 | return {}; |
| 105 | |||
| 106 | NsoHeader nso_header{}; | ||
| 107 | if (sizeof(NsoHeader) != file->ReadObject(&nso_header)) | ||
| 108 | return {}; | ||
| 109 | 109 | ||
| 110 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) | 110 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) |
| 111 | return {}; | 111 | return {}; |
| @@ -114,9 +114,8 @@ VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& | |||
| 114 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); | 114 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); |
| 115 | std::vector<u8> program_image; | 115 | std::vector<u8> program_image; |
| 116 | for (int i = 0; i < nso_header.segments.size(); ++i) { | 116 | for (int i = 0; i < nso_header.segments.size(); ++i) { |
| 117 | std::vector<u8> compressed_data(nso_header.segments_compressed_size[i]); | 117 | const std::vector<u8> compressed_data = |
| 118 | for (auto j = 0; j < nso_header.segments_compressed_size[i]; ++j) | 118 | file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); |
| 119 | compressed_data[j] = file_data[nso_header.segments[i].offset + j]; | ||
| 120 | std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]); | 119 | std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]); |
| 121 | program_image.resize(nso_header.segments[i].location); | 120 | program_image.resize(nso_header.segments[i].location); |
| 122 | program_image.insert(program_image.end(), data.begin(), data.end()); | 121 | program_image.insert(program_image.end(), data.begin(), data.end()); |
| @@ -144,7 +143,7 @@ VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& | |||
| 144 | program_image.resize(image_size); | 143 | program_image.resize(image_size); |
| 145 | 144 | ||
| 146 | // Load codeset for current process | 145 | // Load codeset for current process |
| 147 | codeset->name = name; | 146 | codeset->name = file->GetName(); |
| 148 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | 147 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); |
| 149 | Core::CurrentProcess()->LoadModule(codeset, load_base); | 148 | Core::CurrentProcess()->LoadModule(codeset, load_base); |
| 150 | 149 | ||
| @@ -154,72 +153,14 @@ VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& | |||
| 154 | return load_base + image_size; | 153 | return load_base + image_size; |
| 155 | } | 154 | } |
| 156 | 155 | ||
| 157 | VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) { | ||
| 158 | FileUtil::IOFile file(path, "rb"); | ||
| 159 | if (!file.IsOpen()) { | ||
| 160 | return {}; | ||
| 161 | } | ||
| 162 | |||
| 163 | // Read NSO header | ||
| 164 | NsoHeader nso_header{}; | ||
| 165 | file.Seek(0, SEEK_SET); | ||
| 166 | if (sizeof(NsoHeader) != file.ReadBytes(&nso_header, sizeof(NsoHeader))) { | ||
| 167 | return {}; | ||
| 168 | } | ||
| 169 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { | ||
| 170 | return {}; | ||
| 171 | } | ||
| 172 | |||
| 173 | // Build program image | ||
| 174 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); | ||
| 175 | std::vector<u8> program_image; | ||
| 176 | for (int i = 0; i < nso_header.segments.size(); ++i) { | ||
| 177 | std::vector<u8> data = | ||
| 178 | ReadSegment(file, nso_header.segments[i], nso_header.segments_compressed_size[i]); | ||
| 179 | program_image.resize(nso_header.segments[i].location); | ||
| 180 | program_image.insert(program_image.end(), data.begin(), data.end()); | ||
| 181 | codeset->segments[i].addr = nso_header.segments[i].location; | ||
| 182 | codeset->segments[i].offset = nso_header.segments[i].location; | ||
| 183 | codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); | ||
| 184 | } | ||
| 185 | |||
| 186 | // MOD header pointer is at .text offset + 4 | ||
| 187 | u32 module_offset; | ||
| 188 | std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); | ||
| 189 | |||
| 190 | // Read MOD header | ||
| 191 | ModHeader mod_header{}; | ||
| 192 | // Default .bss to size in segment header if MOD0 section doesn't exist | ||
| 193 | u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; | ||
| 194 | std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader)); | ||
| 195 | const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; | ||
| 196 | if (has_mod_header) { | ||
| 197 | // Resize program image to include .bss section and page align each section | ||
| 198 | bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); | ||
| 199 | } | ||
| 200 | codeset->data.size += bss_size; | ||
| 201 | const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; | ||
| 202 | program_image.resize(image_size); | ||
| 203 | |||
| 204 | // Load codeset for current process | ||
| 205 | codeset->name = path; | ||
| 206 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | ||
| 207 | Core::CurrentProcess()->LoadModule(codeset, load_base); | ||
| 208 | |||
| 209 | return load_base + image_size; | ||
| 210 | } | ||
| 211 | |||
| 212 | ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | 156 | ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { |
| 213 | if (is_loaded) { | 157 | if (is_loaded) { |
| 214 | return ResultStatus::ErrorAlreadyLoaded; | 158 | return ResultStatus::ErrorAlreadyLoaded; |
| 215 | } | 159 | } |
| 216 | if (!file.IsOpen()) { | ||
| 217 | return ResultStatus::Error; | ||
| 218 | } | ||
| 219 | 160 | ||
| 220 | // Load module | 161 | // Load module |
| 221 | LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR); | 162 | LoadModule(file, Memory::PROCESS_IMAGE_VADDR); |
| 222 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", filepath, Memory::PROCESS_IMAGE_VADDR); | 163 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), Memory::PROCESS_IMAGE_VADDR); |
| 223 | 164 | ||
| 224 | process->svc_access_mask.set(); | 165 | process->svc_access_mask.set(); |
| 225 | process->address_mappings = default_address_mappings; | 166 | 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/core/memory.cpp b/src/core/memory.cpp index 190ccc25c..a18e27da0 100644 --- a/src/core/memory.cpp +++ b/src/core/memory.cpp | |||
| @@ -115,11 +115,6 @@ static std::set<MemoryHookPointer> GetSpecialHandlers(const PageTable& page_tabl | |||
| 115 | return result; | 115 | return result; |
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | static std::set<MemoryHookPointer> GetSpecialHandlers(VAddr vaddr, u64 size) { | ||
| 119 | const PageTable& page_table = Core::CurrentProcess()->vm_manager.page_table; | ||
| 120 | return GetSpecialHandlers(page_table, vaddr, size); | ||
| 121 | } | ||
| 122 | |||
| 123 | /** | 118 | /** |
| 124 | * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) | 119 | * Gets a pointer to the exact memory at the virtual address (i.e. not page aligned) |
| 125 | * using a VMA from the current process | 120 | * using a VMA from the current process |
| @@ -587,8 +582,6 @@ void ZeroBlock(const Kernel::Process& process, const VAddr dest_addr, const size | |||
| 587 | size_t page_index = dest_addr >> PAGE_BITS; | 582 | size_t page_index = dest_addr >> PAGE_BITS; |
| 588 | size_t page_offset = dest_addr & PAGE_MASK; | 583 | size_t page_offset = dest_addr & PAGE_MASK; |
| 589 | 584 | ||
| 590 | static const std::array<u8, PAGE_SIZE> zeros = {}; | ||
| 591 | |||
| 592 | while (remaining_size > 0) { | 585 | while (remaining_size > 0) { |
| 593 | const size_t copy_amount = | 586 | const size_t copy_amount = |
| 594 | std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); | 587 | std::min(static_cast<size_t>(PAGE_SIZE) - page_offset, remaining_size); |
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt index 12f1b93e0..6a0a62ecc 100644 --- a/src/tests/CMakeLists.txt +++ b/src/tests/CMakeLists.txt | |||
| @@ -3,7 +3,6 @@ add_executable(tests | |||
| 3 | core/arm/arm_test_common.cpp | 3 | core/arm/arm_test_common.cpp |
| 4 | core/arm/arm_test_common.h | 4 | core/arm/arm_test_common.h |
| 5 | core/core_timing.cpp | 5 | core/core_timing.cpp |
| 6 | core/file_sys/path_parser.cpp | ||
| 7 | core/memory/memory.cpp | 6 | core/memory/memory.cpp |
| 8 | glad.cpp | 7 | glad.cpp |
| 9 | tests.cpp | 8 | tests.cpp |
diff --git a/src/tests/core/file_sys/path_parser.cpp b/src/tests/core/file_sys/path_parser.cpp deleted file mode 100644 index 2b543e438..000000000 --- a/src/tests/core/file_sys/path_parser.cpp +++ /dev/null | |||
| @@ -1,38 +0,0 @@ | |||
| 1 | // Copyright 2016 Citra Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <catch.hpp> | ||
| 6 | #include "common/file_util.h" | ||
| 7 | #include "core/file_sys/path_parser.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | TEST_CASE("PathParser", "[core][file_sys]") { | ||
| 12 | REQUIRE(!PathParser(Path(std::vector<u8>{})).IsValid()); | ||
| 13 | REQUIRE(!PathParser(Path("a")).IsValid()); | ||
| 14 | REQUIRE(!PathParser(Path("/|")).IsValid()); | ||
| 15 | REQUIRE(PathParser(Path("/a")).IsValid()); | ||
| 16 | REQUIRE(!PathParser(Path("/a/b/../../c/../../d")).IsValid()); | ||
| 17 | REQUIRE(PathParser(Path("/a/b/../c/../../d")).IsValid()); | ||
| 18 | REQUIRE(PathParser(Path("/")).IsRootDirectory()); | ||
| 19 | REQUIRE(!PathParser(Path("/a")).IsRootDirectory()); | ||
| 20 | REQUIRE(PathParser(Path("/a/..")).IsRootDirectory()); | ||
| 21 | } | ||
| 22 | |||
| 23 | TEST_CASE("PathParser - Host file system", "[core][file_sys]") { | ||
| 24 | std::string test_dir = "./test"; | ||
| 25 | FileUtil::CreateDir(test_dir); | ||
| 26 | FileUtil::CreateDir(test_dir + "/z"); | ||
| 27 | FileUtil::CreateEmptyFile(test_dir + "/a"); | ||
| 28 | |||
| 29 | REQUIRE(PathParser(Path("/a")).GetHostStatus(test_dir) == PathParser::FileFound); | ||
| 30 | REQUIRE(PathParser(Path("/b")).GetHostStatus(test_dir) == PathParser::NotFound); | ||
| 31 | REQUIRE(PathParser(Path("/z")).GetHostStatus(test_dir) == PathParser::DirectoryFound); | ||
| 32 | REQUIRE(PathParser(Path("/a/c")).GetHostStatus(test_dir) == PathParser::FileInPath); | ||
| 33 | REQUIRE(PathParser(Path("/b/c")).GetHostStatus(test_dir) == PathParser::PathNotFound); | ||
| 34 | |||
| 35 | FileUtil::DeleteDirRecursively(test_dir); | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace FileSys | ||
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index eecbc5ff0..7e620584f 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -128,8 +128,8 @@ RasterizerOpenGL::~RasterizerOpenGL() { | |||
| 128 | std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, | 128 | std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, |
| 129 | GLintptr buffer_offset) { | 129 | GLintptr buffer_offset) { |
| 130 | MICROPROFILE_SCOPE(OpenGL_VAO); | 130 | MICROPROFILE_SCOPE(OpenGL_VAO); |
| 131 | const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; | 131 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 132 | const auto& memory_manager = Core::System().GetInstance().GPU().memory_manager; | 132 | const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; |
| 133 | 133 | ||
| 134 | state.draw.vertex_array = hw_vao.handle; | 134 | state.draw.vertex_array = hw_vao.handle; |
| 135 | state.draw.vertex_buffer = stream_buffer->GetHandle(); | 135 | state.draw.vertex_buffer = stream_buffer->GetHandle(); |
| @@ -184,7 +184,7 @@ std::pair<u8*, GLintptr> RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, | |||
| 184 | } | 184 | } |
| 185 | 185 | ||
| 186 | static GLShader::ProgramCode GetShaderProgramCode(Maxwell::ShaderProgram program) { | 186 | static GLShader::ProgramCode GetShaderProgramCode(Maxwell::ShaderProgram program) { |
| 187 | auto& gpu = Core::System().GetInstance().GPU().Maxwell3D(); | 187 | auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); |
| 188 | 188 | ||
| 189 | // Fetch program code from memory | 189 | // Fetch program code from memory |
| 190 | GLShader::ProgramCode program_code; | 190 | GLShader::ProgramCode program_code; |
| @@ -207,7 +207,7 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { | |||
| 207 | } | 207 | } |
| 208 | }; | 208 | }; |
| 209 | 209 | ||
| 210 | auto& gpu = Core::System().GetInstance().GPU().Maxwell3D(); | 210 | auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); |
| 211 | 211 | ||
| 212 | // Next available bindpoints to use when uploading the const buffers and textures to the GLSL | 212 | // Next available bindpoints to use when uploading the const buffers and textures to the GLSL |
| 213 | // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points. | 213 | // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points. |
| @@ -297,7 +297,7 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { | |||
| 297 | } | 297 | } |
| 298 | 298 | ||
| 299 | size_t RasterizerOpenGL::CalculateVertexArraysSize() const { | 299 | size_t RasterizerOpenGL::CalculateVertexArraysSize() const { |
| 300 | const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; | 300 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 301 | 301 | ||
| 302 | size_t size = 0; | 302 | size_t size = 0; |
| 303 | for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { | 303 | for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) { |
| @@ -322,7 +322,7 @@ bool RasterizerOpenGL::AccelerateDrawBatch(bool is_indexed) { | |||
| 322 | 322 | ||
| 323 | std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, | 323 | std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_color_fb, |
| 324 | bool using_depth_fb) { | 324 | bool using_depth_fb) { |
| 325 | const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; | 325 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 326 | 326 | ||
| 327 | // TODO(bunnei): Implement this | 327 | // TODO(bunnei): Implement this |
| 328 | const bool has_stencil = false; | 328 | const bool has_stencil = false; |
| @@ -374,7 +374,7 @@ std::pair<Surface, Surface> RasterizerOpenGL::ConfigureFramebuffers(bool using_c | |||
| 374 | } | 374 | } |
| 375 | 375 | ||
| 376 | void RasterizerOpenGL::Clear() { | 376 | void RasterizerOpenGL::Clear() { |
| 377 | const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; | 377 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 378 | 378 | ||
| 379 | bool use_color_fb = false; | 379 | bool use_color_fb = false; |
| 380 | bool use_depth_fb = false; | 380 | bool use_depth_fb = false; |
| @@ -426,7 +426,7 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 426 | return; | 426 | return; |
| 427 | 427 | ||
| 428 | MICROPROFILE_SCOPE(OpenGL_Drawing); | 428 | MICROPROFILE_SCOPE(OpenGL_Drawing); |
| 429 | const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; | 429 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 430 | 430 | ||
| 431 | ScopeAcquireGLContext acquire_context; | 431 | ScopeAcquireGLContext acquire_context; |
| 432 | 432 | ||
| @@ -473,7 +473,7 @@ void RasterizerOpenGL::DrawArrays() { | |||
| 473 | // If indexed mode, copy the index buffer | 473 | // If indexed mode, copy the index buffer |
| 474 | GLintptr index_buffer_offset = 0; | 474 | GLintptr index_buffer_offset = 0; |
| 475 | if (is_indexed) { | 475 | if (is_indexed) { |
| 476 | const auto& memory_manager = Core::System().GetInstance().GPU().memory_manager; | 476 | const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; |
| 477 | const boost::optional<VAddr> index_data_addr{ | 477 | const boost::optional<VAddr> index_data_addr{ |
| 478 | memory_manager->GpuToCpuAddress(regs.index_array.StartAddress())}; | 478 | memory_manager->GpuToCpuAddress(regs.index_array.StartAddress())}; |
| 479 | Memory::ReadBlock(*index_data_addr, offseted_buffer, index_buffer_size); | 479 | Memory::ReadBlock(*index_data_addr, offseted_buffer, index_buffer_size); |
| @@ -775,7 +775,7 @@ void RasterizerOpenGL::BindFramebufferSurfaces(const Surface& color_surface, | |||
| 775 | } | 775 | } |
| 776 | 776 | ||
| 777 | void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect) { | 777 | void RasterizerOpenGL::SyncViewport(const MathUtil::Rectangle<u32>& surfaces_rect) { |
| 778 | const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; | 778 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 779 | const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; | 779 | const MathUtil::Rectangle<s32> viewport_rect{regs.viewport_transform[0].GetRect()}; |
| 780 | 780 | ||
| 781 | state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left; | 781 | state.viewport.x = static_cast<GLint>(surfaces_rect.left) + viewport_rect.left; |
| @@ -793,7 +793,7 @@ void RasterizerOpenGL::SyncClipCoef() { | |||
| 793 | } | 793 | } |
| 794 | 794 | ||
| 795 | void RasterizerOpenGL::SyncCullMode() { | 795 | void RasterizerOpenGL::SyncCullMode() { |
| 796 | const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; | 796 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 797 | 797 | ||
| 798 | state.cull.enabled = regs.cull.enabled != 0; | 798 | state.cull.enabled = regs.cull.enabled != 0; |
| 799 | 799 | ||
| @@ -825,7 +825,7 @@ void RasterizerOpenGL::SyncDepthOffset() { | |||
| 825 | } | 825 | } |
| 826 | 826 | ||
| 827 | void RasterizerOpenGL::SyncDepthTestState() { | 827 | void RasterizerOpenGL::SyncDepthTestState() { |
| 828 | const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; | 828 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 829 | 829 | ||
| 830 | state.depth.test_enabled = regs.depth_test_enable != 0; | 830 | state.depth.test_enabled = regs.depth_test_enable != 0; |
| 831 | state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE; | 831 | state.depth.write_mask = regs.depth_write_enabled ? GL_TRUE : GL_FALSE; |
| @@ -837,7 +837,7 @@ void RasterizerOpenGL::SyncDepthTestState() { | |||
| 837 | } | 837 | } |
| 838 | 838 | ||
| 839 | void RasterizerOpenGL::SyncBlendState() { | 839 | void RasterizerOpenGL::SyncBlendState() { |
| 840 | const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; | 840 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 841 | 841 | ||
| 842 | // TODO(Subv): Support more than just render target 0. | 842 | // TODO(Subv): Support more than just render target 0. |
| 843 | state.blend.enabled = regs.blend.enable[0] != 0; | 843 | state.blend.enabled = regs.blend.enable[0] != 0; |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index c171c4c5b..2c43982b0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -505,7 +505,7 @@ Surface RasterizerCacheOpenGL::GetTextureSurface(const Tegra::Texture::FullTextu | |||
| 505 | 505 | ||
| 506 | SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( | 506 | SurfaceSurfaceRect_Tuple RasterizerCacheOpenGL::GetFramebufferSurfaces( |
| 507 | bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport) { | 507 | bool using_color_fb, bool using_depth_fb, const MathUtil::Rectangle<s32>& viewport) { |
| 508 | const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; | 508 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 509 | 509 | ||
| 510 | // TODO(bunnei): This is hard corded to use just the first render buffer | 510 | // TODO(bunnei): This is hard corded to use just the first render buffer |
| 511 | LOG_WARNING(Render_OpenGL, "hard-coded for render target 0!"); | 511 | LOG_WARNING(Render_OpenGL, "hard-coded for render target 0!"); |
diff --git a/src/video_core/renderer_opengl/gl_shader_manager.cpp b/src/video_core/renderer_opengl/gl_shader_manager.cpp index d7167b298..1aa437f76 100644 --- a/src/video_core/renderer_opengl/gl_shader_manager.cpp +++ b/src/video_core/renderer_opengl/gl_shader_manager.cpp | |||
| @@ -35,7 +35,7 @@ void SetShaderUniformBlockBindings(GLuint shader) { | |||
| 35 | } // namespace Impl | 35 | } // namespace Impl |
| 36 | 36 | ||
| 37 | void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) { | 37 | void MaxwellUniformData::SetFromRegs(const Maxwell3D::State::ShaderStageInfo& shader_stage) { |
| 38 | const auto& regs = Core::System().GetInstance().GPU().Maxwell3D().regs; | 38 | const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; |
| 39 | 39 | ||
| 40 | // TODO(bunnei): Support more than one viewport | 40 | // TODO(bunnei): Support more than one viewport |
| 41 | viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; | 41 | viewport_flip[0] = regs.viewport_transform[0].scale_x < 0.0 ? -1.0f : 1.0f; |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index be18aa299..a3684a195 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.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 <cmath> | ||
| 5 | #include <cstring> | 6 | #include <cstring> |
| 6 | #include "common/assert.h" | 7 | #include "common/assert.h" |
| 7 | #include "core/memory.h" | 8 | #include "core/memory.h" |
| @@ -17,7 +18,9 @@ namespace Texture { | |||
| 17 | * Taken from the Tegra X1 TRM. | 18 | * Taken from the Tegra X1 TRM. |
| 18 | */ | 19 | */ |
| 19 | static u32 GetSwizzleOffset(u32 x, u32 y, u32 image_width, u32 bytes_per_pixel, u32 block_height) { | 20 | static u32 GetSwizzleOffset(u32 x, u32 y, u32 image_width, u32 bytes_per_pixel, u32 block_height) { |
| 20 | u32 image_width_in_gobs = image_width * bytes_per_pixel / 64; | 21 | // Round up to the next gob |
| 22 | const u32 image_width_in_gobs{(image_width * bytes_per_pixel + 63) / 64}; | ||
| 23 | |||
| 21 | u32 GOB_address = 0 + (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs + | 24 | u32 GOB_address = 0 + (y / (8 * block_height)) * 512 * block_height * image_width_in_gobs + |
| 22 | (x * bytes_per_pixel / 64) * 512 * block_height + | 25 | (x * bytes_per_pixel / 64) * 512 * block_height + |
| 23 | (y % (8 * block_height) / 8) * 512; | 26 | (y % (8 * block_height) / 8) * 512; |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 5f43eb177..e1ca0e77b 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" |
| @@ -138,7 +139,7 @@ GameList::SearchField::SearchField(GameList* parent) : QWidget{parent} { | |||
| 138 | * @param userinput String containing all words getting checked | 139 | * @param userinput String containing all words getting checked |
| 139 | * @return true if the haystack contains all words of userinput | 140 | * @return true if the haystack contains all words of userinput |
| 140 | */ | 141 | */ |
| 141 | bool GameList::ContainsAllWords(const QString& haystack, const QString& userinput) const { | 142 | static bool ContainsAllWords(const QString& haystack, const QString& userinput) { |
| 142 | const QStringList userinput_split = | 143 | const QStringList userinput_split = |
| 143 | userinput.split(' ', QString::SplitBehavior::SkipEmptyParts); | 144 | userinput.split(' ', QString::SplitBehavior::SkipEmptyParts); |
| 144 | 145 | ||
| @@ -403,7 +404,8 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||
| 403 | bool is_dir = FileUtil::IsDirectory(physical_name); | 404 | bool is_dir = FileUtil::IsDirectory(physical_name); |
| 404 | if (!is_dir && | 405 | if (!is_dir && |
| 405 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { | 406 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { |
| 406 | std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name); | 407 | std::unique_ptr<Loader::AppLoader> loader = |
| 408 | Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name)); | ||
| 407 | if (!loader) | 409 | if (!loader) |
| 408 | return true; | 410 | return true; |
| 409 | 411 | ||
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h index 14db3956d..3bc14f07f 100644 --- a/src/yuzu/game_list.h +++ b/src/yuzu/game_list.h | |||
| @@ -89,7 +89,6 @@ private: | |||
| 89 | 89 | ||
| 90 | void PopupContextMenu(const QPoint& menu_location); | 90 | void PopupContextMenu(const QPoint& menu_location); |
| 91 | void RefreshGameDirectory(); | 91 | void RefreshGameDirectory(); |
| 92 | bool ContainsAllWords(const QString& haystack, const QString& userinput) const; | ||
| 93 | 92 | ||
| 94 | SearchField* search_field; | 93 | SearchField* search_field; |
| 95 | GMainWindow* main_window = nullptr; | 94 | GMainWindow* main_window = nullptr; |