diff options
Diffstat (limited to 'src')
66 files changed, 1822 insertions, 1862 deletions
diff --git a/src/common/assert.h b/src/common/assert.h index 655446f34..0d4eddc19 100644 --- a/src/common/assert.h +++ b/src/common/assert.h | |||
| @@ -52,5 +52,5 @@ __declspec(noinline, noreturn) | |||
| 52 | #define DEBUG_ASSERT_MSG(_a_, _desc_, ...) | 52 | #define DEBUG_ASSERT_MSG(_a_, _desc_, ...) |
| 53 | #endif | 53 | #endif |
| 54 | 54 | ||
| 55 | #define UNIMPLEMENTED() DEBUG_ASSERT_MSG(false, "Unimplemented code!") | 55 | #define UNIMPLEMENTED() LOG_CRITICAL(Debug, "Unimplemented code!") |
| 56 | #define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__) | 56 | #define UNIMPLEMENTED_MSG(...) ASSERT_MSG(false, __VA_ARGS__) |
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 7cf7b7997..995938d0b 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h | |||
| @@ -4,7 +4,7 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #if !defined(ARCHITECTURE_x86_64) && !defined(_M_ARM) | 7 | #if !defined(ARCHITECTURE_x86_64) && !defined(ARCHITECTURE_ARM) |
| 8 | #include <cstdlib> // for exit | 8 | #include <cstdlib> // for exit |
| 9 | #endif | 9 | #endif |
| 10 | #include "common/common_types.h" | 10 | #include "common/common_types.h" |
| @@ -30,7 +30,7 @@ | |||
| 30 | 30 | ||
| 31 | #ifdef ARCHITECTURE_x86_64 | 31 | #ifdef ARCHITECTURE_x86_64 |
| 32 | #define Crash() __asm__ __volatile__("int $3") | 32 | #define Crash() __asm__ __volatile__("int $3") |
| 33 | #elif defined(_M_ARM) | 33 | #elif defined(ARCHITECTURE_ARM) |
| 34 | #define Crash() __asm__ __volatile__("trap") | 34 | #define Crash() __asm__ __volatile__("trap") |
| 35 | #else | 35 | #else |
| 36 | #define Crash() exit(1) | 36 | #define Crash() exit(1) |
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp index 493a81e01..7213abe18 100644 --- a/src/common/file_util.cpp +++ b/src/common/file_util.cpp | |||
| @@ -2,7 +2,6 @@ | |||
| 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> | ||
| 6 | #include "common/assert.h" | 5 | #include "common/assert.h" |
| 7 | #include "common/common_funcs.h" | 6 | #include "common/common_funcs.h" |
| 8 | #include "common/common_paths.h" | 7 | #include "common/common_paths.h" |
| @@ -387,7 +386,7 @@ u64 GetSize(FILE* f) { | |||
| 387 | bool CreateEmptyFile(const std::string& filename) { | 386 | bool CreateEmptyFile(const std::string& filename) { |
| 388 | LOG_TRACE(Common_Filesystem, "{}", filename); | 387 | LOG_TRACE(Common_Filesystem, "{}", filename); |
| 389 | 388 | ||
| 390 | if (!FileUtil::IOFile(filename, "wb").IsOpen()) { | 389 | if (!FileUtil::IOFile(filename, "wb")) { |
| 391 | LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); | 390 | LOG_ERROR(Common_Filesystem, "failed {}: {}", filename, GetLastErrorMsg()); |
| 392 | return false; | 391 | return false; |
| 393 | } | 392 | } |
| @@ -751,7 +750,7 @@ size_t WriteStringToFile(bool text_file, const std::string& str, const char* fil | |||
| 751 | size_t ReadFileToString(bool text_file, const char* filename, std::string& str) { | 750 | size_t ReadFileToString(bool text_file, const char* filename, std::string& str) { |
| 752 | IOFile file(filename, text_file ? "r" : "rb"); | 751 | IOFile file(filename, text_file ? "r" : "rb"); |
| 753 | 752 | ||
| 754 | if (!file.IsOpen()) | 753 | if (!file) |
| 755 | return false; | 754 | return false; |
| 756 | 755 | ||
| 757 | str.resize(static_cast<u32>(file.GetSize())); | 756 | str.resize(static_cast<u32>(file.GetSize())); |
| @@ -800,57 +799,6 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam | |||
| 800 | } | 799 | } |
| 801 | } | 800 | } |
| 802 | 801 | ||
| 803 | std::vector<std::string> SplitPathComponents(const std::string& filename) { | ||
| 804 | auto copy(filename); | ||
| 805 | std::replace(copy.begin(), copy.end(), '\\', '/'); | ||
| 806 | std::vector<std::string> out; | ||
| 807 | |||
| 808 | std::stringstream stream(filename); | ||
| 809 | std::string item; | ||
| 810 | while (std::getline(stream, item, '/')) | ||
| 811 | out.push_back(std::move(item)); | ||
| 812 | |||
| 813 | return out; | ||
| 814 | } | ||
| 815 | |||
| 816 | std::string GetParentPath(const std::string& path) { | ||
| 817 | auto out = path; | ||
| 818 | const auto name_bck_index = out.find_last_of('\\'); | ||
| 819 | const auto name_fwd_index = out.find_last_of('/'); | ||
| 820 | size_t name_index; | ||
| 821 | if (name_bck_index == std::string::npos || name_fwd_index == std::string::npos) | ||
| 822 | name_index = std::min<size_t>(name_bck_index, name_fwd_index); | ||
| 823 | else | ||
| 824 | name_index = std::max<size_t>(name_bck_index, name_fwd_index); | ||
| 825 | |||
| 826 | return out.erase(name_index); | ||
| 827 | } | ||
| 828 | |||
| 829 | std::string GetFilename(std::string path) { | ||
| 830 | std::replace(path.begin(), path.end(), '\\', '/'); | ||
| 831 | auto name_index = path.find_last_of('/'); | ||
| 832 | if (name_index == std::string::npos) | ||
| 833 | return ""; | ||
| 834 | return path.substr(name_index + 1); | ||
| 835 | } | ||
| 836 | |||
| 837 | std::string GetExtensionFromFilename(const std::string& name) { | ||
| 838 | size_t index = name.find_last_of('.'); | ||
| 839 | if (index == std::string::npos) | ||
| 840 | return ""; | ||
| 841 | |||
| 842 | return name.substr(index + 1); | ||
| 843 | } | ||
| 844 | |||
| 845 | std::string RemoveTrailingSlash(const std::string& path) { | ||
| 846 | if (path.empty()) | ||
| 847 | return path; | ||
| 848 | if (path.back() == '\\' || path.back() == '/') | ||
| 849 | return path.substr(0, path.size() - 1); | ||
| 850 | |||
| 851 | return path; | ||
| 852 | } | ||
| 853 | |||
| 854 | IOFile::IOFile() {} | 802 | IOFile::IOFile() {} |
| 855 | 803 | ||
| 856 | IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { | 804 | IOFile::IOFile(const std::string& filename, const char openmode[], int flags) { |
| @@ -872,6 +820,7 @@ IOFile& IOFile::operator=(IOFile&& other) noexcept { | |||
| 872 | 820 | ||
| 873 | void IOFile::Swap(IOFile& other) noexcept { | 821 | void IOFile::Swap(IOFile& other) noexcept { |
| 874 | std::swap(m_file, other.m_file); | 822 | std::swap(m_file, other.m_file); |
| 823 | std::swap(m_good, other.m_good); | ||
| 875 | } | 824 | } |
| 876 | 825 | ||
| 877 | bool IOFile::Open(const std::string& filename, const char openmode[], int flags) { | 826 | bool IOFile::Open(const std::string& filename, const char openmode[], int flags) { |
| @@ -888,15 +837,16 @@ bool IOFile::Open(const std::string& filename, const char openmode[], int flags) | |||
| 888 | m_file = fopen(filename.c_str(), openmode); | 837 | m_file = fopen(filename.c_str(), openmode); |
| 889 | #endif | 838 | #endif |
| 890 | 839 | ||
| 891 | return IsOpen(); | 840 | m_good = IsOpen(); |
| 841 | return m_good; | ||
| 892 | } | 842 | } |
| 893 | 843 | ||
| 894 | bool IOFile::Close() { | 844 | bool IOFile::Close() { |
| 895 | if (!IsOpen() || 0 != std::fclose(m_file)) | 845 | if (!IsOpen() || 0 != std::fclose(m_file)) |
| 896 | return false; | 846 | m_good = false; |
| 897 | 847 | ||
| 898 | m_file = nullptr; | 848 | m_file = nullptr; |
| 899 | return true; | 849 | return m_good; |
| 900 | } | 850 | } |
| 901 | 851 | ||
| 902 | u64 IOFile::GetSize() const { | 852 | u64 IOFile::GetSize() const { |
| @@ -906,8 +856,11 @@ u64 IOFile::GetSize() const { | |||
| 906 | return 0; | 856 | return 0; |
| 907 | } | 857 | } |
| 908 | 858 | ||
| 909 | bool IOFile::Seek(s64 off, int origin) const { | 859 | bool IOFile::Seek(s64 off, int origin) { |
| 910 | return IsOpen() && 0 == fseeko(m_file, off, origin); | 860 | if (!IsOpen() || 0 != fseeko(m_file, off, origin)) |
| 861 | m_good = false; | ||
| 862 | |||
| 863 | return m_good; | ||
| 911 | } | 864 | } |
| 912 | 865 | ||
| 913 | u64 IOFile::Tell() const { | 866 | u64 IOFile::Tell() const { |
| @@ -918,20 +871,26 @@ u64 IOFile::Tell() const { | |||
| 918 | } | 871 | } |
| 919 | 872 | ||
| 920 | bool IOFile::Flush() { | 873 | bool IOFile::Flush() { |
| 921 | return IsOpen() && 0 == std::fflush(m_file); | 874 | if (!IsOpen() || 0 != std::fflush(m_file)) |
| 875 | m_good = false; | ||
| 876 | |||
| 877 | return m_good; | ||
| 922 | } | 878 | } |
| 923 | 879 | ||
| 924 | bool IOFile::Resize(u64 size) { | 880 | bool IOFile::Resize(u64 size) { |
| 925 | return IsOpen() && 0 == | 881 | if (!IsOpen() || 0 != |
| 926 | #ifdef _WIN32 | 882 | #ifdef _WIN32 |
| 927 | // ector: _chsize sucks, not 64-bit safe | 883 | // ector: _chsize sucks, not 64-bit safe |
| 928 | // F|RES: changed to _chsize_s. i think it is 64-bit safe | 884 | // F|RES: changed to _chsize_s. i think it is 64-bit safe |
| 929 | _chsize_s(_fileno(m_file), size) | 885 | _chsize_s(_fileno(m_file), size) |
| 930 | #else | 886 | #else |
| 931 | // TODO: handle 64bit and growing | 887 | // TODO: handle 64bit and growing |
| 932 | ftruncate(fileno(m_file), size) | 888 | ftruncate(fileno(m_file), size) |
| 933 | #endif | 889 | #endif |
| 934 | ; | 890 | ) |
| 891 | m_good = false; | ||
| 892 | |||
| 893 | return m_good; | ||
| 935 | } | 894 | } |
| 936 | 895 | ||
| 937 | } // namespace FileUtil | 896 | } // namespace FileUtil |
diff --git a/src/common/file_util.h b/src/common/file_util.h index 9bb3c4109..5bc7fbf7c 100644 --- a/src/common/file_util.h +++ b/src/common/file_util.h | |||
| @@ -150,31 +150,6 @@ size_t ReadFileToString(bool text_file, const char* filename, std::string& str); | |||
| 150 | void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, | 150 | void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name, |
| 151 | std::array<char, 4>& extension); | 151 | std::array<char, 4>& extension); |
| 152 | 152 | ||
| 153 | // Splits the path on '/' or '\' and put the components into a vector | ||
| 154 | // i.e. "C:\Users\Yuzu\Documents\save.bin" becomes {"C:", "Users", "Yuzu", "Documents", "save.bin" } | ||
| 155 | std::vector<std::string> SplitPathComponents(const std::string& filename); | ||
| 156 | |||
| 157 | // Gets all of the text prior to the last '/' or '\' in the path. | ||
| 158 | std::string GetParentPath(const std::string& path); | ||
| 159 | |||
| 160 | // Gets the filename of the path | ||
| 161 | std::string GetFilename(std::string path); | ||
| 162 | |||
| 163 | // Gets the extension of the filename | ||
| 164 | std::string GetExtensionFromFilename(const std::string& name); | ||
| 165 | |||
| 166 | // Removes the final '/' or '\' if one exists | ||
| 167 | std::string RemoveTrailingSlash(const std::string& path); | ||
| 168 | |||
| 169 | // Creates a new vector containing indices [first, last) from the original. | ||
| 170 | template <typename T> | ||
| 171 | std::vector<T> SliceVector(const std::vector<T>& vector, size_t first, size_t last) { | ||
| 172 | if (first >= last) | ||
| 173 | return {}; | ||
| 174 | last = std::min<size_t>(last, vector.size()); | ||
| 175 | return std::vector<T>(vector.begin() + first, vector.begin() + first + last); | ||
| 176 | } | ||
| 177 | |||
| 178 | // simple wrapper for cstdlib file functions to | 153 | // simple wrapper for cstdlib file functions to |
| 179 | // hopefully will make error checking easier | 154 | // hopefully will make error checking easier |
| 180 | // and make forgetting an fclose() harder | 155 | // and make forgetting an fclose() harder |
| @@ -197,27 +172,41 @@ public: | |||
| 197 | bool Close(); | 172 | bool Close(); |
| 198 | 173 | ||
| 199 | template <typename T> | 174 | template <typename T> |
| 200 | size_t ReadArray(T* data, size_t length) const { | 175 | size_t ReadArray(T* data, size_t length) { |
| 201 | static_assert(std::is_trivially_copyable<T>(), | 176 | static_assert(std::is_trivially_copyable<T>(), |
| 202 | "Given array does not consist of trivially copyable objects"); | 177 | "Given array does not consist of trivially copyable objects"); |
| 203 | 178 | ||
| 204 | if (!IsOpen()) | 179 | if (!IsOpen()) { |
| 180 | m_good = false; | ||
| 205 | return -1; | 181 | return -1; |
| 182 | } | ||
| 206 | 183 | ||
| 207 | return std::fread(data, sizeof(T), length, m_file); | 184 | size_t items_read = std::fread(data, sizeof(T), length, m_file); |
| 185 | if (items_read != length) | ||
| 186 | m_good = false; | ||
| 187 | |||
| 188 | return items_read; | ||
| 208 | } | 189 | } |
| 209 | 190 | ||
| 210 | template <typename T> | 191 | template <typename T> |
| 211 | size_t WriteArray(const T* data, size_t length) { | 192 | size_t WriteArray(const T* data, size_t length) { |
| 212 | static_assert(std::is_trivially_copyable<T>(), | 193 | static_assert(std::is_trivially_copyable<T>(), |
| 213 | "Given array does not consist of trivially copyable objects"); | 194 | "Given array does not consist of trivially copyable objects"); |
| 214 | if (!IsOpen()) | 195 | |
| 196 | if (!IsOpen()) { | ||
| 197 | m_good = false; | ||
| 215 | return -1; | 198 | return -1; |
| 216 | return std::fwrite(data, sizeof(T), length, m_file); | 199 | } |
| 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; | ||
| 217 | } | 206 | } |
| 218 | 207 | ||
| 219 | template <typename T> | 208 | template <typename T> |
| 220 | size_t ReadBytes(T* data, size_t length) const { | 209 | size_t ReadBytes(T* data, size_t length) { |
| 221 | static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable"); | 210 | static_assert(std::is_trivially_copyable<T>(), "T must be trivially copyable"); |
| 222 | return ReadArray(reinterpret_cast<char*>(data), length); | 211 | return ReadArray(reinterpret_cast<char*>(data), length); |
| 223 | } | 212 | } |
| @@ -242,7 +231,15 @@ public: | |||
| 242 | return nullptr != m_file; | 231 | return nullptr != m_file; |
| 243 | } | 232 | } |
| 244 | 233 | ||
| 245 | bool Seek(s64 off, int origin) const; | 234 | // m_good is set to false when a read, write or other function fails |
| 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); | ||
| 246 | u64 Tell() const; | 243 | u64 Tell() const; |
| 247 | u64 GetSize() const; | 244 | u64 GetSize() const; |
| 248 | bool Resize(u64 size); | 245 | bool Resize(u64 size); |
| @@ -250,11 +247,13 @@ public: | |||
| 250 | 247 | ||
| 251 | // clear error state | 248 | // clear error state |
| 252 | void Clear() { | 249 | void Clear() { |
| 250 | m_good = true; | ||
| 253 | std::clearerr(m_file); | 251 | std::clearerr(m_file); |
| 254 | } | 252 | } |
| 255 | 253 | ||
| 256 | private: | 254 | private: |
| 257 | std::FILE* m_file = nullptr; | 255 | std::FILE* m_file = nullptr; |
| 256 | bool m_good = true; | ||
| 258 | }; | 257 | }; |
| 259 | 258 | ||
| 260 | } // namespace FileUtil | 259 | } // namespace FileUtil |
diff --git a/src/common/memory_util.cpp b/src/common/memory_util.cpp index 5d89209ed..09462ccee 100644 --- a/src/common/memory_util.cpp +++ b/src/common/memory_util.cpp | |||
| @@ -16,7 +16,7 @@ | |||
| 16 | #include <sys/mman.h> | 16 | #include <sys/mman.h> |
| 17 | #endif | 17 | #endif |
| 18 | 18 | ||
| 19 | #if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT) | 19 | #if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT) |
| 20 | #include <unistd.h> | 20 | #include <unistd.h> |
| 21 | #define PAGE_MASK (getpagesize() - 1) | 21 | #define PAGE_MASK (getpagesize() - 1) |
| 22 | #define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK)) | 22 | #define round_page(x) ((((unsigned long)(x)) + PAGE_MASK) & ~(PAGE_MASK)) |
| @@ -30,7 +30,7 @@ void* AllocateExecutableMemory(size_t size, bool low) { | |||
| 30 | void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); | 30 | void* ptr = VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); |
| 31 | #else | 31 | #else |
| 32 | static char* map_hint = nullptr; | 32 | static char* map_hint = nullptr; |
| 33 | #if defined(ARCHITECTURE_X64) && !defined(MAP_32BIT) | 33 | #if defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT) |
| 34 | // This OS has no flag to enforce allocation below the 4 GB boundary, | 34 | // This OS has no flag to enforce allocation below the 4 GB boundary, |
| 35 | // but if we hint that we want a low address it is very likely we will | 35 | // but if we hint that we want a low address it is very likely we will |
| 36 | // get one. | 36 | // get one. |
| @@ -42,7 +42,7 @@ void* AllocateExecutableMemory(size_t size, bool low) { | |||
| 42 | #endif | 42 | #endif |
| 43 | void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, | 43 | void* ptr = mmap(map_hint, size, PROT_READ | PROT_WRITE | PROT_EXEC, |
| 44 | MAP_ANON | MAP_PRIVATE | 44 | MAP_ANON | MAP_PRIVATE |
| 45 | #if defined(ARCHITECTURE_X64) && defined(MAP_32BIT) | 45 | #if defined(ARCHITECTURE_x86_64) && defined(MAP_32BIT) |
| 46 | | (low ? MAP_32BIT : 0) | 46 | | (low ? MAP_32BIT : 0) |
| 47 | #endif | 47 | #endif |
| 48 | , | 48 | , |
| @@ -57,7 +57,7 @@ void* AllocateExecutableMemory(size_t size, bool low) { | |||
| 57 | #endif | 57 | #endif |
| 58 | LOG_ERROR(Common_Memory, "Failed to allocate executable memory"); | 58 | LOG_ERROR(Common_Memory, "Failed to allocate executable memory"); |
| 59 | } | 59 | } |
| 60 | #if !defined(_WIN32) && defined(ARCHITECTURE_X64) && !defined(MAP_32BIT) | 60 | #if !defined(_WIN32) && defined(ARCHITECTURE_x86_64) && !defined(MAP_32BIT) |
| 61 | else { | 61 | else { |
| 62 | if (low) { | 62 | if (low) { |
| 63 | map_hint += size; | 63 | map_hint += size; |
diff --git a/src/common/swap.h b/src/common/swap.h index 4a4012d1a..f025f7450 100644 --- a/src/common/swap.h +++ b/src/common/swap.h | |||
| @@ -69,7 +69,7 @@ inline u32 swap32(u32 _data) { | |||
| 69 | inline u64 swap64(u64 _data) { | 69 | inline u64 swap64(u64 _data) { |
| 70 | return _byteswap_uint64(_data); | 70 | return _byteswap_uint64(_data); |
| 71 | } | 71 | } |
| 72 | #elif _M_ARM | 72 | #elif ARCHITECTURE_ARM |
| 73 | inline u16 swap16(u16 _data) { | 73 | inline u16 swap16(u16 _data) { |
| 74 | u32 data = _data; | 74 | u32 data = _data; |
| 75 | __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data)); | 75 | __asm__("rev16 %0, %1\n" : "=l"(data) : "l"(data)); |
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index 582294a57..3dff068df 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt | |||
| @@ -8,9 +8,9 @@ add_library(core STATIC | |||
| 8 | core_cpu.h | 8 | core_cpu.h |
| 9 | core_timing.cpp | 9 | core_timing.cpp |
| 10 | core_timing.h | 10 | core_timing.h |
| 11 | file_sys/content_archive.cpp | ||
| 12 | file_sys/content_archive.h | ||
| 13 | file_sys/directory.h | 11 | file_sys/directory.h |
| 12 | file_sys/disk_filesystem.cpp | ||
| 13 | file_sys/disk_filesystem.h | ||
| 14 | file_sys/errors.h | 14 | file_sys/errors.h |
| 15 | file_sys/filesystem.cpp | 15 | file_sys/filesystem.cpp |
| 16 | file_sys/filesystem.h | 16 | file_sys/filesystem.h |
| @@ -20,13 +20,15 @@ add_library(core STATIC | |||
| 20 | file_sys/path_parser.h | 20 | file_sys/path_parser.h |
| 21 | file_sys/program_metadata.cpp | 21 | file_sys/program_metadata.cpp |
| 22 | file_sys/program_metadata.h | 22 | file_sys/program_metadata.h |
| 23 | file_sys/romfs_factory.cpp | ||
| 24 | file_sys/romfs_factory.h | ||
| 25 | file_sys/romfs_filesystem.cpp | ||
| 26 | file_sys/romfs_filesystem.h | ||
| 27 | file_sys/savedata_factory.cpp | ||
| 28 | file_sys/savedata_factory.h | ||
| 29 | file_sys/sdmc_factory.cpp | ||
| 30 | file_sys/sdmc_factory.h | ||
| 23 | file_sys/storage.h | 31 | file_sys/storage.h |
| 24 | file_sys/vfs.cpp | ||
| 25 | file_sys/vfs.h | ||
| 26 | file_sys/vfs_offset.cpp | ||
| 27 | file_sys/vfs_offset.h | ||
| 28 | file_sys/vfs_real.cpp | ||
| 29 | file_sys/vfs_real.h | ||
| 30 | frontend/emu_window.cpp | 32 | frontend/emu_window.cpp |
| 31 | frontend/emu_window.h | 33 | frontend/emu_window.h |
| 32 | frontend/framebuffer_layout.cpp | 34 | frontend/framebuffer_layout.cpp |
diff --git a/src/core/core.cpp b/src/core/core.cpp index 82db5cccf..8335d502e 100644 --- a/src/core/core.cpp +++ b/src/core/core.cpp | |||
| @@ -19,7 +19,6 @@ | |||
| 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" | ||
| 23 | #include "video_core/video_core.h" | 22 | #include "video_core/video_core.h" |
| 24 | 23 | ||
| 25 | namespace Core { | 24 | namespace Core { |
| @@ -85,7 +84,7 @@ System::ResultStatus System::SingleStep() { | |||
| 85 | } | 84 | } |
| 86 | 85 | ||
| 87 | System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) { | 86 | System::ResultStatus System::Load(EmuWindow* emu_window, const std::string& filepath) { |
| 88 | app_loader = Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(filepath)); | 87 | app_loader = Loader::GetLoader(filepath); |
| 89 | 88 | ||
| 90 | if (!app_loader) { | 89 | if (!app_loader) { |
| 91 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); | 90 | LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath); |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp deleted file mode 100644 index b45b83a26..000000000 --- a/src/core/file_sys/content_archive.cpp +++ /dev/null | |||
| @@ -1,164 +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 "common/logging/log.h" | ||
| 6 | #include "core/file_sys/content_archive.h" | ||
| 7 | #include "core/file_sys/vfs_offset.h" | ||
| 8 | #include "core/loader/loader.h" | ||
| 9 | |||
| 10 | namespace FileSys { | ||
| 11 | |||
| 12 | // Media offsets in headers are stored divided by 512. Mult. by this to get real offset. | ||
| 13 | constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200; | ||
| 14 | |||
| 15 | constexpr u64 SECTION_HEADER_SIZE = 0x200; | ||
| 16 | constexpr u64 SECTION_HEADER_OFFSET = 0x400; | ||
| 17 | |||
| 18 | constexpr u32 IVFC_MAX_LEVEL = 6; | ||
| 19 | |||
| 20 | enum class NCASectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 }; | ||
| 21 | |||
| 22 | struct NCASectionHeaderBlock { | ||
| 23 | INSERT_PADDING_BYTES(3); | ||
| 24 | NCASectionFilesystemType filesystem_type; | ||
| 25 | u8 crypto_type; | ||
| 26 | INSERT_PADDING_BYTES(3); | ||
| 27 | }; | ||
| 28 | static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size."); | ||
| 29 | |||
| 30 | struct PFS0Superblock { | ||
| 31 | NCASectionHeaderBlock header_block; | ||
| 32 | std::array<u8, 0x20> hash; | ||
| 33 | u32_le size; | ||
| 34 | INSERT_PADDING_BYTES(4); | ||
| 35 | u64_le hash_table_offset; | ||
| 36 | u64_le hash_table_size; | ||
| 37 | u64_le pfs0_header_offset; | ||
| 38 | u64_le pfs0_size; | ||
| 39 | INSERT_PADDING_BYTES(432); | ||
| 40 | }; | ||
| 41 | static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size."); | ||
| 42 | |||
| 43 | struct IVFCLevel { | ||
| 44 | u64_le offset; | ||
| 45 | u64_le size; | ||
| 46 | u32_le block_size; | ||
| 47 | u32_le reserved; | ||
| 48 | }; | ||
| 49 | static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size."); | ||
| 50 | |||
| 51 | struct RomFSSuperblock { | ||
| 52 | NCASectionHeaderBlock header_block; | ||
| 53 | u32_le magic; | ||
| 54 | u32_le magic_number; | ||
| 55 | INSERT_PADDING_BYTES(8); | ||
| 56 | std::array<IVFCLevel, 6> levels; | ||
| 57 | INSERT_PADDING_BYTES(64); | ||
| 58 | }; | ||
| 59 | static_assert(sizeof(RomFSSuperblock) == 0xE8, "RomFSSuperblock has incorrect size."); | ||
| 60 | |||
| 61 | NCA::NCA(VirtualFile file_) : file(file_) { | ||
| 62 | if (sizeof(NCAHeader) != file->ReadObject(&header)) | ||
| 63 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 64 | |||
| 65 | if (!IsValidNCA(header)) { | ||
| 66 | status = Loader::ResultStatus::ErrorInvalidFormat; | ||
| 67 | return; | ||
| 68 | } | ||
| 69 | |||
| 70 | std::ptrdiff_t number_sections = | ||
| 71 | std::count_if(std::begin(header.section_tables), std::end(header.section_tables), | ||
| 72 | [](NCASectionTableEntry entry) { return entry.media_offset > 0; }); | ||
| 73 | |||
| 74 | for (std::ptrdiff_t i = 0; i < number_sections; ++i) { | ||
| 75 | // Seek to beginning of this section. | ||
| 76 | NCASectionHeaderBlock block{}; | ||
| 77 | if (sizeof(NCASectionHeaderBlock) != | ||
| 78 | file->ReadObject(&block, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) | ||
| 79 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 80 | |||
| 81 | if (block.filesystem_type == NCASectionFilesystemType::ROMFS) { | ||
| 82 | RomFSSuperblock sb{}; | ||
| 83 | if (sizeof(RomFSSuperblock) != | ||
| 84 | file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) | ||
| 85 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 86 | |||
| 87 | const size_t romfs_offset = | ||
| 88 | header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + | ||
| 89 | sb.levels[IVFC_MAX_LEVEL - 1].offset; | ||
| 90 | const size_t romfs_size = sb.levels[IVFC_MAX_LEVEL - 1].size; | ||
| 91 | files.emplace_back(std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset)); | ||
| 92 | romfs = files.back(); | ||
| 93 | } else if (block.filesystem_type == NCASectionFilesystemType::PFS0) { | ||
| 94 | PFS0Superblock sb{}; | ||
| 95 | // Seek back to beginning of this section. | ||
| 96 | if (sizeof(PFS0Superblock) != | ||
| 97 | file->ReadObject(&sb, SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE)) | ||
| 98 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 99 | |||
| 100 | u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * | ||
| 101 | MEDIA_OFFSET_MULTIPLIER) + | ||
| 102 | sb.pfs0_header_offset; | ||
| 103 | u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset - | ||
| 104 | header.section_tables[i].media_offset); | ||
| 105 | auto npfs = std::make_shared<PartitionFilesystem>( | ||
| 106 | std::make_shared<OffsetVfsFile>(file, size, offset)); | ||
| 107 | |||
| 108 | if (npfs->GetStatus() == Loader::ResultStatus::Success) { | ||
| 109 | dirs.emplace_back(npfs); | ||
| 110 | if (IsDirectoryExeFS(dirs.back())) | ||
| 111 | exefs = dirs.back(); | ||
| 112 | } | ||
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 | status = Loader::ResultStatus::Success; | ||
| 117 | } | ||
| 118 | |||
| 119 | Loader::ResultStatus NCA::GetStatus() const { | ||
| 120 | return status; | ||
| 121 | } | ||
| 122 | |||
| 123 | std::vector<std::shared_ptr<VfsFile>> NCA::GetFiles() const { | ||
| 124 | if (status != Loader::ResultStatus::Success) | ||
| 125 | return {}; | ||
| 126 | return files; | ||
| 127 | } | ||
| 128 | |||
| 129 | std::vector<std::shared_ptr<VfsDirectory>> NCA::GetSubdirectories() const { | ||
| 130 | if (status != Loader::ResultStatus::Success) | ||
| 131 | return {}; | ||
| 132 | return dirs; | ||
| 133 | } | ||
| 134 | |||
| 135 | std::string NCA::GetName() const { | ||
| 136 | return file->GetName(); | ||
| 137 | } | ||
| 138 | |||
| 139 | std::shared_ptr<VfsDirectory> NCA::GetParentDirectory() const { | ||
| 140 | return file->GetContainingDirectory(); | ||
| 141 | } | ||
| 142 | |||
| 143 | NCAContentType NCA::GetType() const { | ||
| 144 | return header.content_type; | ||
| 145 | } | ||
| 146 | |||
| 147 | u64 NCA::GetTitleId() const { | ||
| 148 | if (status != Loader::ResultStatus::Success) | ||
| 149 | return {}; | ||
| 150 | return header.title_id; | ||
| 151 | } | ||
| 152 | |||
| 153 | VirtualFile NCA::GetRomFS() const { | ||
| 154 | return romfs; | ||
| 155 | } | ||
| 156 | |||
| 157 | VirtualDir NCA::GetExeFS() const { | ||
| 158 | return exefs; | ||
| 159 | } | ||
| 160 | |||
| 161 | bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 162 | return false; | ||
| 163 | } | ||
| 164 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h deleted file mode 100644 index eb4ca1c18..000000000 --- a/src/core/file_sys/content_archive.h +++ /dev/null | |||
| @@ -1,89 +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 "common/common_funcs.h" | ||
| 8 | #include "common/common_types.h" | ||
| 9 | #include "common/swap.h" | ||
| 10 | #include "core/file_sys/partition_filesystem.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | enum class NCAContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 }; | ||
| 15 | |||
| 16 | struct NCASectionTableEntry { | ||
| 17 | u32_le media_offset; | ||
| 18 | u32_le media_end_offset; | ||
| 19 | INSERT_PADDING_BYTES(0x8); | ||
| 20 | }; | ||
| 21 | static_assert(sizeof(NCASectionTableEntry) == 0x10, "NCASectionTableEntry has incorrect size."); | ||
| 22 | |||
| 23 | struct NCAHeader { | ||
| 24 | std::array<u8, 0x100> rsa_signature_1; | ||
| 25 | std::array<u8, 0x100> rsa_signature_2; | ||
| 26 | u32_le magic; | ||
| 27 | u8 is_system; | ||
| 28 | NCAContentType content_type; | ||
| 29 | u8 crypto_type; | ||
| 30 | u8 key_index; | ||
| 31 | u64_le size; | ||
| 32 | u64_le title_id; | ||
| 33 | INSERT_PADDING_BYTES(0x4); | ||
| 34 | u32_le sdk_version; | ||
| 35 | u8 crypto_type_2; | ||
| 36 | INSERT_PADDING_BYTES(15); | ||
| 37 | std::array<u8, 0x10> rights_id; | ||
| 38 | std::array<NCASectionTableEntry, 0x4> section_tables; | ||
| 39 | std::array<std::array<u8, 0x20>, 0x4> hash_tables; | ||
| 40 | std::array<std::array<u8, 0x10>, 0x4> key_area; | ||
| 41 | INSERT_PADDING_BYTES(0xC0); | ||
| 42 | }; | ||
| 43 | static_assert(sizeof(NCAHeader) == 0x400, "NCAHeader has incorrect size."); | ||
| 44 | |||
| 45 | static bool IsDirectoryExeFS(std::shared_ptr<FileSys::VfsDirectory> pfs) { | ||
| 46 | // According to switchbrew, an exefs must only contain these two files: | ||
| 47 | return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; | ||
| 48 | } | ||
| 49 | |||
| 50 | static bool IsValidNCA(const NCAHeader& header) { | ||
| 51 | return header.magic == Common::MakeMagic('N', 'C', 'A', '2') || | ||
| 52 | header.magic == Common::MakeMagic('N', 'C', 'A', '3'); | ||
| 53 | } | ||
| 54 | |||
| 55 | // An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. | ||
| 56 | // After construction, use GetStatus to determine if the file is valid and ready to be used. | ||
| 57 | class NCA : public ReadOnlyVfsDirectory { | ||
| 58 | public: | ||
| 59 | explicit NCA(VirtualFile file); | ||
| 60 | Loader::ResultStatus GetStatus() const; | ||
| 61 | |||
| 62 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | ||
| 63 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | ||
| 64 | std::string GetName() const override; | ||
| 65 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | ||
| 66 | |||
| 67 | NCAContentType GetType() const; | ||
| 68 | u64 GetTitleId() const; | ||
| 69 | |||
| 70 | VirtualFile GetRomFS() const; | ||
| 71 | VirtualDir GetExeFS() const; | ||
| 72 | |||
| 73 | protected: | ||
| 74 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 75 | |||
| 76 | private: | ||
| 77 | std::vector<VirtualDir> dirs; | ||
| 78 | std::vector<VirtualFile> files; | ||
| 79 | |||
| 80 | VirtualFile romfs = nullptr; | ||
| 81 | VirtualDir exefs = nullptr; | ||
| 82 | VirtualFile file; | ||
| 83 | |||
| 84 | NCAHeader header{}; | ||
| 85 | |||
| 86 | Loader::ResultStatus status{}; | ||
| 87 | }; | ||
| 88 | |||
| 89 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/disk_filesystem.cpp b/src/core/file_sys/disk_filesystem.cpp new file mode 100644 index 000000000..8c6f15bb5 --- /dev/null +++ b/src/core/file_sys/disk_filesystem.cpp | |||
| @@ -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 | #include <cstring> | ||
| 6 | #include <memory> | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/file_sys/disk_filesystem.h" | ||
| 10 | #include "core/file_sys/errors.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | static std::string ModeFlagsToString(Mode mode) { | ||
| 15 | std::string mode_str; | ||
| 16 | u32 mode_flags = static_cast<u32>(mode); | ||
| 17 | |||
| 18 | // Calculate the correct open mode for the file. | ||
| 19 | if ((mode_flags & static_cast<u32>(Mode::Read)) && | ||
| 20 | (mode_flags & static_cast<u32>(Mode::Write))) { | ||
| 21 | if (mode_flags & static_cast<u32>(Mode::Append)) | ||
| 22 | mode_str = "a+"; | ||
| 23 | else | ||
| 24 | mode_str = "r+"; | ||
| 25 | } else { | ||
| 26 | if (mode_flags & static_cast<u32>(Mode::Read)) | ||
| 27 | mode_str = "r"; | ||
| 28 | else if (mode_flags & static_cast<u32>(Mode::Append)) | ||
| 29 | mode_str = "a"; | ||
| 30 | else if (mode_flags & static_cast<u32>(Mode::Write)) | ||
| 31 | mode_str = "w"; | ||
| 32 | } | ||
| 33 | |||
| 34 | mode_str += "b"; | ||
| 35 | |||
| 36 | return mode_str; | ||
| 37 | } | ||
| 38 | |||
| 39 | std::string Disk_FileSystem::GetName() const { | ||
| 40 | return "Disk"; | ||
| 41 | } | ||
| 42 | |||
| 43 | ResultVal<std::unique_ptr<StorageBackend>> Disk_FileSystem::OpenFile(const std::string& path, | ||
| 44 | Mode mode) const { | ||
| 45 | |||
| 46 | // Calculate the correct open mode for the file. | ||
| 47 | std::string mode_str = ModeFlagsToString(mode); | ||
| 48 | |||
| 49 | std::string full_path = base_directory + path; | ||
| 50 | auto file = std::make_shared<FileUtil::IOFile>(full_path, mode_str.c_str()); | ||
| 51 | |||
| 52 | if (!file->IsOpen()) { | ||
| 53 | return ERROR_PATH_NOT_FOUND; | ||
| 54 | } | ||
| 55 | |||
| 56 | return MakeResult<std::unique_ptr<StorageBackend>>( | ||
| 57 | std::make_unique<Disk_Storage>(std::move(file))); | ||
| 58 | } | ||
| 59 | |||
| 60 | ResultCode Disk_FileSystem::DeleteFile(const std::string& path) const { | ||
| 61 | if (!FileUtil::Exists(path)) { | ||
| 62 | return ERROR_PATH_NOT_FOUND; | ||
| 63 | } | ||
| 64 | |||
| 65 | FileUtil::Delete(path); | ||
| 66 | |||
| 67 | return RESULT_SUCCESS; | ||
| 68 | } | ||
| 69 | |||
| 70 | ResultCode Disk_FileSystem::RenameFile(const std::string& src_path, | ||
| 71 | const std::string& dest_path) const { | ||
| 72 | const std::string full_src_path = base_directory + src_path; | ||
| 73 | const std::string full_dest_path = base_directory + dest_path; | ||
| 74 | |||
| 75 | if (!FileUtil::Exists(full_src_path)) { | ||
| 76 | return ERROR_PATH_NOT_FOUND; | ||
| 77 | } | ||
| 78 | // TODO(wwylele): Use correct error code | ||
| 79 | return FileUtil::Rename(full_src_path, full_dest_path) ? RESULT_SUCCESS : ResultCode(-1); | ||
| 80 | } | ||
| 81 | |||
| 82 | ResultCode Disk_FileSystem::DeleteDirectory(const Path& path) const { | ||
| 83 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 84 | // TODO(wwylele): Use correct error code | ||
| 85 | return ResultCode(-1); | ||
| 86 | } | ||
| 87 | |||
| 88 | ResultCode Disk_FileSystem::DeleteDirectoryRecursively(const Path& path) const { | ||
| 89 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 90 | // TODO(wwylele): Use correct error code | ||
| 91 | return ResultCode(-1); | ||
| 92 | } | ||
| 93 | |||
| 94 | ResultCode Disk_FileSystem::CreateFile(const std::string& path, u64 size) const { | ||
| 95 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 96 | |||
| 97 | std::string full_path = base_directory + path; | ||
| 98 | if (size == 0) { | ||
| 99 | FileUtil::CreateEmptyFile(full_path); | ||
| 100 | return RESULT_SUCCESS; | ||
| 101 | } | ||
| 102 | |||
| 103 | FileUtil::IOFile file(full_path, "wb"); | ||
| 104 | // Creates a sparse file (or a normal file on filesystems without the concept of sparse files) | ||
| 105 | // We do this by seeking to the right size, then writing a single null byte. | ||
| 106 | if (file.Seek(size - 1, SEEK_SET) && file.WriteBytes("", 1) == 1) { | ||
| 107 | return RESULT_SUCCESS; | ||
| 108 | } | ||
| 109 | |||
| 110 | LOG_ERROR(Service_FS, "Too large file"); | ||
| 111 | // TODO(Subv): Find out the correct error code | ||
| 112 | return ResultCode(-1); | ||
| 113 | } | ||
| 114 | |||
| 115 | ResultCode Disk_FileSystem::CreateDirectory(const std::string& path) const { | ||
| 116 | // TODO(Subv): Perform path validation to prevent escaping the emulator sandbox. | ||
| 117 | std::string full_path = base_directory + path; | ||
| 118 | |||
| 119 | if (FileUtil::CreateDir(full_path)) { | ||
| 120 | return RESULT_SUCCESS; | ||
| 121 | } | ||
| 122 | |||
| 123 | LOG_CRITICAL(Service_FS, "(unreachable) Unknown error creating {}", full_path); | ||
| 124 | // TODO(wwylele): Use correct error code | ||
| 125 | return ResultCode(-1); | ||
| 126 | } | ||
| 127 | |||
| 128 | ResultCode Disk_FileSystem::RenameDirectory(const Path& src_path, const Path& dest_path) const { | ||
| 129 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 130 | // TODO(wwylele): Use correct error code | ||
| 131 | return ResultCode(-1); | ||
| 132 | } | ||
| 133 | |||
| 134 | ResultVal<std::unique_ptr<DirectoryBackend>> Disk_FileSystem::OpenDirectory( | ||
| 135 | const std::string& path) const { | ||
| 136 | |||
| 137 | std::string full_path = base_directory + path; | ||
| 138 | |||
| 139 | if (!FileUtil::IsDirectory(full_path)) { | ||
| 140 | // TODO(Subv): Find the correct error code for this. | ||
| 141 | return ResultCode(-1); | ||
| 142 | } | ||
| 143 | |||
| 144 | auto directory = std::make_unique<Disk_Directory>(full_path); | ||
| 145 | return MakeResult<std::unique_ptr<DirectoryBackend>>(std::move(directory)); | ||
| 146 | } | ||
| 147 | |||
| 148 | u64 Disk_FileSystem::GetFreeSpaceSize() const { | ||
| 149 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 150 | return 0; | ||
| 151 | } | ||
| 152 | |||
| 153 | ResultVal<FileSys::EntryType> Disk_FileSystem::GetEntryType(const std::string& path) const { | ||
| 154 | std::string full_path = base_directory + path; | ||
| 155 | if (!FileUtil::Exists(full_path)) { | ||
| 156 | return ERROR_PATH_NOT_FOUND; | ||
| 157 | } | ||
| 158 | |||
| 159 | if (FileUtil::IsDirectory(full_path)) | ||
| 160 | return MakeResult(EntryType::Directory); | ||
| 161 | |||
| 162 | return MakeResult(EntryType::File); | ||
| 163 | } | ||
| 164 | |||
| 165 | ResultVal<size_t> Disk_Storage::Read(const u64 offset, const size_t length, u8* buffer) const { | ||
| 166 | LOG_TRACE(Service_FS, "called offset={}, length={}", offset, length); | ||
| 167 | file->Seek(offset, SEEK_SET); | ||
| 168 | return MakeResult<size_t>(file->ReadBytes(buffer, length)); | ||
| 169 | } | ||
| 170 | |||
| 171 | ResultVal<size_t> Disk_Storage::Write(const u64 offset, const size_t length, const bool flush, | ||
| 172 | const u8* buffer) const { | ||
| 173 | LOG_WARNING(Service_FS, "(STUBBED) called"); | ||
| 174 | file->Seek(offset, SEEK_SET); | ||
| 175 | size_t written = file->WriteBytes(buffer, length); | ||
| 176 | if (flush) { | ||
| 177 | file->Flush(); | ||
| 178 | } | ||
| 179 | return MakeResult<size_t>(written); | ||
| 180 | } | ||
| 181 | |||
| 182 | u64 Disk_Storage::GetSize() const { | ||
| 183 | return file->GetSize(); | ||
| 184 | } | ||
| 185 | |||
| 186 | bool Disk_Storage::SetSize(const u64 size) const { | ||
| 187 | file->Resize(size); | ||
| 188 | file->Flush(); | ||
| 189 | return true; | ||
| 190 | } | ||
| 191 | |||
| 192 | Disk_Directory::Disk_Directory(const std::string& path) { | ||
| 193 | unsigned size = FileUtil::ScanDirectoryTree(path, directory); | ||
| 194 | directory.size = size; | ||
| 195 | directory.isDirectory = true; | ||
| 196 | children_iterator = directory.children.begin(); | ||
| 197 | } | ||
| 198 | |||
| 199 | u64 Disk_Directory::Read(const u64 count, Entry* entries) { | ||
| 200 | u64 entries_read = 0; | ||
| 201 | |||
| 202 | while (entries_read < count && children_iterator != directory.children.cend()) { | ||
| 203 | const FileUtil::FSTEntry& file = *children_iterator; | ||
| 204 | const std::string& filename = file.virtualName; | ||
| 205 | Entry& entry = entries[entries_read]; | ||
| 206 | |||
| 207 | LOG_TRACE(Service_FS, "File {}: size={} dir={}", filename, file.size, file.isDirectory); | ||
| 208 | |||
| 209 | // TODO(Link Mauve): use a proper conversion to UTF-16. | ||
| 210 | for (size_t j = 0; j < FILENAME_LENGTH; ++j) { | ||
| 211 | entry.filename[j] = filename[j]; | ||
| 212 | if (!filename[j]) | ||
| 213 | break; | ||
| 214 | } | ||
| 215 | |||
| 216 | if (file.isDirectory) { | ||
| 217 | entry.file_size = 0; | ||
| 218 | entry.type = EntryType::Directory; | ||
| 219 | } else { | ||
| 220 | entry.file_size = file.size; | ||
| 221 | entry.type = EntryType::File; | ||
| 222 | } | ||
| 223 | |||
| 224 | ++entries_read; | ||
| 225 | ++children_iterator; | ||
| 226 | } | ||
| 227 | return entries_read; | ||
| 228 | } | ||
| 229 | |||
| 230 | u64 Disk_Directory::GetEntryCount() const { | ||
| 231 | // We convert the children iterator into a const_iterator to allow template argument deduction | ||
| 232 | // in std::distance. | ||
| 233 | std::vector<FileUtil::FSTEntry>::const_iterator current = children_iterator; | ||
| 234 | return std::distance(current, directory.children.end()); | ||
| 235 | } | ||
| 236 | |||
| 237 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/disk_filesystem.h b/src/core/file_sys/disk_filesystem.h new file mode 100644 index 000000000..591e39fda --- /dev/null +++ b/src/core/file_sys/disk_filesystem.h | |||
| @@ -0,0 +1,84 @@ | |||
| 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 0ed7d2a0c..d3e9a3829 100644 --- a/src/core/file_sys/errors.h +++ b/src/core/file_sys/errors.h | |||
| @@ -11,6 +11,7 @@ namespace FileSys { | |||
| 11 | namespace ErrCodes { | 11 | namespace ErrCodes { |
| 12 | enum { | 12 | enum { |
| 13 | NotFound = 1, | 13 | NotFound = 1, |
| 14 | SaveDataNotFound = 1002, | ||
| 14 | }; | 15 | }; |
| 15 | } | 16 | } |
| 16 | 17 | ||
diff --git a/src/core/file_sys/filesystem.h b/src/core/file_sys/filesystem.h index 2d925d9e4..295a3133e 100644 --- a/src/core/file_sys/filesystem.h +++ b/src/core/file_sys/filesystem.h | |||
| @@ -66,4 +66,136 @@ private: | |||
| 66 | std::u16string u16str; | 66 | std::u16string u16str; |
| 67 | }; | 67 | }; |
| 68 | 68 | ||
| 69 | /// Parameters of the archive, as specified in the Create or Format call. | ||
| 70 | struct ArchiveFormatInfo { | ||
| 71 | u32_le total_size; ///< The pre-defined size of the archive. | ||
| 72 | u32_le number_directories; ///< The pre-defined number of directories in the archive. | ||
| 73 | u32_le number_files; ///< The pre-defined number of files in the archive. | ||
| 74 | u8 duplicate_data; ///< Whether the archive should duplicate the data. | ||
| 75 | }; | ||
| 76 | static_assert(std::is_pod<ArchiveFormatInfo>::value, "ArchiveFormatInfo is not POD"); | ||
| 77 | |||
| 78 | class FileSystemBackend : NonCopyable { | ||
| 79 | public: | ||
| 80 | virtual ~FileSystemBackend() {} | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||
| 84 | */ | ||
| 85 | virtual std::string GetName() const = 0; | ||
| 86 | |||
| 87 | /** | ||
| 88 | * Create a file specified by its path | ||
| 89 | * @param path Path relative to the Archive | ||
| 90 | * @param size The size of the new file, filled with zeroes | ||
| 91 | * @return Result of the operation | ||
| 92 | */ | ||
| 93 | virtual ResultCode CreateFile(const std::string& path, u64 size) const = 0; | ||
| 94 | |||
| 95 | /** | ||
| 96 | * Delete a file specified by its path | ||
| 97 | * @param path Path relative to the archive | ||
| 98 | * @return Result of the operation | ||
| 99 | */ | ||
| 100 | virtual ResultCode DeleteFile(const std::string& path) const = 0; | ||
| 101 | |||
| 102 | /** | ||
| 103 | * Create a directory specified by its path | ||
| 104 | * @param path Path relative to the archive | ||
| 105 | * @return Result of the operation | ||
| 106 | */ | ||
| 107 | virtual ResultCode CreateDirectory(const std::string& path) const = 0; | ||
| 108 | |||
| 109 | /** | ||
| 110 | * Delete a directory specified by its path | ||
| 111 | * @param path Path relative to the archive | ||
| 112 | * @return Result of the operation | ||
| 113 | */ | ||
| 114 | virtual ResultCode DeleteDirectory(const Path& path) const = 0; | ||
| 115 | |||
| 116 | /** | ||
| 117 | * Delete a directory specified by its path and anything under it | ||
| 118 | * @param path Path relative to the archive | ||
| 119 | * @return Result of the operation | ||
| 120 | */ | ||
| 121 | virtual ResultCode DeleteDirectoryRecursively(const Path& path) const = 0; | ||
| 122 | |||
| 123 | /** | ||
| 124 | * Rename a File specified by its path | ||
| 125 | * @param src_path Source path relative to the archive | ||
| 126 | * @param dest_path Destination path relative to the archive | ||
| 127 | * @return Result of the operation | ||
| 128 | */ | ||
| 129 | virtual ResultCode RenameFile(const std::string& src_path, | ||
| 130 | const std::string& dest_path) const = 0; | ||
| 131 | |||
| 132 | /** | ||
| 133 | * Rename a Directory specified by its path | ||
| 134 | * @param src_path Source path relative to the archive | ||
| 135 | * @param dest_path Destination path relative to the archive | ||
| 136 | * @return Result of the operation | ||
| 137 | */ | ||
| 138 | virtual ResultCode RenameDirectory(const Path& src_path, const Path& dest_path) const = 0; | ||
| 139 | |||
| 140 | /** | ||
| 141 | * Open a file specified by its path, using the specified mode | ||
| 142 | * @param path Path relative to the archive | ||
| 143 | * @param mode Mode to open the file with | ||
| 144 | * @return Opened file, or error code | ||
| 145 | */ | ||
| 146 | virtual ResultVal<std::unique_ptr<StorageBackend>> OpenFile(const std::string& path, | ||
| 147 | Mode mode) const = 0; | ||
| 148 | |||
| 149 | /** | ||
| 150 | * Open a directory specified by its path | ||
| 151 | * @param path Path relative to the archive | ||
| 152 | * @return Opened directory, or error code | ||
| 153 | */ | ||
| 154 | virtual ResultVal<std::unique_ptr<DirectoryBackend>> OpenDirectory( | ||
| 155 | const std::string& path) const = 0; | ||
| 156 | |||
| 157 | /** | ||
| 158 | * Get the free space | ||
| 159 | * @return The number of free bytes in the archive | ||
| 160 | */ | ||
| 161 | virtual u64 GetFreeSpaceSize() const = 0; | ||
| 162 | |||
| 163 | /** | ||
| 164 | * Get the type of the specified path | ||
| 165 | * @return The type of the specified path or error code | ||
| 166 | */ | ||
| 167 | virtual ResultVal<EntryType> GetEntryType(const std::string& path) const = 0; | ||
| 168 | }; | ||
| 169 | |||
| 170 | class FileSystemFactory : NonCopyable { | ||
| 171 | public: | ||
| 172 | virtual ~FileSystemFactory() {} | ||
| 173 | |||
| 174 | /** | ||
| 175 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||
| 176 | */ | ||
| 177 | virtual std::string GetName() const = 0; | ||
| 178 | |||
| 179 | /** | ||
| 180 | * Tries to open the archive of this type with the specified path | ||
| 181 | * @param path Path to the archive | ||
| 182 | * @return An ArchiveBackend corresponding operating specified archive path. | ||
| 183 | */ | ||
| 184 | virtual ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) = 0; | ||
| 185 | |||
| 186 | /** | ||
| 187 | * Deletes the archive contents and then re-creates the base folder | ||
| 188 | * @param path Path to the archive | ||
| 189 | * @return ResultCode of the operation, 0 on success | ||
| 190 | */ | ||
| 191 | virtual ResultCode Format(const Path& path) = 0; | ||
| 192 | |||
| 193 | /** | ||
| 194 | * Retrieves the format info about the archive with the specified path | ||
| 195 | * @param path Path to the archive | ||
| 196 | * @return Format information about the archive or error code | ||
| 197 | */ | ||
| 198 | virtual ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const = 0; | ||
| 199 | }; | ||
| 200 | |||
| 69 | } // namespace FileSys | 201 | } // namespace FileSys |
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp index 15b1fb946..46d438aca 100644 --- a/src/core/file_sys/partition_filesystem.cpp +++ b/src/core/file_sys/partition_filesystem.cpp | |||
| @@ -6,30 +6,29 @@ | |||
| 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" | ||
| 10 | #include "core/loader/loader.h" | 9 | #include "core/loader/loader.h" |
| 11 | 10 | ||
| 12 | namespace FileSys { | 11 | namespace FileSys { |
| 13 | 12 | ||
| 14 | PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { | 13 | Loader::ResultStatus PartitionFilesystem::Load(const std::string& file_path, size_t offset) { |
| 14 | FileUtil::IOFile file(file_path, "rb"); | ||
| 15 | if (!file.IsOpen()) | ||
| 16 | return Loader::ResultStatus::Error; | ||
| 17 | |||
| 15 | // At least be as large as the header | 18 | // At least be as large as the header |
| 16 | if (file->GetSize() < sizeof(Header)) { | 19 | if (file.GetSize() < sizeof(Header)) |
| 17 | status = Loader::ResultStatus::Error; | 20 | return Loader::ResultStatus::Error; |
| 18 | return; | ||
| 19 | } | ||
| 20 | 21 | ||
| 22 | file.Seek(offset, SEEK_SET); | ||
| 21 | // For cartridges, HFSs can get very large, so we need to calculate the size up to | 23 | // For cartridges, HFSs can get very large, so we need to calculate the size up to |
| 22 | // the actual content itself instead of just blindly reading in the entire file. | 24 | // the actual content itself instead of just blindly reading in the entire file. |
| 23 | Header pfs_header; | 25 | Header pfs_header; |
| 24 | if (sizeof(Header) != file->ReadObject(&pfs_header)) { | 26 | if (!file.ReadBytes(&pfs_header, sizeof(Header))) |
| 25 | status = Loader::ResultStatus::Error; | 27 | return 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 | status = Loader::ResultStatus::ErrorInvalidFormat; | 31 | return Loader::ResultStatus::ErrorInvalidFormat; |
| 32 | return; | ||
| 33 | } | 32 | } |
| 34 | 33 | ||
| 35 | bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); | 34 | bool is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); |
| @@ -39,86 +38,99 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) { | |||
| 39 | sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; | 38 | sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size; |
| 40 | 39 | ||
| 41 | // Actually read in now... | 40 | // Actually read in now... |
| 42 | std::vector<u8> file_data = file->ReadBytes(metadata_size); | 41 | file.Seek(offset, SEEK_SET); |
| 42 | std::vector<u8> file_data(metadata_size); | ||
| 43 | 43 | ||
| 44 | if (file_data.size() != metadata_size) { | 44 | if (!file.ReadBytes(file_data.data(), metadata_size)) |
| 45 | status = Loader::ResultStatus::Error; | 45 | return Loader::ResultStatus::Error; |
| 46 | return; | ||
| 47 | } | ||
| 48 | 46 | ||
| 49 | size_t total_size = file_data.size(); | 47 | Loader::ResultStatus result = Load(file_data); |
| 50 | if (total_size < sizeof(Header)) { | 48 | if (result != Loader::ResultStatus::Success) |
| 51 | status = Loader::ResultStatus::Error; | 49 | LOG_ERROR(Service_FS, "Failed to load PFS from file {}!", file_path); |
| 52 | return; | 50 | |
| 53 | } | 51 | return result; |
| 52 | } | ||
| 54 | 53 | ||
| 55 | memcpy(&pfs_header, file_data.data(), sizeof(Header)); | 54 | Loader::ResultStatus PartitionFilesystem::Load(const std::vector<u8>& file_data, size_t offset) { |
| 55 | size_t total_size = file_data.size() - offset; | ||
| 56 | if (total_size < sizeof(Header)) | ||
| 57 | return Loader::ResultStatus::Error; | ||
| 58 | |||
| 59 | memcpy(&pfs_header, &file_data[offset], sizeof(Header)); | ||
| 56 | if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') && | 60 | if (pfs_header.magic != Common::MakeMagic('H', 'F', 'S', '0') && |
| 57 | pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) { | 61 | pfs_header.magic != Common::MakeMagic('P', 'F', 'S', '0')) { |
| 58 | status = Loader::ResultStatus::ErrorInvalidFormat; | 62 | return Loader::ResultStatus::ErrorInvalidFormat; |
| 59 | return; | ||
| 60 | } | 63 | } |
| 61 | 64 | ||
| 62 | is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); | 65 | is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0'); |
| 63 | 66 | ||
| 64 | size_t entries_offset = sizeof(Header); | 67 | size_t entries_offset = offset + sizeof(Header); |
| 68 | size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry); | ||
| 65 | size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); | 69 | size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size); |
| 66 | content_offset = strtab_offset + pfs_header.strtab_size; | ||
| 67 | for (u16 i = 0; i < pfs_header.num_entries; i++) { | 70 | for (u16 i = 0; i < pfs_header.num_entries; i++) { |
| 68 | FSEntry entry; | 71 | FileEntry entry; |
| 69 | |||
| 70 | memcpy(&entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry)); | ||
| 71 | std::string name( | ||
| 72 | reinterpret_cast<const char*>(&file_data[strtab_offset + entry.strtab_offset])); | ||
| 73 | 72 | ||
| 74 | pfs_files.emplace_back( | 73 | memcpy(&entry.fs_entry, &file_data[entries_offset + (i * entry_size)], sizeof(FSEntry)); |
| 75 | std::make_shared<OffsetVfsFile>(file, entry.size, content_offset + entry.offset, name)); | 74 | entry.name = std::string(reinterpret_cast<const char*>( |
| 75 | &file_data[strtab_offset + entry.fs_entry.strtab_offset])); | ||
| 76 | pfs_entries.push_back(std::move(entry)); | ||
| 76 | } | 77 | } |
| 77 | 78 | ||
| 78 | status = Loader::ResultStatus::Success; | 79 | content_offset = strtab_offset + pfs_header.strtab_size; |
| 79 | } | ||
| 80 | 80 | ||
| 81 | Loader::ResultStatus PartitionFilesystem::GetStatus() const { | 81 | return Loader::ResultStatus::Success; |
| 82 | return status; | ||
| 83 | } | 82 | } |
| 84 | 83 | ||
| 85 | std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const { | 84 | u32 PartitionFilesystem::GetNumEntries() const { |
| 86 | return pfs_files; | 85 | return pfs_header.num_entries; |
| 87 | } | 86 | } |
| 88 | 87 | ||
| 89 | std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const { | 88 | u64 PartitionFilesystem::GetEntryOffset(u32 index) const { |
| 90 | return {}; | 89 | if (index > GetNumEntries()) |
| 90 | return 0; | ||
| 91 | |||
| 92 | return content_offset + pfs_entries[index].fs_entry.offset; | ||
| 91 | } | 93 | } |
| 92 | 94 | ||
| 93 | std::string PartitionFilesystem::GetName() const { | 95 | u64 PartitionFilesystem::GetEntrySize(u32 index) const { |
| 94 | return is_hfs ? "HFS0" : "PFS0"; | 96 | if (index > GetNumEntries()) |
| 97 | return 0; | ||
| 98 | |||
| 99 | return pfs_entries[index].fs_entry.size; | ||
| 95 | } | 100 | } |
| 96 | 101 | ||
| 97 | std::shared_ptr<VfsDirectory> PartitionFilesystem::GetParentDirectory() const { | 102 | std::string PartitionFilesystem::GetEntryName(u32 index) const { |
| 98 | // TODO(DarkLordZach): Add support for nested containers. | 103 | if (index > GetNumEntries()) |
| 99 | return nullptr; | 104 | return ""; |
| 105 | |||
| 106 | return pfs_entries[index].name; | ||
| 100 | } | 107 | } |
| 101 | 108 | ||
| 102 | void PartitionFilesystem::PrintDebugInfo() const { | 109 | u64 PartitionFilesystem::GetFileOffset(const std::string& name) const { |
| 103 | LOG_DEBUG(Service_FS, "Magic: {:.4}", pfs_header.magic); | ||
| 104 | LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); | ||
| 105 | for (u32 i = 0; i < pfs_header.num_entries; i++) { | 110 | for (u32 i = 0; i < pfs_header.num_entries; i++) { |
| 106 | LOG_DEBUG(Service_FS, " > File {}: {} (0x{:X} bytes, at 0x{:X})", i, | 111 | if (pfs_entries[i].name == name) |
| 107 | pfs_files[i]->GetName(), pfs_files[i]->GetSize(), | 112 | return content_offset + pfs_entries[i].fs_entry.offset; |
| 108 | dynamic_cast<OffsetVfsFile*>(pfs_files[i].get())->GetOffset()); | ||
| 109 | } | 113 | } |
| 110 | } | ||
| 111 | 114 | ||
| 112 | bool PartitionFilesystem::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | 115 | return 0; |
| 113 | auto iter = std::find(pfs_files.begin(), pfs_files.end(), file); | 116 | } |
| 114 | if (iter == pfs_files.end()) | ||
| 115 | return false; | ||
| 116 | 117 | ||
| 117 | pfs_files[iter - pfs_files.begin()] = pfs_files.back(); | 118 | u64 PartitionFilesystem::GetFileSize(const std::string& name) const { |
| 118 | pfs_files.pop_back(); | 119 | for (u32 i = 0; i < pfs_header.num_entries; i++) { |
| 120 | if (pfs_entries[i].name == name) | ||
| 121 | return pfs_entries[i].fs_entry.size; | ||
| 122 | } | ||
| 119 | 123 | ||
| 120 | pfs_dirs.emplace_back(dir); | 124 | return 0; |
| 125 | } | ||
| 121 | 126 | ||
| 122 | return true; | 127 | void PartitionFilesystem::Print() const { |
| 128 | LOG_DEBUG(Service_FS, "Magic: {}", pfs_header.magic); | ||
| 129 | LOG_DEBUG(Service_FS, "Files: {}", pfs_header.num_entries); | ||
| 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 | } | ||
| 123 | } | 135 | } |
| 124 | } // namespace FileSys | 136 | } // namespace FileSys |
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h index 9656b40bf..9c5810cf1 100644 --- a/src/core/file_sys/partition_filesystem.h +++ b/src/core/file_sys/partition_filesystem.h | |||
| @@ -10,7 +10,6 @@ | |||
| 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" | ||
| 14 | 13 | ||
| 15 | namespace Loader { | 14 | namespace Loader { |
| 16 | enum class ResultStatus; | 15 | enum class ResultStatus; |
| @@ -22,19 +21,19 @@ namespace FileSys { | |||
| 22 | * Helper which implements an interface to parse PFS/HFS filesystems. | 21 | * Helper which implements an interface to parse PFS/HFS filesystems. |
| 23 | * Data can either be loaded from a file path or data with an offset into it. | 22 | * Data can either be loaded from a file path or data with an offset into it. |
| 24 | */ | 23 | */ |
| 25 | class PartitionFilesystem : public ReadOnlyVfsDirectory { | 24 | class PartitionFilesystem { |
| 26 | public: | 25 | public: |
| 27 | explicit PartitionFilesystem(std::shared_ptr<VfsFile> file); | 26 | Loader::ResultStatus Load(const std::string& file_path, size_t offset = 0); |
| 28 | Loader::ResultStatus GetStatus() const; | 27 | Loader::ResultStatus Load(const std::vector<u8>& file_data, size_t offset = 0); |
| 29 | 28 | ||
| 30 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | 29 | u32 GetNumEntries() const; |
| 31 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | 30 | u64 GetEntryOffset(u32 index) const; |
| 32 | std::string GetName() const override; | 31 | u64 GetEntrySize(u32 index) const; |
| 33 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | 32 | std::string GetEntryName(u32 index) const; |
| 34 | void PrintDebugInfo() const; | 33 | u64 GetFileOffset(const std::string& name) const; |
| 34 | u64 GetFileSize(const std::string& name) const; | ||
| 35 | 35 | ||
| 36 | protected: | 36 | void Print() const; |
| 37 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 38 | 37 | ||
| 39 | private: | 38 | private: |
| 40 | struct Header { | 39 | struct Header { |
| @@ -73,14 +72,16 @@ private: | |||
| 73 | 72 | ||
| 74 | #pragma pack(pop) | 73 | #pragma pack(pop) |
| 75 | 74 | ||
| 76 | Loader::ResultStatus status; | 75 | struct FileEntry { |
| 76 | FSEntry fs_entry; | ||
| 77 | std::string name; | ||
| 78 | }; | ||
| 77 | 79 | ||
| 78 | Header pfs_header; | 80 | Header pfs_header; |
| 79 | bool is_hfs; | 81 | bool is_hfs; |
| 80 | size_t content_offset; | 82 | size_t content_offset; |
| 81 | 83 | ||
| 82 | std::vector<VirtualFile> pfs_files; | 84 | std::vector<FileEntry> pfs_entries; |
| 83 | std::vector<VirtualDir> pfs_dirs; | ||
| 84 | }; | 85 | }; |
| 85 | 86 | ||
| 86 | } // namespace FileSys | 87 | } // namespace FileSys |
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp index 63d4b6e4f..226811115 100644 --- a/src/core/file_sys/program_metadata.cpp +++ b/src/core/file_sys/program_metadata.cpp | |||
| @@ -9,30 +9,41 @@ | |||
| 9 | 9 | ||
| 10 | namespace FileSys { | 10 | namespace FileSys { |
| 11 | 11 | ||
| 12 | Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) { | 12 | Loader::ResultStatus ProgramMetadata::Load(const std::string& file_path) { |
| 13 | size_t total_size = static_cast<size_t>(file->GetSize()); | 13 | FileUtil::IOFile file(file_path, "rb"); |
| 14 | if (total_size < sizeof(Header)) | 14 | if (!file.IsOpen()) |
| 15 | return Loader::ResultStatus::Error; | 15 | return Loader::ResultStatus::Error; |
| 16 | 16 | ||
| 17 | // TODO(DarkLordZach): Use ReadObject when Header/AcidHeader becomes trivially copyable. | 17 | std::vector<u8> file_data(file.GetSize()); |
| 18 | std::vector<u8> npdm_header_data = file->ReadBytes(sizeof(Header)); | ||
| 19 | if (sizeof(Header) != npdm_header_data.size()) | ||
| 20 | return Loader::ResultStatus::Error; | ||
| 21 | std::memcpy(&npdm_header, npdm_header_data.data(), sizeof(Header)); | ||
| 22 | 18 | ||
| 23 | std::vector<u8> acid_header_data = file->ReadBytes(sizeof(AcidHeader), npdm_header.acid_offset); | 19 | if (!file.ReadBytes(file_data.data(), file_data.size())) |
| 24 | if (sizeof(AcidHeader) != acid_header_data.size()) | ||
| 25 | return Loader::ResultStatus::Error; | 20 | return Loader::ResultStatus::Error; |
| 26 | std::memcpy(&acid_header, acid_header_data.data(), sizeof(AcidHeader)); | ||
| 27 | 21 | ||
| 28 | if (sizeof(AciHeader) != file->ReadObject(&aci_header, npdm_header.aci_offset)) | 22 | Loader::ResultStatus result = Load(file_data); |
| 29 | return Loader::ResultStatus::Error; | 23 | if (result != Loader::ResultStatus::Success) |
| 24 | LOG_ERROR(Service_FS, "Failed to load NPDM from file {}!", file_path); | ||
| 30 | 25 | ||
| 31 | if (sizeof(FileAccessControl) != file->ReadObject(&acid_file_access, acid_header.fac_offset)) | 26 | return result; |
| 32 | return Loader::ResultStatus::Error; | 27 | } |
| 33 | if (sizeof(FileAccessHeader) != file->ReadObject(&aci_file_access, aci_header.fah_offset)) | 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)) | ||
| 34 | return Loader::ResultStatus::Error; | 32 | return Loader::ResultStatus::Error; |
| 35 | 33 | ||
| 34 | size_t header_offset = offset; | ||
| 35 | memcpy(&npdm_header, &file_data[offset], sizeof(Header)); | ||
| 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 | |||
| 42 | size_t fac_offset = acid_offset + acid_header.fac_offset; | ||
| 43 | size_t fah_offset = aci_offset + aci_header.fah_offset; | ||
| 44 | memcpy(&acid_file_access, &file_data[fac_offset], sizeof(FileAccessControl)); | ||
| 45 | memcpy(&aci_file_access, &file_data[fah_offset], sizeof(FileAccessHeader)); | ||
| 46 | |||
| 36 | return Loader::ResultStatus::Success; | 47 | return Loader::ResultStatus::Success; |
| 37 | } | 48 | } |
| 38 | 49 | ||
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h index 06a7315db..b80a08485 100644 --- a/src/core/file_sys/program_metadata.h +++ b/src/core/file_sys/program_metadata.h | |||
| @@ -10,7 +10,6 @@ | |||
| 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" | ||
| 14 | 13 | ||
| 15 | namespace Loader { | 14 | namespace Loader { |
| 16 | enum class ResultStatus; | 15 | enum class ResultStatus; |
| @@ -38,7 +37,8 @@ enum class ProgramFilePermission : u64 { | |||
| 38 | */ | 37 | */ |
| 39 | class ProgramMetadata { | 38 | class ProgramMetadata { |
| 40 | public: | 39 | public: |
| 41 | Loader::ResultStatus Load(VirtualFile file); | 40 | Loader::ResultStatus Load(const std::string& file_path); |
| 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,7 +51,6 @@ public: | |||
| 51 | void Print() const; | 51 | void Print() const; |
| 52 | 52 | ||
| 53 | private: | 53 | private: |
| 54 | // TODO(DarkLordZach): BitField is not trivially copyable. | ||
| 55 | struct Header { | 54 | struct Header { |
| 56 | std::array<char, 4> magic; | 55 | std::array<char, 4> magic; |
| 57 | std::array<u8, 8> reserved; | 56 | std::array<u8, 8> reserved; |
| @@ -78,7 +77,6 @@ private: | |||
| 78 | 77 | ||
| 79 | static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong"); | 78 | static_assert(sizeof(Header) == 0x80, "NPDM header structure size is wrong"); |
| 80 | 79 | ||
| 81 | // TODO(DarkLordZach): BitField is not trivially copyable. | ||
| 82 | struct AcidHeader { | 80 | struct AcidHeader { |
| 83 | std::array<u8, 0x100> signature; | 81 | std::array<u8, 0x100> signature; |
| 84 | std::array<u8, 0x100> nca_modulus; | 82 | std::array<u8, 0x100> nca_modulus; |
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp new file mode 100644 index 000000000..84ae0d99b --- /dev/null +++ b/src/core/file_sys/romfs_factory.cpp | |||
| @@ -0,0 +1,38 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <memory> | ||
| 7 | #include "common/common_types.h" | ||
| 8 | #include "common/logging/log.h" | ||
| 9 | #include "core/file_sys/romfs_factory.h" | ||
| 10 | #include "core/file_sys/romfs_filesystem.h" | ||
| 11 | |||
| 12 | namespace FileSys { | ||
| 13 | |||
| 14 | RomFS_Factory::RomFS_Factory(Loader::AppLoader& app_loader) { | ||
| 15 | // Load the RomFS from the app | ||
| 16 | if (Loader::ResultStatus::Success != app_loader.ReadRomFS(romfs_file, data_offset, data_size)) { | ||
| 17 | LOG_ERROR(Service_FS, "Unable to read RomFS!"); | ||
| 18 | } | ||
| 19 | } | ||
| 20 | |||
| 21 | ResultVal<std::unique_ptr<FileSystemBackend>> RomFS_Factory::Open(const Path& path) { | ||
| 22 | auto archive = std::make_unique<RomFS_FileSystem>(romfs_file, data_offset, data_size); | ||
| 23 | return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); | ||
| 24 | } | ||
| 25 | |||
| 26 | ResultCode RomFS_Factory::Format(const Path& path) { | ||
| 27 | LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName()); | ||
| 28 | // TODO(bunnei): Find the right error code for this | ||
| 29 | return ResultCode(-1); | ||
| 30 | } | ||
| 31 | |||
| 32 | ResultVal<ArchiveFormatInfo> RomFS_Factory::GetFormatInfo(const Path& path) const { | ||
| 33 | LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); | ||
| 34 | // TODO(bunnei): Find the right error code for this | ||
| 35 | return ResultCode(-1); | ||
| 36 | } | ||
| 37 | |||
| 38 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h new file mode 100644 index 000000000..e0698e642 --- /dev/null +++ b/src/core/file_sys/romfs_factory.h | |||
| @@ -0,0 +1,35 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "core/file_sys/filesystem.h" | ||
| 12 | #include "core/hle/result.h" | ||
| 13 | #include "core/loader/loader.h" | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | |||
| 17 | /// File system interface to the RomFS archive | ||
| 18 | class RomFS_Factory final : public FileSystemFactory { | ||
| 19 | public: | ||
| 20 | explicit RomFS_Factory(Loader::AppLoader& app_loader); | ||
| 21 | |||
| 22 | std::string GetName() const override { | ||
| 23 | return "ArchiveFactory_RomFS"; | ||
| 24 | } | ||
| 25 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; | ||
| 26 | ResultCode Format(const Path& path) override; | ||
| 27 | ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||
| 28 | |||
| 29 | private: | ||
| 30 | std::shared_ptr<FileUtil::IOFile> romfs_file; | ||
| 31 | u64 data_offset; | ||
| 32 | u64 data_size; | ||
| 33 | }; | ||
| 34 | |||
| 35 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/romfs_filesystem.cpp b/src/core/file_sys/romfs_filesystem.cpp new file mode 100644 index 000000000..83162622b --- /dev/null +++ b/src/core/file_sys/romfs_filesystem.cpp | |||
| @@ -0,0 +1,110 @@ | |||
| 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 new file mode 100644 index 000000000..ba9d85823 --- /dev/null +++ b/src/core/file_sys/romfs_filesystem.h | |||
| @@ -0,0 +1,85 @@ | |||
| 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 new file mode 100644 index 000000000..f3aa213af --- /dev/null +++ b/src/core/file_sys/savedata_factory.cpp | |||
| @@ -0,0 +1,63 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "core/core.h" | ||
| 9 | #include "core/file_sys/disk_filesystem.h" | ||
| 10 | #include "core/file_sys/savedata_factory.h" | ||
| 11 | #include "core/hle/kernel/process.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | SaveData_Factory::SaveData_Factory(std::string nand_directory) | ||
| 16 | : nand_directory(std::move(nand_directory)) {} | ||
| 17 | |||
| 18 | ResultVal<std::unique_ptr<FileSystemBackend>> SaveData_Factory::Open(const Path& path) { | ||
| 19 | std::string save_directory = GetFullPath(); | ||
| 20 | |||
| 21 | if (!FileUtil::Exists(save_directory)) { | ||
| 22 | // TODO(bunnei): This is a work-around to always create a save data directory if it does not | ||
| 23 | // already exist. This is a hack, as we do not understand yet how this works on hardware. | ||
| 24 | // Without a save data directory, many games will assert on boot. This should not have any | ||
| 25 | // bad side-effects. | ||
| 26 | FileUtil::CreateFullPath(save_directory); | ||
| 27 | } | ||
| 28 | |||
| 29 | // Return an error if the save data doesn't actually exist. | ||
| 30 | if (!FileUtil::IsDirectory(save_directory)) { | ||
| 31 | // TODO(Subv): Find out correct error code. | ||
| 32 | return ResultCode(-1); | ||
| 33 | } | ||
| 34 | |||
| 35 | auto archive = std::make_unique<Disk_FileSystem>(save_directory); | ||
| 36 | return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); | ||
| 37 | } | ||
| 38 | |||
| 39 | ResultCode SaveData_Factory::Format(const Path& path) { | ||
| 40 | LOG_WARNING(Service_FS, "Format archive {}", GetName()); | ||
| 41 | // Create the save data directory. | ||
| 42 | if (!FileUtil::CreateFullPath(GetFullPath())) { | ||
| 43 | // TODO(Subv): Find the correct error code. | ||
| 44 | return ResultCode(-1); | ||
| 45 | } | ||
| 46 | |||
| 47 | return RESULT_SUCCESS; | ||
| 48 | } | ||
| 49 | |||
| 50 | ResultVal<ArchiveFormatInfo> SaveData_Factory::GetFormatInfo(const Path& path) const { | ||
| 51 | LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); | ||
| 52 | // TODO(bunnei): Find the right error code for this | ||
| 53 | return ResultCode(-1); | ||
| 54 | } | ||
| 55 | |||
| 56 | std::string SaveData_Factory::GetFullPath() const { | ||
| 57 | u64 title_id = Core::CurrentProcess()->program_id; | ||
| 58 | // TODO(Subv): Somehow obtain this value. | ||
| 59 | u32 user = 0; | ||
| 60 | return fmt::format("{}save/{:016X}/{:08X}/", nand_directory, title_id, user); | ||
| 61 | } | ||
| 62 | |||
| 63 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h new file mode 100644 index 000000000..73a42aab6 --- /dev/null +++ b/src/core/file_sys/savedata_factory.h | |||
| @@ -0,0 +1,33 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/file_sys/filesystem.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | /// File system interface to the SaveData archive | ||
| 16 | class SaveData_Factory final : public FileSystemFactory { | ||
| 17 | public: | ||
| 18 | explicit SaveData_Factory(std::string nand_directory); | ||
| 19 | |||
| 20 | std::string GetName() const override { | ||
| 21 | return "SaveData_Factory"; | ||
| 22 | } | ||
| 23 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; | ||
| 24 | ResultCode Format(const Path& path) override; | ||
| 25 | ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||
| 26 | |||
| 27 | private: | ||
| 28 | std::string nand_directory; | ||
| 29 | |||
| 30 | std::string GetFullPath() const; | ||
| 31 | }; | ||
| 32 | |||
| 33 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp new file mode 100644 index 000000000..2e5ffb764 --- /dev/null +++ b/src/core/file_sys/sdmc_factory.cpp | |||
| @@ -0,0 +1,39 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <memory> | ||
| 6 | #include "common/common_types.h" | ||
| 7 | #include "common/logging/log.h" | ||
| 8 | #include "common/string_util.h" | ||
| 9 | #include "core/core.h" | ||
| 10 | #include "core/file_sys/disk_filesystem.h" | ||
| 11 | #include "core/file_sys/sdmc_factory.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | SDMC_Factory::SDMC_Factory(std::string sd_directory) : sd_directory(std::move(sd_directory)) {} | ||
| 16 | |||
| 17 | ResultVal<std::unique_ptr<FileSystemBackend>> SDMC_Factory::Open(const Path& path) { | ||
| 18 | // Create the SD Card directory if it doesn't already exist. | ||
| 19 | if (!FileUtil::IsDirectory(sd_directory)) { | ||
| 20 | FileUtil::CreateFullPath(sd_directory); | ||
| 21 | } | ||
| 22 | |||
| 23 | auto archive = std::make_unique<Disk_FileSystem>(sd_directory); | ||
| 24 | return MakeResult<std::unique_ptr<FileSystemBackend>>(std::move(archive)); | ||
| 25 | } | ||
| 26 | |||
| 27 | ResultCode SDMC_Factory::Format(const Path& path) { | ||
| 28 | LOG_ERROR(Service_FS, "Unimplemented Format archive {}", GetName()); | ||
| 29 | // TODO(Subv): Find the right error code for this | ||
| 30 | return ResultCode(-1); | ||
| 31 | } | ||
| 32 | |||
| 33 | ResultVal<ArchiveFormatInfo> SDMC_Factory::GetFormatInfo(const Path& path) const { | ||
| 34 | LOG_ERROR(Service_FS, "Unimplemented GetFormatInfo archive {}", GetName()); | ||
| 35 | // TODO(bunnei): Find the right error code for this | ||
| 36 | return ResultCode(-1); | ||
| 37 | } | ||
| 38 | |||
| 39 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h new file mode 100644 index 000000000..93becda25 --- /dev/null +++ b/src/core/file_sys/sdmc_factory.h | |||
| @@ -0,0 +1,31 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <memory> | ||
| 8 | #include <string> | ||
| 9 | #include "common/common_types.h" | ||
| 10 | #include "core/file_sys/filesystem.h" | ||
| 11 | #include "core/hle/result.h" | ||
| 12 | |||
| 13 | namespace FileSys { | ||
| 14 | |||
| 15 | /// File system interface to the SDCard archive | ||
| 16 | class SDMC_Factory final : public FileSystemFactory { | ||
| 17 | public: | ||
| 18 | explicit SDMC_Factory(std::string sd_directory); | ||
| 19 | |||
| 20 | std::string GetName() const override { | ||
| 21 | return "SDMC_Factory"; | ||
| 22 | } | ||
| 23 | ResultVal<std::unique_ptr<FileSystemBackend>> Open(const Path& path) override; | ||
| 24 | ResultCode Format(const Path& path) override; | ||
| 25 | ResultVal<ArchiveFormatInfo> GetFormatInfo(const Path& path) const override; | ||
| 26 | |||
| 27 | private: | ||
| 28 | std::string sd_directory; | ||
| 29 | }; | ||
| 30 | |||
| 31 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp deleted file mode 100644 index c99071d6a..000000000 --- a/src/core/file_sys/vfs.cpp +++ /dev/null | |||
| @@ -1,187 +0,0 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <algorithm> | ||
| 6 | #include <numeric> | ||
| 7 | #include "common/file_util.h" | ||
| 8 | #include "core/file_sys/vfs.h" | ||
| 9 | |||
| 10 | namespace FileSys { | ||
| 11 | |||
| 12 | VfsFile::~VfsFile() = default; | ||
| 13 | |||
| 14 | std::string VfsFile::GetExtension() const { | ||
| 15 | return FileUtil::GetExtensionFromFilename(GetName()); | ||
| 16 | } | ||
| 17 | |||
| 18 | VfsDirectory::~VfsDirectory() = default; | ||
| 19 | |||
| 20 | boost::optional<u8> VfsFile::ReadByte(size_t offset) const { | ||
| 21 | u8 out{}; | ||
| 22 | size_t size = Read(&out, 1, offset); | ||
| 23 | if (size == 1) | ||
| 24 | return out; | ||
| 25 | |||
| 26 | return boost::none; | ||
| 27 | } | ||
| 28 | |||
| 29 | std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const { | ||
| 30 | std::vector<u8> out(size); | ||
| 31 | size_t read_size = Read(out.data(), size, offset); | ||
| 32 | out.resize(read_size); | ||
| 33 | return out; | ||
| 34 | } | ||
| 35 | |||
| 36 | std::vector<u8> VfsFile::ReadAllBytes() const { | ||
| 37 | return ReadBytes(GetSize()); | ||
| 38 | } | ||
| 39 | |||
| 40 | bool VfsFile::WriteByte(u8 data, size_t offset) { | ||
| 41 | return Write(&data, 1, offset) == 1; | ||
| 42 | } | ||
| 43 | |||
| 44 | size_t VfsFile::WriteBytes(std::vector<u8> data, size_t offset) { | ||
| 45 | return Write(data.data(), data.size(), offset); | ||
| 46 | } | ||
| 47 | |||
| 48 | std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(const std::string& path) const { | ||
| 49 | auto vec = FileUtil::SplitPathComponents(path); | ||
| 50 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | ||
| 51 | vec.end()); | ||
| 52 | if (vec.empty()) | ||
| 53 | return nullptr; | ||
| 54 | if (vec.size() == 1) | ||
| 55 | return GetFile(vec[0]); | ||
| 56 | auto dir = GetSubdirectory(vec[0]); | ||
| 57 | for (size_t i = 1; i < vec.size() - 1; ++i) { | ||
| 58 | if (dir == nullptr) | ||
| 59 | return nullptr; | ||
| 60 | dir = dir->GetSubdirectory(vec[i]); | ||
| 61 | } | ||
| 62 | if (dir == nullptr) | ||
| 63 | return nullptr; | ||
| 64 | return dir->GetFile(vec.back()); | ||
| 65 | } | ||
| 66 | |||
| 67 | std::shared_ptr<VfsFile> VfsDirectory::GetFileAbsolute(const std::string& path) const { | ||
| 68 | if (IsRoot()) | ||
| 69 | return GetFileRelative(path); | ||
| 70 | |||
| 71 | return GetParentDirectory()->GetFileAbsolute(path); | ||
| 72 | } | ||
| 73 | |||
| 74 | std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(const std::string& path) const { | ||
| 75 | auto vec = FileUtil::SplitPathComponents(path); | ||
| 76 | vec.erase(std::remove_if(vec.begin(), vec.end(), [](const auto& str) { return str.empty(); }), | ||
| 77 | vec.end()); | ||
| 78 | if (vec.empty()) | ||
| 79 | // return std::shared_ptr<VfsDirectory>(this); | ||
| 80 | return nullptr; | ||
| 81 | auto dir = GetSubdirectory(vec[0]); | ||
| 82 | for (size_t i = 1; i < vec.size(); ++i) { | ||
| 83 | dir = dir->GetSubdirectory(vec[i]); | ||
| 84 | } | ||
| 85 | return dir; | ||
| 86 | } | ||
| 87 | |||
| 88 | std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryAbsolute(const std::string& path) const { | ||
| 89 | if (IsRoot()) | ||
| 90 | return GetDirectoryRelative(path); | ||
| 91 | |||
| 92 | return GetParentDirectory()->GetDirectoryAbsolute(path); | ||
| 93 | } | ||
| 94 | |||
| 95 | std::shared_ptr<VfsFile> VfsDirectory::GetFile(const std::string& name) const { | ||
| 96 | const auto& files = GetFiles(); | ||
| 97 | const auto iter = std::find_if(files.begin(), files.end(), | ||
| 98 | [&name](const auto& file1) { return name == file1->GetName(); }); | ||
| 99 | return iter == files.end() ? nullptr : *iter; | ||
| 100 | } | ||
| 101 | |||
| 102 | std::shared_ptr<VfsDirectory> VfsDirectory::GetSubdirectory(const std::string& name) const { | ||
| 103 | const auto& subs = GetSubdirectories(); | ||
| 104 | const auto iter = std::find_if(subs.begin(), subs.end(), | ||
| 105 | [&name](const auto& file1) { return name == file1->GetName(); }); | ||
| 106 | return iter == subs.end() ? nullptr : *iter; | ||
| 107 | } | ||
| 108 | |||
| 109 | bool VfsDirectory::IsRoot() const { | ||
| 110 | return GetParentDirectory() == nullptr; | ||
| 111 | } | ||
| 112 | |||
| 113 | size_t VfsDirectory::GetSize() const { | ||
| 114 | const auto& files = GetFiles(); | ||
| 115 | const auto file_total = | ||
| 116 | std::accumulate(files.begin(), files.end(), 0ull, | ||
| 117 | [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); | ||
| 118 | |||
| 119 | const auto& sub_dir = GetSubdirectories(); | ||
| 120 | const auto subdir_total = | ||
| 121 | std::accumulate(sub_dir.begin(), sub_dir.end(), 0ull, | ||
| 122 | [](const auto& f1, const auto& f2) { return f1 + f2->GetSize(); }); | ||
| 123 | |||
| 124 | return file_total + subdir_total; | ||
| 125 | } | ||
| 126 | |||
| 127 | bool VfsDirectory::DeleteSubdirectoryRecursive(const std::string& name) { | ||
| 128 | auto dir = GetSubdirectory(name); | ||
| 129 | if (dir == nullptr) | ||
| 130 | return false; | ||
| 131 | |||
| 132 | bool success = true; | ||
| 133 | for (const auto& file : dir->GetFiles()) { | ||
| 134 | if (!DeleteFile(file->GetName())) | ||
| 135 | success = false; | ||
| 136 | } | ||
| 137 | |||
| 138 | for (const auto& sdir : dir->GetSubdirectories()) { | ||
| 139 | if (!dir->DeleteSubdirectoryRecursive(sdir->GetName())) | ||
| 140 | success = false; | ||
| 141 | } | ||
| 142 | |||
| 143 | return success; | ||
| 144 | } | ||
| 145 | |||
| 146 | bool VfsDirectory::Copy(const std::string& src, const std::string& dest) { | ||
| 147 | const auto f1 = GetFile(src); | ||
| 148 | auto f2 = CreateFile(dest); | ||
| 149 | if (f1 == nullptr || f2 == nullptr) | ||
| 150 | return false; | ||
| 151 | |||
| 152 | if (!f2->Resize(f1->GetSize())) { | ||
| 153 | DeleteFile(dest); | ||
| 154 | return false; | ||
| 155 | } | ||
| 156 | |||
| 157 | return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize(); | ||
| 158 | } | ||
| 159 | |||
| 160 | bool ReadOnlyVfsDirectory::IsWritable() const { | ||
| 161 | return false; | ||
| 162 | } | ||
| 163 | |||
| 164 | bool ReadOnlyVfsDirectory::IsReadable() const { | ||
| 165 | return true; | ||
| 166 | } | ||
| 167 | |||
| 168 | std::shared_ptr<VfsDirectory> ReadOnlyVfsDirectory::CreateSubdirectory(const std::string& name) { | ||
| 169 | return nullptr; | ||
| 170 | } | ||
| 171 | |||
| 172 | std::shared_ptr<VfsFile> ReadOnlyVfsDirectory::CreateFile(const std::string& name) { | ||
| 173 | return nullptr; | ||
| 174 | } | ||
| 175 | |||
| 176 | bool ReadOnlyVfsDirectory::DeleteSubdirectory(const std::string& name) { | ||
| 177 | return false; | ||
| 178 | } | ||
| 179 | |||
| 180 | bool ReadOnlyVfsDirectory::DeleteFile(const std::string& name) { | ||
| 181 | return false; | ||
| 182 | } | ||
| 183 | |||
| 184 | bool ReadOnlyVfsDirectory::Rename(const std::string& name) { | ||
| 185 | return false; | ||
| 186 | } | ||
| 187 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h deleted file mode 100644 index 0e30991b4..000000000 --- a/src/core/file_sys/vfs.h +++ /dev/null | |||
| @@ -1,220 +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 <type_traits> | ||
| 10 | #include <vector> | ||
| 11 | #include "boost/optional.hpp" | ||
| 12 | #include "common/common_types.h" | ||
| 13 | #include "common/file_util.h" | ||
| 14 | |||
| 15 | namespace FileSys { | ||
| 16 | struct VfsFile; | ||
| 17 | struct VfsDirectory; | ||
| 18 | |||
| 19 | // Convenience typedefs to use VfsDirectory and VfsFile | ||
| 20 | using VirtualDir = std::shared_ptr<FileSys::VfsDirectory>; | ||
| 21 | using VirtualFile = std::shared_ptr<FileSys::VfsFile>; | ||
| 22 | |||
| 23 | // A class representing a file in an abstract filesystem. | ||
| 24 | struct VfsFile : NonCopyable { | ||
| 25 | virtual ~VfsFile(); | ||
| 26 | |||
| 27 | // Retrieves the file name. | ||
| 28 | virtual std::string GetName() const = 0; | ||
| 29 | // Retrieves the extension of the file name. | ||
| 30 | virtual std::string GetExtension() const; | ||
| 31 | // Retrieves the size of the file. | ||
| 32 | virtual size_t GetSize() const = 0; | ||
| 33 | // Resizes the file to new_size. Returns whether or not the operation was successful. | ||
| 34 | virtual bool Resize(size_t new_size) = 0; | ||
| 35 | // Gets a pointer to the directory containing this file, returning nullptr if there is none. | ||
| 36 | virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0; | ||
| 37 | |||
| 38 | // Returns whether or not the file can be written to. | ||
| 39 | virtual bool IsWritable() const = 0; | ||
| 40 | // Returns whether or not the file can be read from. | ||
| 41 | virtual bool IsReadable() const = 0; | ||
| 42 | |||
| 43 | // The primary method of reading from the file. Reads length bytes into data starting at offset | ||
| 44 | // into file. Returns number of bytes successfully read. | ||
| 45 | virtual size_t Read(u8* data, size_t length, size_t offset = 0) const = 0; | ||
| 46 | // The primary method of writing to the file. Writes length bytes from data starting at offset | ||
| 47 | // into file. Returns number of bytes successfully written. | ||
| 48 | virtual size_t Write(const u8* data, size_t length, size_t offset = 0) = 0; | ||
| 49 | |||
| 50 | // Reads exactly one byte at the offset provided, returning boost::none on error. | ||
| 51 | virtual boost::optional<u8> ReadByte(size_t offset = 0) const; | ||
| 52 | // Reads size bytes starting at offset in file into a vector. | ||
| 53 | virtual std::vector<u8> ReadBytes(size_t size, size_t offset = 0) const; | ||
| 54 | // Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(), | ||
| 55 | // 0)' | ||
| 56 | virtual std::vector<u8> ReadAllBytes() const; | ||
| 57 | |||
| 58 | // Reads an array of type T, size number_elements starting at offset. | ||
| 59 | // Returns the number of bytes (sizeof(T)*number_elements) read successfully. | ||
| 60 | template <typename T> | ||
| 61 | size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const { | ||
| 62 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 63 | "Data type must be trivially copyable."); | ||
| 64 | |||
| 65 | return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset); | ||
| 66 | } | ||
| 67 | |||
| 68 | // Reads size bytes into the memory starting at data starting at offset into the file. | ||
| 69 | // Returns the number of bytes read successfully. | ||
| 70 | template <typename T> | ||
| 71 | size_t ReadBytes(T* data, size_t size, size_t offset = 0) const { | ||
| 72 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 73 | "Data type must be trivially copyable."); | ||
| 74 | return Read(reinterpret_cast<u8*>(data), size, offset); | ||
| 75 | } | ||
| 76 | |||
| 77 | // Reads one object of type T starting at offset in file. | ||
| 78 | // Returns the number of bytes read successfully (sizeof(T)). | ||
| 79 | template <typename T> | ||
| 80 | size_t ReadObject(T* data, size_t offset = 0) const { | ||
| 81 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 82 | "Data type must be trivially copyable."); | ||
| 83 | return Read(reinterpret_cast<u8*>(data), sizeof(T), offset); | ||
| 84 | } | ||
| 85 | |||
| 86 | // Writes exactly one byte to offset in file and retuns whether or not the byte was written | ||
| 87 | // successfully. | ||
| 88 | virtual bool WriteByte(u8 data, size_t offset = 0); | ||
| 89 | // Writes a vector of bytes to offset in file and returns the number of bytes successfully | ||
| 90 | // written. | ||
| 91 | virtual size_t WriteBytes(std::vector<u8> data, size_t offset = 0); | ||
| 92 | |||
| 93 | // Writes an array of type T, size number_elements to offset in file. | ||
| 94 | // Returns the number of bytes (sizeof(T)*number_elements) written successfully. | ||
| 95 | template <typename T> | ||
| 96 | size_t WriteArray(T* data, size_t number_elements, size_t offset = 0) { | ||
| 97 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 98 | "Data type must be trivially copyable."); | ||
| 99 | |||
| 100 | return Write(data, number_elements * sizeof(T), offset); | ||
| 101 | } | ||
| 102 | |||
| 103 | // Writes size bytes starting at memory location data to offset in file. | ||
| 104 | // Returns the number of bytes written successfully. | ||
| 105 | template <typename T> | ||
| 106 | size_t WriteBytes(T* data, size_t size, size_t offset = 0) { | ||
| 107 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 108 | "Data type must be trivially copyable."); | ||
| 109 | return Write(reinterpret_cast<u8*>(data), size, offset); | ||
| 110 | } | ||
| 111 | |||
| 112 | // Writes one object of type T to offset in file. | ||
| 113 | // Returns the number of bytes written successfully (sizeof(T)). | ||
| 114 | template <typename T> | ||
| 115 | size_t WriteObject(const T& data, size_t offset = 0) { | ||
| 116 | static_assert(std::is_trivially_copyable<T>::value, | ||
| 117 | "Data type must be trivially copyable."); | ||
| 118 | return Write(&data, sizeof(T), offset); | ||
| 119 | } | ||
| 120 | |||
| 121 | // Renames the file to name. Returns whether or not the operation was successsful. | ||
| 122 | virtual bool Rename(const std::string& name) = 0; | ||
| 123 | }; | ||
| 124 | |||
| 125 | // A class representing a directory in an abstract filesystem. | ||
| 126 | struct VfsDirectory : NonCopyable { | ||
| 127 | virtual ~VfsDirectory(); | ||
| 128 | |||
| 129 | // Retrives the file located at path as if the current directory was root. Returns nullptr if | ||
| 130 | // not found. | ||
| 131 | virtual std::shared_ptr<VfsFile> GetFileRelative(const std::string& path) const; | ||
| 132 | // Calls GetFileRelative(path) on the root of the current directory. | ||
| 133 | virtual std::shared_ptr<VfsFile> GetFileAbsolute(const std::string& path) const; | ||
| 134 | |||
| 135 | // Retrives the directory located at path as if the current directory was root. Returns nullptr | ||
| 136 | // if not found. | ||
| 137 | virtual std::shared_ptr<VfsDirectory> GetDirectoryRelative(const std::string& path) const; | ||
| 138 | // Calls GetDirectoryRelative(path) on the root of the current directory. | ||
| 139 | virtual std::shared_ptr<VfsDirectory> GetDirectoryAbsolute(const std::string& path) const; | ||
| 140 | |||
| 141 | // Returns a vector containing all of the files in this directory. | ||
| 142 | virtual std::vector<std::shared_ptr<VfsFile>> GetFiles() const = 0; | ||
| 143 | // Returns the file with filename matching name. Returns nullptr if directory dosen't have a | ||
| 144 | // file with name. | ||
| 145 | virtual std::shared_ptr<VfsFile> GetFile(const std::string& name) const; | ||
| 146 | |||
| 147 | // Returns a vector containing all of the subdirectories in this directory. | ||
| 148 | virtual std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const = 0; | ||
| 149 | // Returns the directory with name matching name. Returns nullptr if directory dosen't have a | ||
| 150 | // directory with name. | ||
| 151 | virtual std::shared_ptr<VfsDirectory> GetSubdirectory(const std::string& name) const; | ||
| 152 | |||
| 153 | // Returns whether or not the directory can be written to. | ||
| 154 | virtual bool IsWritable() const = 0; | ||
| 155 | // Returns whether of not the directory can be read from. | ||
| 156 | virtual bool IsReadable() const = 0; | ||
| 157 | |||
| 158 | // Returns whether or not the directory is the root of the current file tree. | ||
| 159 | virtual bool IsRoot() const; | ||
| 160 | |||
| 161 | // Returns the name of the directory. | ||
| 162 | virtual std::string GetName() const = 0; | ||
| 163 | // Returns the total size of all files and subdirectories in this directory. | ||
| 164 | virtual size_t GetSize() const; | ||
| 165 | // Returns the parent directory of this directory. Returns nullptr if this directory is root or | ||
| 166 | // has no parent. | ||
| 167 | virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0; | ||
| 168 | |||
| 169 | // Creates a new subdirectory with name name. Returns a pointer to the new directory or nullptr | ||
| 170 | // if the operation failed. | ||
| 171 | virtual std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) = 0; | ||
| 172 | // Creates a new file with name name. Returns a pointer to the new file or nullptr if the | ||
| 173 | // operation failed. | ||
| 174 | virtual std::shared_ptr<VfsFile> CreateFile(const std::string& name) = 0; | ||
| 175 | |||
| 176 | // Deletes the subdirectory with name and returns true on success. | ||
| 177 | virtual bool DeleteSubdirectory(const std::string& name) = 0; | ||
| 178 | // Deletes all subdirectories and files of subdirectory with name recirsively and then deletes | ||
| 179 | // the subdirectory. Returns true on success. | ||
| 180 | virtual bool DeleteSubdirectoryRecursive(const std::string& name); | ||
| 181 | // Returnes whether or not the file with name name was deleted successfully. | ||
| 182 | virtual bool DeleteFile(const std::string& name) = 0; | ||
| 183 | |||
| 184 | // Returns whether or not this directory was renamed to name. | ||
| 185 | virtual bool Rename(const std::string& name) = 0; | ||
| 186 | |||
| 187 | // Returns whether or not the file with name src was successfully copied to a new file with name | ||
| 188 | // dest. | ||
| 189 | virtual bool Copy(const std::string& src, const std::string& dest); | ||
| 190 | |||
| 191 | // Interprets the file with name file instead as a directory of type directory. | ||
| 192 | // The directory must have a constructor that takes a single argument of type | ||
| 193 | // std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a | ||
| 194 | // subdirectory in one call. | ||
| 195 | template <typename Directory> | ||
| 196 | bool InterpretAsDirectory(const std::string& file) { | ||
| 197 | auto file_p = GetFile(file); | ||
| 198 | if (file_p == nullptr) | ||
| 199 | return false; | ||
| 200 | return ReplaceFileWithSubdirectory(file, std::make_shared<Directory>(file_p)); | ||
| 201 | } | ||
| 202 | |||
| 203 | protected: | ||
| 204 | // Backend for InterpretAsDirectory. | ||
| 205 | // Removes all references to file and adds a reference to dir in the directory's implementation. | ||
| 206 | virtual bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) = 0; | ||
| 207 | }; | ||
| 208 | |||
| 209 | // A convenience partial-implementation of VfsDirectory that stubs out methods that should only work | ||
| 210 | // if writable. This is to avoid redundant empty methods everywhere. | ||
| 211 | struct ReadOnlyVfsDirectory : public VfsDirectory { | ||
| 212 | bool IsWritable() const override; | ||
| 213 | bool IsReadable() const override; | ||
| 214 | std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override; | ||
| 215 | std::shared_ptr<VfsFile> CreateFile(const std::string& name) override; | ||
| 216 | bool DeleteSubdirectory(const std::string& name) override; | ||
| 217 | bool DeleteFile(const std::string& name) override; | ||
| 218 | bool Rename(const std::string& name) override; | ||
| 219 | }; | ||
| 220 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp deleted file mode 100644 index 288499cb5..000000000 --- a/src/core/file_sys/vfs_offset.cpp +++ /dev/null | |||
| @@ -1,92 +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 "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 deleted file mode 100644 index adc615b38..000000000 --- a/src/core/file_sys/vfs_offset.h +++ /dev/null | |||
| @@ -1,46 +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 "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 deleted file mode 100644 index 8b95e8c72..000000000 --- a/src/core/file_sys/vfs_real.cpp +++ /dev/null | |||
| @@ -1,168 +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 "common/common_paths.h" | ||
| 6 | #include "common/logging/log.h" | ||
| 7 | #include "core/file_sys/vfs_real.h" | ||
| 8 | |||
| 9 | namespace FileSys { | ||
| 10 | |||
| 11 | static std::string PermissionsToCharArray(Mode perms) { | ||
| 12 | std::string out; | ||
| 13 | switch (perms) { | ||
| 14 | case Mode::Read: | ||
| 15 | out += "r"; | ||
| 16 | break; | ||
| 17 | case Mode::Write: | ||
| 18 | out += "r+"; | ||
| 19 | break; | ||
| 20 | case Mode::Append: | ||
| 21 | out += "a"; | ||
| 22 | break; | ||
| 23 | } | ||
| 24 | return out + "b"; | ||
| 25 | } | ||
| 26 | |||
| 27 | RealVfsFile::RealVfsFile(const std::string& path_, Mode perms_) | ||
| 28 | : backing(path_, PermissionsToCharArray(perms_).c_str()), path(path_), | ||
| 29 | parent_path(FileUtil::GetParentPath(path_)), | ||
| 30 | path_components(FileUtil::SplitPathComponents(path_)), | ||
| 31 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | ||
| 32 | perms(perms_) {} | ||
| 33 | |||
| 34 | std::string RealVfsFile::GetName() const { | ||
| 35 | return path_components.back(); | ||
| 36 | } | ||
| 37 | |||
| 38 | size_t RealVfsFile::GetSize() const { | ||
| 39 | return backing.GetSize(); | ||
| 40 | } | ||
| 41 | |||
| 42 | bool RealVfsFile::Resize(size_t new_size) { | ||
| 43 | return backing.Resize(new_size); | ||
| 44 | } | ||
| 45 | |||
| 46 | std::shared_ptr<VfsDirectory> RealVfsFile::GetContainingDirectory() const { | ||
| 47 | return std::make_shared<RealVfsDirectory>(parent_path, perms); | ||
| 48 | } | ||
| 49 | |||
| 50 | bool RealVfsFile::IsWritable() const { | ||
| 51 | return perms == Mode::Append || perms == Mode::Write; | ||
| 52 | } | ||
| 53 | |||
| 54 | bool RealVfsFile::IsReadable() const { | ||
| 55 | return perms == Mode::Read || perms == Mode::Write; | ||
| 56 | } | ||
| 57 | |||
| 58 | size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const { | ||
| 59 | if (!backing.Seek(offset, SEEK_SET)) | ||
| 60 | return 0; | ||
| 61 | return backing.ReadBytes(data, length); | ||
| 62 | } | ||
| 63 | |||
| 64 | size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) { | ||
| 65 | if (!backing.Seek(offset, SEEK_SET)) | ||
| 66 | return 0; | ||
| 67 | return backing.WriteBytes(data, length); | ||
| 68 | } | ||
| 69 | |||
| 70 | bool RealVfsFile::Rename(const std::string& name) { | ||
| 71 | const auto out = FileUtil::Rename(GetName(), name); | ||
| 72 | path = parent_path + DIR_SEP + name; | ||
| 73 | path_components = parent_components; | ||
| 74 | path_components.push_back(name); | ||
| 75 | backing = FileUtil::IOFile(path, PermissionsToCharArray(perms).c_str()); | ||
| 76 | return out; | ||
| 77 | } | ||
| 78 | |||
| 79 | RealVfsDirectory::RealVfsDirectory(const std::string& path_, Mode perms_) | ||
| 80 | : path(FileUtil::RemoveTrailingSlash(path_)), parent_path(FileUtil::GetParentPath(path)), | ||
| 81 | path_components(FileUtil::SplitPathComponents(path)), | ||
| 82 | parent_components(FileUtil::SliceVector(path_components, 0, path_components.size() - 1)), | ||
| 83 | perms(perms_) { | ||
| 84 | if (!FileUtil::Exists(path) && (perms == Mode::Write || perms == Mode::Append)) | ||
| 85 | FileUtil::CreateDir(path); | ||
| 86 | unsigned size; | ||
| 87 | if (perms != Mode::Append) { | ||
| 88 | FileUtil::ForeachDirectoryEntry( | ||
| 89 | &size, path, | ||
| 90 | [this](unsigned* entries_out, const std::string& directory, | ||
| 91 | const std::string& filename) { | ||
| 92 | std::string full_path = directory + DIR_SEP + filename; | ||
| 93 | if (FileUtil::IsDirectory(full_path)) | ||
| 94 | subdirectories.emplace_back( | ||
| 95 | std::make_shared<RealVfsDirectory>(full_path, perms)); | ||
| 96 | else | ||
| 97 | files.emplace_back(std::make_shared<RealVfsFile>(full_path, perms)); | ||
| 98 | return true; | ||
| 99 | }); | ||
| 100 | } | ||
| 101 | } | ||
| 102 | |||
| 103 | std::vector<std::shared_ptr<VfsFile>> RealVfsDirectory::GetFiles() const { | ||
| 104 | return std::vector<std::shared_ptr<VfsFile>>(files); | ||
| 105 | } | ||
| 106 | |||
| 107 | std::vector<std::shared_ptr<VfsDirectory>> RealVfsDirectory::GetSubdirectories() const { | ||
| 108 | return std::vector<std::shared_ptr<VfsDirectory>>(subdirectories); | ||
| 109 | } | ||
| 110 | |||
| 111 | bool RealVfsDirectory::IsWritable() const { | ||
| 112 | return perms == Mode::Write || perms == Mode::Append; | ||
| 113 | } | ||
| 114 | |||
| 115 | bool RealVfsDirectory::IsReadable() const { | ||
| 116 | return perms == Mode::Read || perms == Mode::Write; | ||
| 117 | } | ||
| 118 | |||
| 119 | std::string RealVfsDirectory::GetName() const { | ||
| 120 | return path_components.back(); | ||
| 121 | } | ||
| 122 | |||
| 123 | std::shared_ptr<VfsDirectory> RealVfsDirectory::GetParentDirectory() const { | ||
| 124 | if (path_components.size() <= 1) | ||
| 125 | return nullptr; | ||
| 126 | |||
| 127 | return std::make_shared<RealVfsDirectory>(parent_path, perms); | ||
| 128 | } | ||
| 129 | |||
| 130 | std::shared_ptr<VfsDirectory> RealVfsDirectory::CreateSubdirectory(const std::string& name) { | ||
| 131 | if (!FileUtil::CreateDir(path + DIR_SEP + name)) | ||
| 132 | return nullptr; | ||
| 133 | subdirectories.emplace_back(std::make_shared<RealVfsDirectory>(path + DIR_SEP + name, perms)); | ||
| 134 | return subdirectories.back(); | ||
| 135 | } | ||
| 136 | |||
| 137 | std::shared_ptr<VfsFile> RealVfsDirectory::CreateFile(const std::string& name) { | ||
| 138 | if (!FileUtil::CreateEmptyFile(path + DIR_SEP + name)) | ||
| 139 | return nullptr; | ||
| 140 | files.emplace_back(std::make_shared<RealVfsFile>(path + DIR_SEP + name, perms)); | ||
| 141 | return files.back(); | ||
| 142 | } | ||
| 143 | |||
| 144 | bool RealVfsDirectory::DeleteSubdirectory(const std::string& name) { | ||
| 145 | return FileUtil::DeleteDirRecursively(path + DIR_SEP + name); | ||
| 146 | } | ||
| 147 | |||
| 148 | bool RealVfsDirectory::DeleteFile(const std::string& name) { | ||
| 149 | return FileUtil::Delete(path + DIR_SEP + name); | ||
| 150 | } | ||
| 151 | |||
| 152 | bool RealVfsDirectory::Rename(const std::string& name) { | ||
| 153 | return FileUtil::Rename(path, parent_path + DIR_SEP + name); | ||
| 154 | } | ||
| 155 | |||
| 156 | bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 157 | auto iter = std::find(files.begin(), files.end(), file); | ||
| 158 | if (iter == files.end()) | ||
| 159 | return false; | ||
| 160 | |||
| 161 | files[iter - files.begin()] = files.back(); | ||
| 162 | files.pop_back(); | ||
| 163 | |||
| 164 | subdirectories.emplace_back(dir); | ||
| 165 | |||
| 166 | return true; | ||
| 167 | } | ||
| 168 | } // namespace FileSys | ||
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h deleted file mode 100644 index 01717f485..000000000 --- a/src/core/file_sys/vfs_real.h +++ /dev/null | |||
| @@ -1,65 +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 "common/file_util.h" | ||
| 8 | #include "core/file_sys/filesystem.h" | ||
| 9 | #include "core/file_sys/vfs.h" | ||
| 10 | |||
| 11 | namespace FileSys { | ||
| 12 | |||
| 13 | // An implmentation of VfsFile that represents a file on the user's computer. | ||
| 14 | struct RealVfsFile : public VfsFile { | ||
| 15 | RealVfsFile(const std::string& name, Mode perms = Mode::Read); | ||
| 16 | |||
| 17 | std::string GetName() const override; | ||
| 18 | size_t GetSize() const override; | ||
| 19 | bool Resize(size_t new_size) override; | ||
| 20 | std::shared_ptr<VfsDirectory> GetContainingDirectory() const override; | ||
| 21 | bool IsWritable() const override; | ||
| 22 | bool IsReadable() const override; | ||
| 23 | size_t Read(u8* data, size_t length, size_t offset) const override; | ||
| 24 | size_t Write(const u8* data, size_t length, size_t offset) override; | ||
| 25 | bool Rename(const std::string& name) override; | ||
| 26 | |||
| 27 | private: | ||
| 28 | FileUtil::IOFile backing; | ||
| 29 | std::string path; | ||
| 30 | std::string parent_path; | ||
| 31 | std::vector<std::string> path_components; | ||
| 32 | std::vector<std::string> parent_components; | ||
| 33 | Mode perms; | ||
| 34 | }; | ||
| 35 | |||
| 36 | // An implementation of VfsDirectory that represents a directory on the user's computer. | ||
| 37 | struct RealVfsDirectory : public VfsDirectory { | ||
| 38 | RealVfsDirectory(const std::string& path, Mode perms); | ||
| 39 | |||
| 40 | std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; | ||
| 41 | std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override; | ||
| 42 | bool IsWritable() const override; | ||
| 43 | bool IsReadable() const override; | ||
| 44 | std::string GetName() const override; | ||
| 45 | std::shared_ptr<VfsDirectory> GetParentDirectory() const override; | ||
| 46 | std::shared_ptr<VfsDirectory> CreateSubdirectory(const std::string& name) override; | ||
| 47 | std::shared_ptr<VfsFile> CreateFile(const std::string& name) override; | ||
| 48 | bool DeleteSubdirectory(const std::string& name) override; | ||
| 49 | bool DeleteFile(const std::string& name) override; | ||
| 50 | bool Rename(const std::string& name) override; | ||
| 51 | |||
| 52 | protected: | ||
| 53 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 54 | |||
| 55 | private: | ||
| 56 | std::string path; | ||
| 57 | std::string parent_path; | ||
| 58 | std::vector<std::string> path_components; | ||
| 59 | std::vector<std::string> parent_components; | ||
| 60 | Mode perms; | ||
| 61 | std::vector<std::shared_ptr<VfsFile>> files; | ||
| 62 | std::vector<std::shared_ptr<VfsDirectory>> subdirectories; | ||
| 63 | }; | ||
| 64 | |||
| 65 | } // namespace FileSys | ||
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp index 7bb13c735..a871b3eaa 100644 --- a/src/core/hle/service/am/am.cpp +++ b/src/core/hle/service/am/am.cpp | |||
| @@ -621,7 +621,7 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { | |||
| 621 | IPC::ResponseBuilder rb{ctx, 4}; | 621 | IPC::ResponseBuilder rb{ctx, 4}; |
| 622 | 622 | ||
| 623 | FileSys::Path unused; | 623 | FileSys::Path unused; |
| 624 | auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData); | 624 | auto savedata = FileSystem::OpenFileSystem(FileSystem::Type::SaveData, unused); |
| 625 | if (savedata.Failed()) { | 625 | if (savedata.Failed()) { |
| 626 | // Create the save data and return an error indicating that the operation was performed. | 626 | // Create the save data and return an error indicating that the operation was performed. |
| 627 | FileSystem::FormatFileSystem(FileSystem::Type::SaveData); | 627 | FileSystem::FormatFileSystem(FileSystem::Type::SaveData); |
diff --git a/src/core/hle/service/audio/audout_u.cpp b/src/core/hle/service/audio/audout_u.cpp index 1b4b649d8..8bf273b22 100644 --- a/src/core/hle/service/audio/audout_u.cpp +++ b/src/core/hle/service/audio/audout_u.cpp | |||
| @@ -27,12 +27,12 @@ public: | |||
| 27 | {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, | 27 | {0, &IAudioOut::GetAudioOutState, "GetAudioOutState"}, |
| 28 | {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, | 28 | {1, &IAudioOut::StartAudioOut, "StartAudioOut"}, |
| 29 | {2, &IAudioOut::StopAudioOut, "StopAudioOut"}, | 29 | {2, &IAudioOut::StopAudioOut, "StopAudioOut"}, |
| 30 | {3, &IAudioOut::AppendAudioOutBuffer, "AppendAudioOutBuffer"}, | 30 | {3, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBuffer"}, |
| 31 | {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, | 31 | {4, &IAudioOut::RegisterBufferEvent, "RegisterBufferEvent"}, |
| 32 | {5, &IAudioOut::GetReleasedAudioOutBuffer, "GetReleasedAudioOutBuffer"}, | 32 | {5, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBuffer"}, |
| 33 | {6, nullptr, "ContainsAudioOutBuffer"}, | 33 | {6, nullptr, "ContainsAudioOutBuffer"}, |
| 34 | {7, nullptr, "AppendAudioOutBufferAuto"}, | 34 | {7, &IAudioOut::AppendAudioOutBufferImpl, "AppendAudioOutBufferAuto"}, |
| 35 | {8, nullptr, "GetReleasedAudioOutBufferAuto"}, | 35 | {8, &IAudioOut::GetReleasedAudioOutBufferImpl, "GetReleasedAudioOutBufferAuto"}, |
| 36 | {9, nullptr, "GetAudioOutBufferCount"}, | 36 | {9, nullptr, "GetAudioOutBufferCount"}, |
| 37 | {10, nullptr, "GetAudioOutPlayedSampleCount"}, | 37 | {10, nullptr, "GetAudioOutPlayedSampleCount"}, |
| 38 | {11, nullptr, "FlushAudioOutBuffers"}, | 38 | {11, nullptr, "FlushAudioOutBuffers"}, |
| @@ -96,7 +96,7 @@ private: | |||
| 96 | rb.PushCopyObjects(buffer_event); | 96 | rb.PushCopyObjects(buffer_event); |
| 97 | } | 97 | } |
| 98 | 98 | ||
| 99 | void AppendAudioOutBuffer(Kernel::HLERequestContext& ctx) { | 99 | void AppendAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { |
| 100 | LOG_WARNING(Service_Audio, "(STUBBED) called"); | 100 | LOG_WARNING(Service_Audio, "(STUBBED) called"); |
| 101 | IPC::RequestParser rp{ctx}; | 101 | IPC::RequestParser rp{ctx}; |
| 102 | 102 | ||
| @@ -107,7 +107,7 @@ private: | |||
| 107 | rb.Push(RESULT_SUCCESS); | 107 | rb.Push(RESULT_SUCCESS); |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | void GetReleasedAudioOutBuffer(Kernel::HLERequestContext& ctx) { | 110 | void GetReleasedAudioOutBufferImpl(Kernel::HLERequestContext& ctx) { |
| 111 | LOG_WARNING(Service_Audio, "(STUBBED) called"); | 111 | LOG_WARNING(Service_Audio, "(STUBBED) called"); |
| 112 | 112 | ||
| 113 | // TODO(st4rk): This is how libtransistor currently implements the | 113 | // TODO(st4rk): This is how libtransistor currently implements the |
| @@ -163,7 +163,7 @@ private: | |||
| 163 | AudioState audio_out_state; | 163 | AudioState audio_out_state; |
| 164 | }; | 164 | }; |
| 165 | 165 | ||
| 166 | void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { | 166 | void AudOutU::ListAudioOutsImpl(Kernel::HLERequestContext& ctx) { |
| 167 | LOG_WARNING(Service_Audio, "(STUBBED) called"); | 167 | LOG_WARNING(Service_Audio, "(STUBBED) called"); |
| 168 | IPC::RequestParser rp{ctx}; | 168 | IPC::RequestParser rp{ctx}; |
| 169 | 169 | ||
| @@ -179,7 +179,7 @@ void AudOutU::ListAudioOuts(Kernel::HLERequestContext& ctx) { | |||
| 179 | rb.Push<u32>(1); | 179 | rb.Push<u32>(1); |
| 180 | } | 180 | } |
| 181 | 181 | ||
| 182 | void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { | 182 | void AudOutU::OpenAudioOutImpl(Kernel::HLERequestContext& ctx) { |
| 183 | LOG_WARNING(Service_Audio, "(STUBBED) called"); | 183 | LOG_WARNING(Service_Audio, "(STUBBED) called"); |
| 184 | 184 | ||
| 185 | if (!audio_out_interface) { | 185 | if (!audio_out_interface) { |
| @@ -196,10 +196,10 @@ void AudOutU::OpenAudioOut(Kernel::HLERequestContext& ctx) { | |||
| 196 | } | 196 | } |
| 197 | 197 | ||
| 198 | AudOutU::AudOutU() : ServiceFramework("audout:u") { | 198 | AudOutU::AudOutU() : ServiceFramework("audout:u") { |
| 199 | static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOuts, "ListAudioOuts"}, | 199 | static const FunctionInfo functions[] = {{0, &AudOutU::ListAudioOutsImpl, "ListAudioOuts"}, |
| 200 | {1, &AudOutU::OpenAudioOut, "OpenAudioOut"}, | 200 | {1, &AudOutU::OpenAudioOutImpl, "OpenAudioOut"}, |
| 201 | {2, nullptr, "ListAudioOutsAuto"}, | 201 | {2, &AudOutU::ListAudioOutsImpl, "ListAudioOutsAuto"}, |
| 202 | {3, nullptr, "OpenAudioOutAuto"}}; | 202 | {3, &AudOutU::OpenAudioOutImpl, "OpenAudioOutAuto"}}; |
| 203 | RegisterHandlers(functions); | 203 | RegisterHandlers(functions); |
| 204 | } | 204 | } |
| 205 | 205 | ||
diff --git a/src/core/hle/service/audio/audout_u.h b/src/core/hle/service/audio/audout_u.h index 1f9bb9bcf..847d86aa6 100644 --- a/src/core/hle/service/audio/audout_u.h +++ b/src/core/hle/service/audio/audout_u.h | |||
| @@ -22,8 +22,8 @@ public: | |||
| 22 | private: | 22 | private: |
| 23 | std::shared_ptr<IAudioOut> audio_out_interface; | 23 | std::shared_ptr<IAudioOut> audio_out_interface; |
| 24 | 24 | ||
| 25 | void ListAudioOuts(Kernel::HLERequestContext& ctx); | 25 | void ListAudioOutsImpl(Kernel::HLERequestContext& ctx); |
| 26 | void OpenAudioOut(Kernel::HLERequestContext& ctx); | 26 | void OpenAudioOutImpl(Kernel::HLERequestContext& ctx); |
| 27 | 27 | ||
| 28 | enum class PcmFormat : u32 { | 28 | enum class PcmFormat : u32 { |
| 29 | Invalid = 0, | 29 | Invalid = 0, |
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp index 25810c165..f58b518b6 100644 --- a/src/core/hle/service/filesystem/filesystem.cpp +++ b/src/core/hle/service/filesystem/filesystem.cpp | |||
| @@ -2,221 +2,36 @@ | |||
| 2 | // Licensed under GPLv2 or any later version | 2 | // Licensed under GPLv2 or any later version |
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include "boost/container/flat_map.hpp" | 5 | #include <boost/container/flat_map.hpp> |
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/common_paths.h" | ||
| 8 | #include "common/file_util.h" | 6 | #include "common/file_util.h" |
| 9 | #include "core/core.h" | ||
| 10 | #include "core/file_sys/errors.h" | ||
| 11 | #include "core/file_sys/filesystem.h" | 7 | #include "core/file_sys/filesystem.h" |
| 12 | #include "core/file_sys/vfs.h" | 8 | #include "core/file_sys/savedata_factory.h" |
| 13 | #include "core/file_sys/vfs_offset.h" | 9 | #include "core/file_sys/sdmc_factory.h" |
| 14 | #include "core/file_sys/vfs_real.h" | ||
| 15 | #include "core/hle/kernel/process.h" | ||
| 16 | #include "core/hle/service/filesystem/filesystem.h" | 10 | #include "core/hle/service/filesystem/filesystem.h" |
| 17 | #include "core/hle/service/filesystem/fsp_srv.h" | 11 | #include "core/hle/service/filesystem/fsp_srv.h" |
| 18 | 12 | ||
| 19 | namespace Service::FileSystem { | 13 | namespace Service::FileSystem { |
| 20 | 14 | ||
| 21 | // Size of emulated sd card free space, reported in bytes. | ||
| 22 | // Just using 32GB because thats reasonable | ||
| 23 | // TODO(DarkLordZach): Eventually make this configurable in settings. | ||
| 24 | constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000; | ||
| 25 | |||
| 26 | static FileSys::VirtualDir GetDirectoryRelativeWrapped(FileSys::VirtualDir base, | ||
| 27 | const std::string& dir_name) { | ||
| 28 | if (dir_name == "." || dir_name == "" || dir_name == "/" || dir_name == "\\") | ||
| 29 | return base; | ||
| 30 | |||
| 31 | return base->GetDirectoryRelative(dir_name); | ||
| 32 | } | ||
| 33 | |||
| 34 | VfsDirectoryServiceWrapper::VfsDirectoryServiceWrapper(FileSys::VirtualDir backing_) | ||
| 35 | : backing(backing_) {} | ||
| 36 | |||
| 37 | std::string VfsDirectoryServiceWrapper::GetName() const { | ||
| 38 | return backing->GetName(); | ||
| 39 | } | ||
| 40 | |||
| 41 | ResultCode VfsDirectoryServiceWrapper::CreateFile(const std::string& path, u64 size) const { | ||
| 42 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 43 | auto file = dir->CreateFile(FileUtil::GetFilename(path)); | ||
| 44 | if (file == nullptr) | ||
| 45 | return ResultCode(-1); | ||
| 46 | if (!file->Resize(size)) | ||
| 47 | return ResultCode(-1); | ||
| 48 | return RESULT_SUCCESS; | ||
| 49 | } | ||
| 50 | |||
| 51 | ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path) const { | ||
| 52 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 53 | if (!backing->DeleteFile(FileUtil::GetFilename(path))) | ||
| 54 | return ResultCode(-1); | ||
| 55 | return RESULT_SUCCESS; | ||
| 56 | } | ||
| 57 | |||
| 58 | ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path) const { | ||
| 59 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 60 | if (dir == nullptr && FileUtil::GetFilename(FileUtil::GetParentPath(path)).empty()) | ||
| 61 | dir = backing; | ||
| 62 | auto new_dir = dir->CreateSubdirectory(FileUtil::GetFilename(path)); | ||
| 63 | if (new_dir == nullptr) | ||
| 64 | return ResultCode(-1); | ||
| 65 | return RESULT_SUCCESS; | ||
| 66 | } | ||
| 67 | |||
| 68 | ResultCode VfsDirectoryServiceWrapper::DeleteDirectory(const std::string& path) const { | ||
| 69 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 70 | if (!dir->DeleteSubdirectory(FileUtil::GetFilename(path))) | ||
| 71 | return ResultCode(-1); | ||
| 72 | return RESULT_SUCCESS; | ||
| 73 | } | ||
| 74 | |||
| 75 | ResultCode VfsDirectoryServiceWrapper::DeleteDirectoryRecursively(const std::string& path) const { | ||
| 76 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 77 | if (!dir->DeleteSubdirectoryRecursive(FileUtil::GetFilename(path))) | ||
| 78 | return ResultCode(-1); | ||
| 79 | return RESULT_SUCCESS; | ||
| 80 | } | ||
| 81 | |||
| 82 | ResultCode VfsDirectoryServiceWrapper::RenameFile(const std::string& src_path, | ||
| 83 | const std::string& dest_path) const { | ||
| 84 | auto src = backing->GetFileRelative(src_path); | ||
| 85 | if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { | ||
| 86 | // Use more-optimized vfs implementation rename. | ||
| 87 | if (src == nullptr) | ||
| 88 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 89 | if (!src->Rename(FileUtil::GetFilename(dest_path))) | ||
| 90 | return ResultCode(-1); | ||
| 91 | return RESULT_SUCCESS; | ||
| 92 | } | ||
| 93 | |||
| 94 | // Move by hand -- TODO(DarkLordZach): Optimize | ||
| 95 | auto c_res = CreateFile(dest_path, src->GetSize()); | ||
| 96 | if (c_res != RESULT_SUCCESS) | ||
| 97 | return c_res; | ||
| 98 | |||
| 99 | auto dest = backing->GetFileRelative(dest_path); | ||
| 100 | ASSERT_MSG(dest != nullptr, "Newly created file with success cannot be found."); | ||
| 101 | |||
| 102 | ASSERT_MSG(dest->WriteBytes(src->ReadAllBytes()) == src->GetSize(), | ||
| 103 | "Could not write all of the bytes but everything else has succeded."); | ||
| 104 | |||
| 105 | if (!src->GetContainingDirectory()->DeleteFile(FileUtil::GetFilename(src_path))) | ||
| 106 | return ResultCode(-1); | ||
| 107 | |||
| 108 | return RESULT_SUCCESS; | ||
| 109 | } | ||
| 110 | |||
| 111 | ResultCode VfsDirectoryServiceWrapper::RenameDirectory(const std::string& src_path, | ||
| 112 | const std::string& dest_path) const { | ||
| 113 | auto src = GetDirectoryRelativeWrapped(backing, src_path); | ||
| 114 | if (FileUtil::GetParentPath(src_path) == FileUtil::GetParentPath(dest_path)) { | ||
| 115 | // Use more-optimized vfs implementation rename. | ||
| 116 | if (src == nullptr) | ||
| 117 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 118 | if (!src->Rename(FileUtil::GetFilename(dest_path))) | ||
| 119 | return ResultCode(-1); | ||
| 120 | return RESULT_SUCCESS; | ||
| 121 | } | ||
| 122 | |||
| 123 | // TODO(DarkLordZach): Implement renaming across the tree (move). | ||
| 124 | ASSERT_MSG(false, | ||
| 125 | "Could not rename directory with path \"{}\" to new path \"{}\" because parent dirs " | ||
| 126 | "don't match -- UNIMPLEMENTED", | ||
| 127 | src_path, dest_path); | ||
| 128 | |||
| 129 | return ResultCode(-1); | ||
| 130 | } | ||
| 131 | |||
| 132 | ResultVal<FileSys::VirtualFile> VfsDirectoryServiceWrapper::OpenFile(const std::string& path, | ||
| 133 | FileSys::Mode mode) const { | ||
| 134 | auto file = backing->GetFileRelative(path); | ||
| 135 | if (file == nullptr) | ||
| 136 | return FileSys::ERROR_PATH_NOT_FOUND; | ||
| 137 | |||
| 138 | if ((static_cast<u32>(mode) & static_cast<u32>(FileSys::Mode::Append)) != 0) { | ||
| 139 | return MakeResult<FileSys::VirtualFile>( | ||
| 140 | std::make_shared<FileSys::OffsetVfsFile>(file, 0, file->GetSize())); | ||
| 141 | } | ||
| 142 | |||
| 143 | return MakeResult<FileSys::VirtualFile>(file); | ||
| 144 | } | ||
| 145 | |||
| 146 | ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const std::string& path) { | ||
| 147 | auto dir = GetDirectoryRelativeWrapped(backing, path); | ||
| 148 | if (dir == nullptr) | ||
| 149 | return ResultCode(-1); | ||
| 150 | return MakeResult(dir); | ||
| 151 | } | ||
| 152 | |||
| 153 | u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const { | ||
| 154 | if (backing->IsWritable()) | ||
| 155 | return EMULATED_SD_REPORTED_SIZE; | ||
| 156 | |||
| 157 | return 0; | ||
| 158 | } | ||
| 159 | |||
| 160 | ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( | ||
| 161 | const std::string& path) const { | ||
| 162 | auto dir = GetDirectoryRelativeWrapped(backing, FileUtil::GetParentPath(path)); | ||
| 163 | if (dir == nullptr) | ||
| 164 | return ResultCode(-1); | ||
| 165 | auto filename = FileUtil::GetFilename(path); | ||
| 166 | if (dir->GetFile(filename) != nullptr) | ||
| 167 | return MakeResult(FileSys::EntryType::File); | ||
| 168 | if (dir->GetSubdirectory(filename) != nullptr) | ||
| 169 | return MakeResult(FileSys::EntryType::Directory); | ||
| 170 | return ResultCode(-1); | ||
| 171 | } | ||
| 172 | |||
| 173 | // A deferred filesystem for nand save data. | ||
| 174 | // This must be deferred because the directory is dependent on title id, which is not set at | ||
| 175 | // registration time. | ||
| 176 | struct SaveDataDeferredFilesystem : DeferredFilesystem { | ||
| 177 | protected: | ||
| 178 | FileSys::VirtualDir CreateFilesystem() override { | ||
| 179 | u64 title_id = Core::CurrentProcess()->program_id; | ||
| 180 | // TODO(DarkLordZach): Users | ||
| 181 | u32 user_id = 0; | ||
| 182 | std::string nand_directory = fmt::format( | ||
| 183 | "{}save/{:016X}/{:08X}/", FileUtil::GetUserPath(D_NAND_IDX), title_id, user_id); | ||
| 184 | |||
| 185 | auto savedata = | ||
| 186 | std::make_shared<FileSys::RealVfsDirectory>(nand_directory, FileSys::Mode::Write); | ||
| 187 | return savedata; | ||
| 188 | } | ||
| 189 | }; | ||
| 190 | |||
| 191 | /** | 15 | /** |
| 192 | * Map of registered file systems, identified by type. Once an file system is registered here, it | 16 | * Map of registered file systems, identified by type. Once an file system is registered here, it |
| 193 | * is never removed until UnregisterFileSystems is called. | 17 | * is never removed until UnregisterFileSystems is called. |
| 194 | */ | 18 | */ |
| 195 | static boost::container::flat_map<Type, std::unique_ptr<DeferredFilesystem>> filesystem_map; | 19 | static boost::container::flat_map<Type, std::unique_ptr<FileSys::FileSystemFactory>> filesystem_map; |
| 196 | static FileSys::VirtualFile filesystem_romfs = nullptr; | ||
| 197 | 20 | ||
| 198 | ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& factory, Type type) { | 21 | ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type) { |
| 199 | auto result = filesystem_map.emplace(type, std::move(factory)); | 22 | auto result = filesystem_map.emplace(type, std::move(factory)); |
| 200 | 23 | ||
| 201 | bool inserted = result.second; | 24 | bool inserted = result.second; |
| 202 | ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); | 25 | ASSERT_MSG(inserted, "Tried to register more than one system with same id code"); |
| 203 | 26 | ||
| 204 | auto& filesystem = result.first->second; | 27 | auto& filesystem = result.first->second; |
| 205 | LOG_DEBUG(Service_FS, "Registered file system with id code 0x{:08X}", static_cast<u32>(type)); | ||
| 206 | return RESULT_SUCCESS; | ||
| 207 | } | ||
| 208 | |||
| 209 | ResultCode RegisterRomFS(FileSys::VirtualFile filesystem) { | ||
| 210 | ASSERT_MSG(filesystem_romfs == nullptr, | ||
| 211 | "Tried to register more than one system with same id code"); | ||
| 212 | |||
| 213 | filesystem_romfs = filesystem; | ||
| 214 | LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(), | 28 | LOG_DEBUG(Service_FS, "Registered file system {} with id code 0x{:08X}", filesystem->GetName(), |
| 215 | static_cast<u32>(Type::RomFS)); | 29 | static_cast<u32>(type)); |
| 216 | return RESULT_SUCCESS; | 30 | return RESULT_SUCCESS; |
| 217 | } | 31 | } |
| 218 | 32 | ||
| 219 | ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type) { | 33 | ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, |
| 34 | FileSys::Path& path) { | ||
| 220 | LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type)); | 35 | LOG_TRACE(Service_FS, "Opening FileSystem with type={}", static_cast<u32>(type)); |
| 221 | 36 | ||
| 222 | auto itr = filesystem_map.find(type); | 37 | auto itr = filesystem_map.find(type); |
| @@ -225,13 +40,7 @@ ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type) { | |||
| 225 | return ResultCode(-1); | 40 | return ResultCode(-1); |
| 226 | } | 41 | } |
| 227 | 42 | ||
| 228 | return MakeResult(itr->second->Get()); | 43 | return itr->second->Open(path); |
| 229 | } | ||
| 230 | |||
| 231 | ResultVal<FileSys::VirtualFile> OpenRomFS() { | ||
| 232 | if (filesystem_romfs == nullptr) | ||
| 233 | return ResultCode(-1); | ||
| 234 | return MakeResult(filesystem_romfs); | ||
| 235 | } | 44 | } |
| 236 | 45 | ||
| 237 | ResultCode FormatFileSystem(Type type) { | 46 | ResultCode FormatFileSystem(Type type) { |
| @@ -243,21 +52,21 @@ ResultCode FormatFileSystem(Type type) { | |||
| 243 | return ResultCode(-1); | 52 | return ResultCode(-1); |
| 244 | } | 53 | } |
| 245 | 54 | ||
| 246 | return itr->second->Get()->GetParentDirectory()->DeleteSubdirectory( | 55 | FileSys::Path unused; |
| 247 | itr->second->Get()->GetName()) | 56 | return itr->second->Format(unused); |
| 248 | ? RESULT_SUCCESS | ||
| 249 | : ResultCode(-1); | ||
| 250 | } | 57 | } |
| 251 | 58 | ||
| 252 | void RegisterFileSystems() { | 59 | void RegisterFileSystems() { |
| 253 | filesystem_map.clear(); | 60 | filesystem_map.clear(); |
| 254 | filesystem_romfs = nullptr; | ||
| 255 | 61 | ||
| 62 | std::string nand_directory = FileUtil::GetUserPath(D_NAND_IDX); | ||
| 256 | std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX); | 63 | std::string sd_directory = FileUtil::GetUserPath(D_SDMC_IDX); |
| 257 | auto sdcard = std::make_shared<FileSys::RealVfsDirectory>(sd_directory, FileSys::Mode::Write); | ||
| 258 | RegisterFileSystem(std::make_unique<DeferredFilesystem>(sdcard), Type::SDMC); | ||
| 259 | 64 | ||
| 260 | RegisterFileSystem(std::make_unique<SaveDataDeferredFilesystem>(), Type::SaveData); | 65 | auto savedata = std::make_unique<FileSys::SaveData_Factory>(std::move(nand_directory)); |
| 66 | RegisterFileSystem(std::move(savedata), Type::SaveData); | ||
| 67 | |||
| 68 | auto sdcard = std::make_unique<FileSys::SDMC_Factory>(std::move(sd_directory)); | ||
| 69 | RegisterFileSystem(std::move(sdcard), Type::SDMC); | ||
| 261 | } | 70 | } |
| 262 | 71 | ||
| 263 | void InstallInterfaces(SM::ServiceManager& service_manager) { | 72 | void InstallInterfaces(SM::ServiceManager& service_manager) { |
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h index d3de797f1..56d26146e 100644 --- a/src/core/hle/service/filesystem/filesystem.h +++ b/src/core/hle/service/filesystem/filesystem.h | |||
| @@ -6,8 +6,6 @@ | |||
| 6 | 6 | ||
| 7 | #include <memory> | 7 | #include <memory> |
| 8 | #include "common/common_types.h" | 8 | #include "common/common_types.h" |
| 9 | #include "core/file_sys/filesystem.h" | ||
| 10 | #include "core/file_sys/vfs.h" | ||
| 11 | #include "core/hle/result.h" | 9 | #include "core/hle/result.h" |
| 12 | 10 | ||
| 13 | namespace FileSys { | 11 | namespace FileSys { |
| @@ -31,136 +29,12 @@ enum class Type { | |||
| 31 | SDMC = 3, | 29 | SDMC = 3, |
| 32 | }; | 30 | }; |
| 33 | 31 | ||
| 34 | // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of | ||
| 35 | // pointers and booleans. This makes using a VfsDirectory with switch services much easier and | ||
| 36 | // avoids repetitive code. | ||
| 37 | class VfsDirectoryServiceWrapper { | ||
| 38 | public: | ||
| 39 | explicit VfsDirectoryServiceWrapper(FileSys::VirtualDir backing); | ||
| 40 | |||
| 41 | /** | ||
| 42 | * Get a descriptive name for the archive (e.g. "RomFS", "SaveData", etc.) | ||
| 43 | */ | ||
| 44 | std::string GetName() const; | ||
| 45 | |||
| 46 | /** | ||
| 47 | * Create a file specified by its path | ||
| 48 | * @param path Path relative to the Archive | ||
| 49 | * @param size The size of the new file, filled with zeroes | ||
| 50 | * @return Result of the operation | ||
| 51 | */ | ||
| 52 | ResultCode CreateFile(const std::string& path, u64 size) const; | ||
| 53 | |||
| 54 | /** | ||
| 55 | * Delete a file specified by its path | ||
| 56 | * @param path Path relative to the archive | ||
| 57 | * @return Result of the operation | ||
| 58 | */ | ||
| 59 | ResultCode DeleteFile(const std::string& path) const; | ||
| 60 | |||
| 61 | /** | ||
| 62 | * Create a directory specified by its path | ||
| 63 | * @param path Path relative to the archive | ||
| 64 | * @return Result of the operation | ||
| 65 | */ | ||
| 66 | ResultCode CreateDirectory(const std::string& path) const; | ||
| 67 | |||
| 68 | /** | ||
| 69 | * Delete a directory specified by its path | ||
| 70 | * @param path Path relative to the archive | ||
| 71 | * @return Result of the operation | ||
| 72 | */ | ||
| 73 | ResultCode DeleteDirectory(const std::string& path) const; | ||
| 74 | |||
| 75 | /** | ||
| 76 | * Delete a directory specified by its path and anything under it | ||
| 77 | * @param path Path relative to the archive | ||
| 78 | * @return Result of the operation | ||
| 79 | */ | ||
| 80 | ResultCode DeleteDirectoryRecursively(const std::string& path) const; | ||
| 81 | |||
| 82 | /** | ||
| 83 | * Rename a File specified by its path | ||
| 84 | * @param src_path Source path relative to the archive | ||
| 85 | * @param dest_path Destination path relative to the archive | ||
| 86 | * @return Result of the operation | ||
| 87 | */ | ||
| 88 | ResultCode RenameFile(const std::string& src_path, const std::string& dest_path) const; | ||
| 89 | |||
| 90 | /** | ||
| 91 | * Rename a Directory specified by its path | ||
| 92 | * @param src_path Source path relative to the archive | ||
| 93 | * @param dest_path Destination path relative to the archive | ||
| 94 | * @return Result of the operation | ||
| 95 | */ | ||
| 96 | ResultCode RenameDirectory(const std::string& src_path, const std::string& dest_path) const; | ||
| 97 | |||
| 98 | /** | ||
| 99 | * Open a file specified by its path, using the specified mode | ||
| 100 | * @param path Path relative to the archive | ||
| 101 | * @param mode Mode to open the file with | ||
| 102 | * @return Opened file, or error code | ||
| 103 | */ | ||
| 104 | ResultVal<FileSys::VirtualFile> OpenFile(const std::string& path, FileSys::Mode mode) const; | ||
| 105 | |||
| 106 | /** | ||
| 107 | * Open a directory specified by its path | ||
| 108 | * @param path Path relative to the archive | ||
| 109 | * @return Opened directory, or error code | ||
| 110 | */ | ||
| 111 | ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path); | ||
| 112 | |||
| 113 | /** | ||
| 114 | * Get the free space | ||
| 115 | * @return The number of free bytes in the archive | ||
| 116 | */ | ||
| 117 | u64 GetFreeSpaceSize() const; | ||
| 118 | |||
| 119 | /** | ||
| 120 | * Get the type of the specified path | ||
| 121 | * @return The type of the specified path or error code | ||
| 122 | */ | ||
| 123 | ResultVal<FileSys::EntryType> GetEntryType(const std::string& path) const; | ||
| 124 | |||
| 125 | private: | ||
| 126 | FileSys::VirtualDir backing; | ||
| 127 | }; | ||
| 128 | |||
| 129 | // A class that deferres the creation of a filesystem until a later time. | ||
| 130 | // This is useful if construction depends on a variable not known when the filesystem is registered. | ||
| 131 | // Construct this with a filesystem (VirtualDir) to avoid the deferrence feature or override the | ||
| 132 | // CreateFilesystem method which will be called on first use. | ||
| 133 | struct DeferredFilesystem { | ||
| 134 | DeferredFilesystem() = default; | ||
| 135 | |||
| 136 | explicit DeferredFilesystem(FileSys::VirtualDir vfs_directory) : fs(std::move(vfs_directory)) {} | ||
| 137 | |||
| 138 | FileSys::VirtualDir Get() { | ||
| 139 | if (fs == nullptr) | ||
| 140 | fs = CreateFilesystem(); | ||
| 141 | |||
| 142 | return fs; | ||
| 143 | } | ||
| 144 | |||
| 145 | virtual ~DeferredFilesystem() = default; | ||
| 146 | |||
| 147 | protected: | ||
| 148 | virtual FileSys::VirtualDir CreateFilesystem() { | ||
| 149 | return fs; | ||
| 150 | } | ||
| 151 | |||
| 152 | private: | ||
| 153 | FileSys::VirtualDir fs; | ||
| 154 | }; | ||
| 155 | |||
| 156 | /** | 32 | /** |
| 157 | * Registers a FileSystem, instances of which can later be opened using its IdCode. | 33 | * Registers a FileSystem, instances of which can later be opened using its IdCode. |
| 158 | * @param factory FileSystem backend interface to use | 34 | * @param factory FileSystem backend interface to use |
| 159 | * @param type Type used to access this type of FileSystem | 35 | * @param type Type used to access this type of FileSystem |
| 160 | */ | 36 | */ |
| 161 | ResultCode RegisterFileSystem(std::unique_ptr<DeferredFilesystem>&& fs, Type type); | 37 | ResultCode RegisterFileSystem(std::unique_ptr<FileSys::FileSystemFactory>&& factory, Type type); |
| 162 | |||
| 163 | ResultCode RegisterRomFS(FileSys::VirtualFile fs); | ||
| 164 | 38 | ||
| 165 | /** | 39 | /** |
| 166 | * Opens a file system | 40 | * Opens a file system |
| @@ -168,9 +42,8 @@ ResultCode RegisterRomFS(FileSys::VirtualFile fs); | |||
| 168 | * @param path Path to the file system, used with Binary paths | 42 | * @param path Path to the file system, used with Binary paths |
| 169 | * @return FileSys::FileSystemBackend interface to the file system | 43 | * @return FileSys::FileSystemBackend interface to the file system |
| 170 | */ | 44 | */ |
| 171 | ResultVal<FileSys::VirtualDir> OpenFileSystem(Type type); | 45 | ResultVal<std::unique_ptr<FileSys::FileSystemBackend>> OpenFileSystem(Type type, |
| 172 | 46 | FileSys::Path& path); | |
| 173 | ResultVal<FileSys::VirtualFile> OpenRomFS(); | ||
| 174 | 47 | ||
| 175 | /** | 48 | /** |
| 176 | * Formats a file system | 49 | * Formats a file system |
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp index 26e8a8c88..82efe7f7d 100644 --- a/src/core/hle/service/filesystem/fsp_srv.cpp +++ b/src/core/hle/service/filesystem/fsp_srv.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/string_util.h" | 7 | #include "common/string_util.h" |
| 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/filesystem.h" | 11 | #include "core/file_sys/filesystem.h" |
| 11 | #include "core/file_sys/storage.h" | 12 | #include "core/file_sys/storage.h" |
| 12 | #include "core/hle/ipc_helpers.h" | 13 | #include "core/hle/ipc_helpers.h" |
| @@ -19,8 +20,8 @@ namespace Service::FileSystem { | |||
| 19 | 20 | ||
| 20 | class IStorage final : public ServiceFramework<IStorage> { | 21 | class IStorage final : public ServiceFramework<IStorage> { |
| 21 | public: | 22 | public: |
| 22 | IStorage(FileSys::VirtualFile backend_) | 23 | IStorage(std::unique_ptr<FileSys::StorageBackend>&& backend) |
| 23 | : ServiceFramework("IStorage"), backend(std::move(backend_)) { | 24 | : ServiceFramework("IStorage"), backend(std::move(backend)) { |
| 24 | static const FunctionInfo functions[] = { | 25 | static const FunctionInfo functions[] = { |
| 25 | {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, | 26 | {0, &IStorage::Read, "Read"}, {1, nullptr, "Write"}, {2, nullptr, "Flush"}, |
| 26 | {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"}, | 27 | {3, nullptr, "SetSize"}, {4, nullptr, "GetSize"}, {5, nullptr, "OperateRange"}, |
| @@ -29,7 +30,7 @@ public: | |||
| 29 | } | 30 | } |
| 30 | 31 | ||
| 31 | private: | 32 | private: |
| 32 | FileSys::VirtualFile backend; | 33 | std::unique_ptr<FileSys::StorageBackend> backend; |
| 33 | 34 | ||
| 34 | void Read(Kernel::HLERequestContext& ctx) { | 35 | void Read(Kernel::HLERequestContext& ctx) { |
| 35 | IPC::RequestParser rp{ctx}; | 36 | IPC::RequestParser rp{ctx}; |
| @@ -51,8 +52,8 @@ private: | |||
| 51 | } | 52 | } |
| 52 | 53 | ||
| 53 | // Read the data from the Storage backend | 54 | // Read the data from the Storage backend |
| 54 | std::vector<u8> output = backend->ReadBytes(length, offset); | 55 | std::vector<u8> output(length); |
| 55 | auto res = MakeResult<size_t>(output.size()); | 56 | ResultVal<size_t> res = backend->Read(offset, length, output.data()); |
| 56 | if (res.Failed()) { | 57 | if (res.Failed()) { |
| 57 | IPC::ResponseBuilder rb{ctx, 2}; | 58 | IPC::ResponseBuilder rb{ctx, 2}; |
| 58 | rb.Push(res.Code()); | 59 | rb.Push(res.Code()); |
| @@ -69,8 +70,8 @@ private: | |||
| 69 | 70 | ||
| 70 | class IFile final : public ServiceFramework<IFile> { | 71 | class IFile final : public ServiceFramework<IFile> { |
| 71 | public: | 72 | public: |
| 72 | explicit IFile(FileSys::VirtualFile backend_) | 73 | explicit IFile(std::unique_ptr<FileSys::StorageBackend>&& backend) |
| 73 | : ServiceFramework("IFile"), backend(std::move(backend_)) { | 74 | : ServiceFramework("IFile"), backend(std::move(backend)) { |
| 74 | static const FunctionInfo functions[] = { | 75 | static const FunctionInfo functions[] = { |
| 75 | {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, | 76 | {0, &IFile::Read, "Read"}, {1, &IFile::Write, "Write"}, |
| 76 | {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"}, | 77 | {2, &IFile::Flush, "Flush"}, {3, &IFile::SetSize, "SetSize"}, |
| @@ -80,7 +81,7 @@ public: | |||
| 80 | } | 81 | } |
| 81 | 82 | ||
| 82 | private: | 83 | private: |
| 83 | FileSys::VirtualFile backend; | 84 | std::unique_ptr<FileSys::StorageBackend> backend; |
| 84 | 85 | ||
| 85 | void Read(Kernel::HLERequestContext& ctx) { | 86 | void Read(Kernel::HLERequestContext& ctx) { |
| 86 | IPC::RequestParser rp{ctx}; | 87 | IPC::RequestParser rp{ctx}; |
| @@ -103,8 +104,8 @@ private: | |||
| 103 | } | 104 | } |
| 104 | 105 | ||
| 105 | // Read the data from the Storage backend | 106 | // Read the data from the Storage backend |
| 106 | std::vector<u8> output = backend->ReadBytes(length, offset); | 107 | std::vector<u8> output(length); |
| 107 | auto res = MakeResult<size_t>(output.size()); | 108 | ResultVal<size_t> res = backend->Read(offset, length, output.data()); |
| 108 | if (res.Failed()) { | 109 | if (res.Failed()) { |
| 109 | IPC::ResponseBuilder rb{ctx, 2}; | 110 | IPC::ResponseBuilder rb{ctx, 2}; |
| 110 | rb.Push(res.Code()); | 111 | rb.Push(res.Code()); |
| @@ -139,10 +140,9 @@ private: | |||
| 139 | return; | 140 | return; |
| 140 | } | 141 | } |
| 141 | 142 | ||
| 142 | std::vector<u8> data = ctx.ReadBuffer(); | ||
| 143 | data.resize(length); | ||
| 144 | // Write the data to the Storage backend | 143 | // Write the data to the Storage backend |
| 145 | auto res = MakeResult<size_t>(backend->WriteBytes(data, offset)); | 144 | std::vector<u8> data = ctx.ReadBuffer(); |
| 145 | ResultVal<size_t> res = backend->Write(offset, length, true, data.data()); | ||
| 146 | if (res.Failed()) { | 146 | if (res.Failed()) { |
| 147 | IPC::ResponseBuilder rb{ctx, 2}; | 147 | IPC::ResponseBuilder rb{ctx, 2}; |
| 148 | rb.Push(res.Code()); | 148 | rb.Push(res.Code()); |
| @@ -155,8 +155,7 @@ private: | |||
| 155 | 155 | ||
| 156 | void Flush(Kernel::HLERequestContext& ctx) { | 156 | void Flush(Kernel::HLERequestContext& ctx) { |
| 157 | LOG_DEBUG(Service_FS, "called"); | 157 | LOG_DEBUG(Service_FS, "called"); |
| 158 | 158 | backend->Flush(); | |
| 159 | // Exists for SDK compatibiltity -- No need to flush file. | ||
| 160 | 159 | ||
| 161 | IPC::ResponseBuilder rb{ctx, 2}; | 160 | IPC::ResponseBuilder rb{ctx, 2}; |
| 162 | rb.Push(RESULT_SUCCESS); | 161 | rb.Push(RESULT_SUCCESS); |
| @@ -165,7 +164,7 @@ private: | |||
| 165 | void SetSize(Kernel::HLERequestContext& ctx) { | 164 | void SetSize(Kernel::HLERequestContext& ctx) { |
| 166 | IPC::RequestParser rp{ctx}; | 165 | IPC::RequestParser rp{ctx}; |
| 167 | const u64 size = rp.Pop<u64>(); | 166 | const u64 size = rp.Pop<u64>(); |
| 168 | backend->Resize(size); | 167 | backend->SetSize(size); |
| 169 | LOG_DEBUG(Service_FS, "called, size={}", size); | 168 | LOG_DEBUG(Service_FS, "called, size={}", size); |
| 170 | 169 | ||
| 171 | IPC::ResponseBuilder rb{ctx, 2}; | 170 | IPC::ResponseBuilder rb{ctx, 2}; |
| @@ -182,38 +181,19 @@ private: | |||
| 182 | } | 181 | } |
| 183 | }; | 182 | }; |
| 184 | 183 | ||
| 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 | |||
| 198 | class IDirectory final : public ServiceFramework<IDirectory> { | 184 | class IDirectory final : public ServiceFramework<IDirectory> { |
| 199 | public: | 185 | public: |
| 200 | explicit IDirectory(FileSys::VirtualDir backend_) | 186 | explicit IDirectory(std::unique_ptr<FileSys::DirectoryBackend>&& backend) |
| 201 | : ServiceFramework("IDirectory"), backend(std::move(backend_)) { | 187 | : ServiceFramework("IDirectory"), backend(std::move(backend)) { |
| 202 | static const FunctionInfo functions[] = { | 188 | static const FunctionInfo functions[] = { |
| 203 | {0, &IDirectory::Read, "Read"}, | 189 | {0, &IDirectory::Read, "Read"}, |
| 204 | {1, &IDirectory::GetEntryCount, "GetEntryCount"}, | 190 | {1, &IDirectory::GetEntryCount, "GetEntryCount"}, |
| 205 | }; | 191 | }; |
| 206 | RegisterHandlers(functions); | 192 | RegisterHandlers(functions); |
| 207 | |||
| 208 | // Build entry index now to save time later. | ||
| 209 | BuildEntryIndex(entries, backend->GetFiles(), FileSys::File); | ||
| 210 | BuildEntryIndex(entries, backend->GetSubdirectories(), FileSys::Directory); | ||
| 211 | } | 193 | } |
| 212 | 194 | ||
| 213 | private: | 195 | private: |
| 214 | FileSys::VirtualDir backend; | 196 | std::unique_ptr<FileSys::DirectoryBackend> backend; |
| 215 | std::vector<FileSys::Entry> entries; | ||
| 216 | u64 next_entry_index = 0; | ||
| 217 | 197 | ||
| 218 | void Read(Kernel::HLERequestContext& ctx) { | 198 | void Read(Kernel::HLERequestContext& ctx) { |
| 219 | IPC::RequestParser rp{ctx}; | 199 | IPC::RequestParser rp{ctx}; |
| @@ -224,31 +204,26 @@ private: | |||
| 224 | // Calculate how many entries we can fit in the output buffer | 204 | // Calculate how many entries we can fit in the output buffer |
| 225 | u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); | 205 | u64 count_entries = ctx.GetWriteBufferSize() / sizeof(FileSys::Entry); |
| 226 | 206 | ||
| 227 | // Cap at total number of entries. | ||
| 228 | u64 actual_entries = std::min(count_entries, entries.size() - next_entry_index); | ||
| 229 | |||
| 230 | // Read the data from the Directory backend | 207 | // Read the data from the Directory backend |
| 231 | std::vector<FileSys::Entry> entry_data(entries.begin() + next_entry_index, | 208 | std::vector<FileSys::Entry> entries(count_entries); |
| 232 | entries.begin() + next_entry_index + actual_entries); | 209 | u64 read_entries = backend->Read(count_entries, entries.data()); |
| 233 | |||
| 234 | next_entry_index += actual_entries; | ||
| 235 | 210 | ||
| 236 | // Convert the data into a byte array | 211 | // Convert the data into a byte array |
| 237 | std::vector<u8> output(entry_data.size() * sizeof(FileSys::Entry)); | 212 | std::vector<u8> output(entries.size() * sizeof(FileSys::Entry)); |
| 238 | std::memcpy(output.data(), entry_data.data(), output.size()); | 213 | std::memcpy(output.data(), entries.data(), output.size()); |
| 239 | 214 | ||
| 240 | // Write the data to memory | 215 | // Write the data to memory |
| 241 | ctx.WriteBuffer(output); | 216 | ctx.WriteBuffer(output); |
| 242 | 217 | ||
| 243 | IPC::ResponseBuilder rb{ctx, 4}; | 218 | IPC::ResponseBuilder rb{ctx, 4}; |
| 244 | rb.Push(RESULT_SUCCESS); | 219 | rb.Push(RESULT_SUCCESS); |
| 245 | rb.Push(actual_entries); | 220 | rb.Push(read_entries); |
| 246 | } | 221 | } |
| 247 | 222 | ||
| 248 | void GetEntryCount(Kernel::HLERequestContext& ctx) { | 223 | void GetEntryCount(Kernel::HLERequestContext& ctx) { |
| 249 | LOG_DEBUG(Service_FS, "called"); | 224 | LOG_DEBUG(Service_FS, "called"); |
| 250 | 225 | ||
| 251 | u64 count = entries.size() - next_entry_index; | 226 | u64 count = backend->GetEntryCount(); |
| 252 | 227 | ||
| 253 | IPC::ResponseBuilder rb{ctx, 4}; | 228 | IPC::ResponseBuilder rb{ctx, 4}; |
| 254 | rb.Push(RESULT_SUCCESS); | 229 | rb.Push(RESULT_SUCCESS); |
| @@ -258,7 +233,7 @@ private: | |||
| 258 | 233 | ||
| 259 | class IFileSystem final : public ServiceFramework<IFileSystem> { | 234 | class IFileSystem final : public ServiceFramework<IFileSystem> { |
| 260 | public: | 235 | public: |
| 261 | explicit IFileSystem(FileSys::VirtualDir backend) | 236 | explicit IFileSystem(std::unique_ptr<FileSys::FileSystemBackend>&& backend) |
| 262 | : ServiceFramework("IFileSystem"), backend(std::move(backend)) { | 237 | : ServiceFramework("IFileSystem"), backend(std::move(backend)) { |
| 263 | static const FunctionInfo functions[] = { | 238 | static const FunctionInfo functions[] = { |
| 264 | {0, &IFileSystem::CreateFile, "CreateFile"}, | 239 | {0, &IFileSystem::CreateFile, "CreateFile"}, |
| @@ -293,7 +268,7 @@ public: | |||
| 293 | LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); | 268 | LOG_DEBUG(Service_FS, "called file {} mode 0x{:X} size 0x{:08X}", name, mode, size); |
| 294 | 269 | ||
| 295 | IPC::ResponseBuilder rb{ctx, 2}; | 270 | IPC::ResponseBuilder rb{ctx, 2}; |
| 296 | rb.Push(backend.CreateFile(name, size)); | 271 | rb.Push(backend->CreateFile(name, size)); |
| 297 | } | 272 | } |
| 298 | 273 | ||
| 299 | void DeleteFile(Kernel::HLERequestContext& ctx) { | 274 | void DeleteFile(Kernel::HLERequestContext& ctx) { |
| @@ -305,7 +280,7 @@ public: | |||
| 305 | LOG_DEBUG(Service_FS, "called file {}", name); | 280 | LOG_DEBUG(Service_FS, "called file {}", name); |
| 306 | 281 | ||
| 307 | IPC::ResponseBuilder rb{ctx, 2}; | 282 | IPC::ResponseBuilder rb{ctx, 2}; |
| 308 | rb.Push(backend.DeleteFile(name)); | 283 | rb.Push(backend->DeleteFile(name)); |
| 309 | } | 284 | } |
| 310 | 285 | ||
| 311 | void CreateDirectory(Kernel::HLERequestContext& ctx) { | 286 | void CreateDirectory(Kernel::HLERequestContext& ctx) { |
| @@ -317,7 +292,7 @@ public: | |||
| 317 | LOG_DEBUG(Service_FS, "called directory {}", name); | 292 | LOG_DEBUG(Service_FS, "called directory {}", name); |
| 318 | 293 | ||
| 319 | IPC::ResponseBuilder rb{ctx, 2}; | 294 | IPC::ResponseBuilder rb{ctx, 2}; |
| 320 | rb.Push(backend.CreateDirectory(name)); | 295 | rb.Push(backend->CreateDirectory(name)); |
| 321 | } | 296 | } |
| 322 | 297 | ||
| 323 | void RenameFile(Kernel::HLERequestContext& ctx) { | 298 | void RenameFile(Kernel::HLERequestContext& ctx) { |
| @@ -335,7 +310,7 @@ public: | |||
| 335 | LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); | 310 | LOG_DEBUG(Service_FS, "called file '{}' to file '{}'", src_name, dst_name); |
| 336 | 311 | ||
| 337 | IPC::ResponseBuilder rb{ctx, 2}; | 312 | IPC::ResponseBuilder rb{ctx, 2}; |
| 338 | rb.Push(backend.RenameFile(src_name, dst_name)); | 313 | rb.Push(backend->RenameFile(src_name, dst_name)); |
| 339 | } | 314 | } |
| 340 | 315 | ||
| 341 | void OpenFile(Kernel::HLERequestContext& ctx) { | 316 | void OpenFile(Kernel::HLERequestContext& ctx) { |
| @@ -348,14 +323,14 @@ public: | |||
| 348 | 323 | ||
| 349 | LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); | 324 | LOG_DEBUG(Service_FS, "called file {} mode {}", name, static_cast<u32>(mode)); |
| 350 | 325 | ||
| 351 | auto result = backend.OpenFile(name, mode); | 326 | auto result = backend->OpenFile(name, mode); |
| 352 | if (result.Failed()) { | 327 | if (result.Failed()) { |
| 353 | IPC::ResponseBuilder rb{ctx, 2}; | 328 | IPC::ResponseBuilder rb{ctx, 2}; |
| 354 | rb.Push(result.Code()); | 329 | rb.Push(result.Code()); |
| 355 | return; | 330 | return; |
| 356 | } | 331 | } |
| 357 | 332 | ||
| 358 | IFile file(result.Unwrap()); | 333 | auto file = std::move(result.Unwrap()); |
| 359 | 334 | ||
| 360 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 335 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 361 | rb.Push(RESULT_SUCCESS); | 336 | rb.Push(RESULT_SUCCESS); |
| @@ -373,14 +348,14 @@ public: | |||
| 373 | 348 | ||
| 374 | LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); | 349 | LOG_DEBUG(Service_FS, "called directory {} filter {}", name, filter_flags); |
| 375 | 350 | ||
| 376 | auto result = backend.OpenDirectory(name); | 351 | auto result = backend->OpenDirectory(name); |
| 377 | if (result.Failed()) { | 352 | if (result.Failed()) { |
| 378 | IPC::ResponseBuilder rb{ctx, 2}; | 353 | IPC::ResponseBuilder rb{ctx, 2}; |
| 379 | rb.Push(result.Code()); | 354 | rb.Push(result.Code()); |
| 380 | return; | 355 | return; |
| 381 | } | 356 | } |
| 382 | 357 | ||
| 383 | IDirectory directory(result.Unwrap()); | 358 | auto directory = std::move(result.Unwrap()); |
| 384 | 359 | ||
| 385 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 360 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 386 | rb.Push(RESULT_SUCCESS); | 361 | rb.Push(RESULT_SUCCESS); |
| @@ -395,7 +370,7 @@ public: | |||
| 395 | 370 | ||
| 396 | LOG_DEBUG(Service_FS, "called file {}", name); | 371 | LOG_DEBUG(Service_FS, "called file {}", name); |
| 397 | 372 | ||
| 398 | auto result = backend.GetEntryType(name); | 373 | auto result = backend->GetEntryType(name); |
| 399 | if (result.Failed()) { | 374 | if (result.Failed()) { |
| 400 | IPC::ResponseBuilder rb{ctx, 2}; | 375 | IPC::ResponseBuilder rb{ctx, 2}; |
| 401 | rb.Push(result.Code()); | 376 | rb.Push(result.Code()); |
| @@ -415,7 +390,7 @@ public: | |||
| 415 | } | 390 | } |
| 416 | 391 | ||
| 417 | private: | 392 | private: |
| 418 | VfsDirectoryServiceWrapper backend; | 393 | std::unique_ptr<FileSys::FileSystemBackend> backend; |
| 419 | }; | 394 | }; |
| 420 | 395 | ||
| 421 | FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { | 396 | FSP_SRV::FSP_SRV() : ServiceFramework("fsp-srv") { |
| @@ -516,7 +491,8 @@ void FSP_SRV::TryLoadRomFS() { | |||
| 516 | if (romfs) { | 491 | if (romfs) { |
| 517 | return; | 492 | return; |
| 518 | } | 493 | } |
| 519 | auto res = OpenRomFS(); | 494 | FileSys::Path unused; |
| 495 | auto res = OpenFileSystem(Type::RomFS, unused); | ||
| 520 | if (res.Succeeded()) { | 496 | if (res.Succeeded()) { |
| 521 | romfs = std::move(res.Unwrap()); | 497 | romfs = std::move(res.Unwrap()); |
| 522 | } | 498 | } |
| @@ -532,7 +508,8 @@ void FSP_SRV::Initialize(Kernel::HLERequestContext& ctx) { | |||
| 532 | void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { | 508 | void FSP_SRV::MountSdCard(Kernel::HLERequestContext& ctx) { |
| 533 | LOG_DEBUG(Service_FS, "called"); | 509 | LOG_DEBUG(Service_FS, "called"); |
| 534 | 510 | ||
| 535 | IFileSystem filesystem(OpenFileSystem(Type::SDMC).Unwrap()); | 511 | FileSys::Path unused; |
| 512 | auto filesystem = OpenFileSystem(Type::SDMC, unused).Unwrap(); | ||
| 536 | 513 | ||
| 537 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 514 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 538 | rb.Push(RESULT_SUCCESS); | 515 | rb.Push(RESULT_SUCCESS); |
| @@ -555,11 +532,20 @@ void FSP_SRV::CreateSaveData(Kernel::HLERequestContext& ctx) { | |||
| 555 | void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { | 532 | void FSP_SRV::MountSaveData(Kernel::HLERequestContext& ctx) { |
| 556 | LOG_WARNING(Service_FS, "(STUBBED) called"); | 533 | LOG_WARNING(Service_FS, "(STUBBED) called"); |
| 557 | 534 | ||
| 558 | IFileSystem filesystem(OpenFileSystem(Type::SaveData).Unwrap()); | 535 | // TODO(Subv): Read the input parameters and mount the requested savedata instead of always |
| 536 | // mounting the current process' savedata. | ||
| 537 | FileSys::Path unused; | ||
| 538 | auto filesystem = OpenFileSystem(Type::SaveData, unused); | ||
| 539 | |||
| 540 | if (filesystem.Failed()) { | ||
| 541 | IPC::ResponseBuilder rb{ctx, 2, 0, 0}; | ||
| 542 | rb.Push(ResultCode(ErrorModule::FS, FileSys::ErrCodes::SaveDataNotFound)); | ||
| 543 | return; | ||
| 544 | } | ||
| 559 | 545 | ||
| 560 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 546 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 561 | rb.Push(RESULT_SUCCESS); | 547 | rb.Push(RESULT_SUCCESS); |
| 562 | rb.PushIpcInterface<IFileSystem>(std::move(filesystem)); | 548 | rb.PushIpcInterface<IFileSystem>(std::move(filesystem.Unwrap())); |
| 563 | } | 549 | } |
| 564 | 550 | ||
| 565 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { | 551 | void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { |
| @@ -582,11 +568,18 @@ void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { | |||
| 582 | return; | 568 | return; |
| 583 | } | 569 | } |
| 584 | 570 | ||
| 585 | IStorage storage(romfs); | 571 | // Attempt to open a StorageBackend interface to the RomFS |
| 572 | auto storage = romfs->OpenFile({}, {}); | ||
| 573 | if (storage.Failed()) { | ||
| 574 | LOG_CRITICAL(Service_FS, "no storage interface available!"); | ||
| 575 | IPC::ResponseBuilder rb{ctx, 2}; | ||
| 576 | rb.Push(storage.Code()); | ||
| 577 | return; | ||
| 578 | } | ||
| 586 | 579 | ||
| 587 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; | 580 | IPC::ResponseBuilder rb{ctx, 2, 0, 1}; |
| 588 | rb.Push(RESULT_SUCCESS); | 581 | rb.Push(RESULT_SUCCESS); |
| 589 | rb.PushIpcInterface<IStorage>(std::move(storage)); | 582 | rb.PushIpcInterface<IStorage>(std::move(storage.Unwrap())); |
| 590 | } | 583 | } |
| 591 | 584 | ||
| 592 | void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { | 585 | void FSP_SRV::OpenRomStorage(Kernel::HLERequestContext& ctx) { |
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h index b1ef6397f..acb78fac1 100644 --- a/src/core/hle/service/filesystem/fsp_srv.h +++ b/src/core/hle/service/filesystem/fsp_srv.h | |||
| @@ -29,7 +29,7 @@ private: | |||
| 29 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); | 29 | void OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx); |
| 30 | void OpenRomStorage(Kernel::HLERequestContext& ctx); | 30 | void OpenRomStorage(Kernel::HLERequestContext& ctx); |
| 31 | 31 | ||
| 32 | FileSys::VirtualFile romfs; | 32 | std::unique_ptr<FileSys::FileSystemBackend> romfs; |
| 33 | }; | 33 | }; |
| 34 | 34 | ||
| 35 | } // namespace Service::FileSystem | 35 | } // namespace Service::FileSystem |
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp index ebb8d13e6..b0f4a384e 100644 --- a/src/core/hle/service/hid/hid.cpp +++ b/src/core/hle/service/hid/hid.cpp | |||
| @@ -75,7 +75,7 @@ private: | |||
| 75 | 75 | ||
| 76 | // Set up controllers as neon red+blue Joy-Con attached to console | 76 | // Set up controllers as neon red+blue Joy-Con attached to console |
| 77 | ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header; | 77 | ControllerHeader& controller_header = mem.controllers[Controller_Handheld].header; |
| 78 | controller_header.type = ControllerType_Handheld | ControllerType_JoyconPair; | 78 | controller_header.type = ControllerType_Handheld; |
| 79 | controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent; | 79 | controller_header.single_colors_descriptor = ColorDesc_ColorsNonexistent; |
| 80 | controller_header.right_color_body = JOYCON_BODY_NEON_RED; | 80 | controller_header.right_color_body = JOYCON_BODY_NEON_RED; |
| 81 | controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED; | 81 | controller_header.right_color_buttons = JOYCON_BUTTONS_NEON_RED; |
| @@ -84,23 +84,21 @@ private: | |||
| 84 | 84 | ||
| 85 | for (size_t controller = 0; controller < mem.controllers.size(); controller++) { | 85 | for (size_t controller = 0; controller < mem.controllers.size(); controller++) { |
| 86 | for (int index = 0; index < HID_NUM_LAYOUTS; index++) { | 86 | for (int index = 0; index < HID_NUM_LAYOUTS; index++) { |
| 87 | // TODO(DarkLordZach): Is this layout/controller config actually invalid? | ||
| 88 | if (controller == Controller_Handheld && index == Layout_Single) | ||
| 89 | continue; | ||
| 90 | |||
| 91 | ControllerLayout& layout = mem.controllers[controller].layouts[index]; | 87 | ControllerLayout& layout = mem.controllers[controller].layouts[index]; |
| 92 | layout.header.num_entries = HID_NUM_ENTRIES; | 88 | layout.header.num_entries = HID_NUM_ENTRIES; |
| 93 | layout.header.max_entry_index = HID_NUM_ENTRIES - 1; | 89 | layout.header.max_entry_index = HID_NUM_ENTRIES - 1; |
| 94 | 90 | ||
| 95 | // HID shared memory stores the state of the past 17 samples in a circlular buffer, | 91 | // HID shared memory stores the state of the past 17 samples in a circlular buffer, |
| 96 | // each with a timestamp in number of samples since boot. | 92 | // each with a timestamp in number of samples since boot. |
| 93 | const ControllerInputEntry& last_entry = layout.entries[layout.header.latest_entry]; | ||
| 94 | |||
| 97 | layout.header.timestamp_ticks = CoreTiming::GetTicks(); | 95 | layout.header.timestamp_ticks = CoreTiming::GetTicks(); |
| 98 | layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES; | 96 | layout.header.latest_entry = (layout.header.latest_entry + 1) % HID_NUM_ENTRIES; |
| 99 | 97 | ||
| 100 | ControllerInputEntry& entry = layout.entries[layout.header.latest_entry]; | 98 | ControllerInputEntry& entry = layout.entries[layout.header.latest_entry]; |
| 101 | entry.timestamp++; | 99 | entry.timestamp = last_entry.timestamp + 1; |
| 102 | // TODO(shinyquagsire23): Is this always identical to timestamp? | 100 | // TODO(shinyquagsire23): Is this always identical to timestamp? |
| 103 | entry.timestamp_2++; | 101 | entry.timestamp_2 = entry.timestamp; |
| 104 | 102 | ||
| 105 | // TODO(shinyquagsire23): More than just handheld input | 103 | // TODO(shinyquagsire23): More than just handheld input |
| 106 | if (controller != Controller_Handheld) | 104 | if (controller != Controller_Handheld) |
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp index 54a151c26..0d951084b 100644 --- a/src/core/hle/service/nifm/nifm.cpp +++ b/src/core/hle/service/nifm/nifm.cpp | |||
| @@ -148,6 +148,24 @@ private: | |||
| 148 | 148 | ||
| 149 | LOG_DEBUG(Service_NIFM, "called"); | 149 | LOG_DEBUG(Service_NIFM, "called"); |
| 150 | } | 150 | } |
| 151 | void IsWirelessCommunicationEnabled(Kernel::HLERequestContext& ctx) { | ||
| 152 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 153 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 154 | rb.Push(RESULT_SUCCESS); | ||
| 155 | rb.Push<u8>(0); | ||
| 156 | } | ||
| 157 | void IsEthernetCommunicationEnabled(Kernel::HLERequestContext& ctx) { | ||
| 158 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 159 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 160 | rb.Push(RESULT_SUCCESS); | ||
| 161 | rb.Push<u8>(0); | ||
| 162 | } | ||
| 163 | void IsAnyInternetRequestAccepted(Kernel::HLERequestContext& ctx) { | ||
| 164 | LOG_WARNING(Service_NIFM, "(STUBBED) called"); | ||
| 165 | IPC::ResponseBuilder rb{ctx, 3}; | ||
| 166 | rb.Push(RESULT_SUCCESS); | ||
| 167 | rb.Push<u8>(0); | ||
| 168 | } | ||
| 151 | }; | 169 | }; |
| 152 | 170 | ||
| 153 | IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { | 171 | IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { |
| @@ -167,11 +185,11 @@ IGeneralService::IGeneralService() : ServiceFramework("IGeneralService") { | |||
| 167 | {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"}, | 185 | {14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"}, |
| 168 | {15, nullptr, "GetCurrentIpConfigInfo"}, | 186 | {15, nullptr, "GetCurrentIpConfigInfo"}, |
| 169 | {16, nullptr, "SetWirelessCommunicationEnabled"}, | 187 | {16, nullptr, "SetWirelessCommunicationEnabled"}, |
| 170 | {17, nullptr, "IsWirelessCommunicationEnabled"}, | 188 | {17, &IGeneralService::IsWirelessCommunicationEnabled, "IsWirelessCommunicationEnabled"}, |
| 171 | {18, nullptr, "GetInternetConnectionStatus"}, | 189 | {18, nullptr, "GetInternetConnectionStatus"}, |
| 172 | {19, nullptr, "SetEthernetCommunicationEnabled"}, | 190 | {19, nullptr, "SetEthernetCommunicationEnabled"}, |
| 173 | {20, nullptr, "IsEthernetCommunicationEnabled"}, | 191 | {20, &IGeneralService::IsEthernetCommunicationEnabled, "IsEthernetCommunicationEnabled"}, |
| 174 | {21, nullptr, "IsAnyInternetRequestAccepted"}, | 192 | {21, &IGeneralService::IsAnyInternetRequestAccepted, "IsAnyInternetRequestAccepted"}, |
| 175 | {22, nullptr, "IsAnyForegroundRequestAccepted"}, | 193 | {22, nullptr, "IsAnyForegroundRequestAccepted"}, |
| 176 | {23, nullptr, "PutToSleep"}, | 194 | {23, nullptr, "PutToSleep"}, |
| 177 | {24, nullptr, "WakeUp"}, | 195 | {24, nullptr, "WakeUp"}, |
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp index 303acdcb3..671b092e1 100644 --- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp +++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp | |||
| @@ -29,24 +29,9 @@ u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, std::vector< | |||
| 29 | u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { | 29 | u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) { |
| 30 | IocGetConfigParams params{}; | 30 | IocGetConfigParams params{}; |
| 31 | std::memcpy(¶ms, input.data(), sizeof(params)); | 31 | std::memcpy(¶ms, input.data(), sizeof(params)); |
| 32 | LOG_DEBUG(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), | 32 | LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(), |
| 33 | params.param_str.data()); | 33 | params.param_str.data()); |
| 34 | 34 | return 0x30006; // Returns error on production mode | |
| 35 | if (!strcmp(params.domain_str.data(), "nv")) { | ||
| 36 | if (!strcmp(params.param_str.data(), "NV_MEMORY_PROFILER")) { | ||
| 37 | params.config_str[0] = '0'; | ||
| 38 | } else if (!strcmp(params.param_str.data(), "NVN_THROUGH_OPENGL")) { | ||
| 39 | params.config_str[0] = '0'; | ||
| 40 | } else if (!strcmp(params.param_str.data(), "NVRM_GPU_PREVENT_USE")) { | ||
| 41 | params.config_str[0] = '0'; | ||
| 42 | } else { | ||
| 43 | params.config_str[0] = '\0'; | ||
| 44 | } | ||
| 45 | } else { | ||
| 46 | UNIMPLEMENTED(); // unknown domain? Only nv has been seen so far on hardware | ||
| 47 | } | ||
| 48 | std::memcpy(output.data(), ¶ms, sizeof(params)); | ||
| 49 | return 0; | ||
| 50 | } | 35 | } |
| 51 | 36 | ||
| 52 | u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, | 37 | u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, |
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 0b3b4cd73..eb7feb617 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp | |||
| @@ -4,10 +4,11 @@ | |||
| 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" | ||
| 7 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 8 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 9 | #include "common/string_util.h" | 10 | #include "common/string_util.h" |
| 10 | #include "core/file_sys/content_archive.h" | 11 | #include "core/file_sys/romfs_factory.h" |
| 11 | #include "core/hle/kernel/process.h" | 12 | #include "core/hle/kernel/process.h" |
| 12 | #include "core/hle/kernel/resource_limit.h" | 13 | #include "core/hle/kernel/resource_limit.h" |
| 13 | #include "core/hle/service/filesystem/filesystem.h" | 14 | #include "core/hle/service/filesystem/filesystem.h" |
| @@ -45,11 +46,55 @@ static std::string FindRomFS(const std::string& directory) { | |||
| 45 | return filepath_romfs; | 46 | return filepath_romfs; |
| 46 | } | 47 | } |
| 47 | 48 | ||
| 48 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile file) | 49 | AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, |
| 49 | : AppLoader(std::move(file)) {} | 50 | std::string filepath) |
| 51 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 52 | |||
| 53 | FileType AppLoader_DeconstructedRomDirectory::IdentifyType(FileUtil::IOFile& file, | ||
| 54 | const std::string& filepath) { | ||
| 55 | bool is_main_found{}; | ||
| 56 | bool is_npdm_found{}; | ||
| 57 | bool is_rtld_found{}; | ||
| 58 | bool is_sdk_found{}; | ||
| 59 | |||
| 60 | const auto callback = [&](unsigned* num_entries_out, const std::string& directory, | ||
| 61 | const std::string& virtual_name) -> bool { | ||
| 62 | // Skip directories | ||
| 63 | std::string physical_name = directory + virtual_name; | ||
| 64 | if (FileUtil::IsDirectory(physical_name)) { | ||
| 65 | return true; | ||
| 66 | } | ||
| 67 | |||
| 68 | // Verify filename | ||
| 69 | if (Common::ToLower(virtual_name) == "main") { | ||
| 70 | is_main_found = true; | ||
| 71 | } else if (Common::ToLower(virtual_name) == "main.npdm") { | ||
| 72 | is_npdm_found = true; | ||
| 73 | return true; | ||
| 74 | } else if (Common::ToLower(virtual_name) == "rtld") { | ||
| 75 | is_rtld_found = true; | ||
| 76 | } else if (Common::ToLower(virtual_name) == "sdk") { | ||
| 77 | is_sdk_found = true; | ||
| 78 | } else { | ||
| 79 | // Continue searching | ||
| 80 | return true; | ||
| 81 | } | ||
| 82 | |||
| 83 | // Verify file is an NSO | ||
| 84 | FileUtil::IOFile file(physical_name, "rb"); | ||
| 85 | if (AppLoader_NSO::IdentifyType(file, physical_name) != FileType::NSO) { | ||
| 86 | return false; | ||
| 87 | } | ||
| 88 | |||
| 89 | // We are done if we've found and verified all required NSOs | ||
| 90 | return !(is_main_found && is_npdm_found && is_rtld_found && is_sdk_found); | ||
| 91 | }; | ||
| 92 | |||
| 93 | // Search the directory recursively, looking for the required modules | ||
| 94 | const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; | ||
| 95 | FileUtil::ForeachDirectoryEntry(nullptr, directory, callback); | ||
| 50 | 96 | ||
| 51 | FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::VirtualFile& file) { | 97 | if (is_main_found && is_npdm_found && is_rtld_found && is_sdk_found) { |
| 52 | if (FileSys::IsDirectoryExeFS(file->GetContainingDirectory())) { | ||
| 53 | return FileType::DeconstructedRomDirectory; | 98 | return FileType::DeconstructedRomDirectory; |
| 54 | } | 99 | } |
| 55 | 100 | ||
| @@ -61,13 +106,14 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 61 | if (is_loaded) { | 106 | if (is_loaded) { |
| 62 | return ResultStatus::ErrorAlreadyLoaded; | 107 | return ResultStatus::ErrorAlreadyLoaded; |
| 63 | } | 108 | } |
| 109 | if (!file.IsOpen()) { | ||
| 110 | return ResultStatus::Error; | ||
| 111 | } | ||
| 64 | 112 | ||
| 65 | const FileSys::VirtualDir dir = file->GetContainingDirectory(); | 113 | const std::string directory = filepath.substr(0, filepath.find_last_of("/\\")) + DIR_SEP; |
| 66 | const FileSys::VirtualFile npdm = dir->GetFile("main.npdm"); | 114 | const std::string npdm_path = directory + DIR_SEP + "main.npdm"; |
| 67 | if (npdm == nullptr) | ||
| 68 | return ResultStatus::ErrorInvalidFormat; | ||
| 69 | 115 | ||
| 70 | ResultStatus result = metadata.Load(npdm); | 116 | ResultStatus result = metadata.Load(npdm_path); |
| 71 | if (result != ResultStatus::Success) { | 117 | if (result != ResultStatus::Success) { |
| 72 | return result; | 118 | return result; |
| 73 | } | 119 | } |
| @@ -82,10 +128,9 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 82 | VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; | 128 | VAddr next_load_addr{Memory::PROCESS_IMAGE_VADDR}; |
| 83 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", | 129 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", |
| 84 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { | 130 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { |
| 131 | const std::string path = directory + DIR_SEP + module; | ||
| 85 | const VAddr load_addr = next_load_addr; | 132 | const VAddr load_addr = next_load_addr; |
| 86 | const FileSys::VirtualFile module_file = dir->GetFile(module); | 133 | next_load_addr = AppLoader_NSO::LoadModule(path, load_addr); |
| 87 | if (module_file != nullptr) | ||
| 88 | next_load_addr = AppLoader_NSO::LoadModule(module_file, load_addr); | ||
| 89 | if (next_load_addr) { | 134 | if (next_load_addr) { |
| 90 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); | 135 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); |
| 91 | } else { | 136 | } else { |
| @@ -102,20 +147,42 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( | |||
| 102 | metadata.GetMainThreadStackSize()); | 147 | metadata.GetMainThreadStackSize()); |
| 103 | 148 | ||
| 104 | // Find the RomFS by searching for a ".romfs" file in this directory | 149 | // Find the RomFS by searching for a ".romfs" file in this directory |
| 105 | const auto& files = dir->GetFiles(); | 150 | filepath_romfs = FindRomFS(directory); |
| 106 | const auto romfs_iter = | ||
| 107 | std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { | ||
| 108 | return file->GetName().find(".romfs") != std::string::npos; | ||
| 109 | }); | ||
| 110 | |||
| 111 | // TODO(DarkLordZach): Identify RomFS if its a subdirectory. | ||
| 112 | const auto romfs = (romfs_iter == files.end()) ? nullptr : *romfs_iter; | ||
| 113 | 151 | ||
| 114 | if (romfs != nullptr) | 152 | // Register the RomFS if a ".romfs" file was found |
| 115 | Service::FileSystem::RegisterRomFS(romfs); | 153 | if (!filepath_romfs.empty()) { |
| 154 | Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this), | ||
| 155 | Service::FileSystem::Type::RomFS); | ||
| 156 | } | ||
| 116 | 157 | ||
| 117 | is_loaded = true; | 158 | is_loaded = true; |
| 118 | return ResultStatus::Success; | 159 | return ResultStatus::Success; |
| 119 | } | 160 | } |
| 120 | 161 | ||
| 162 | ResultStatus AppLoader_DeconstructedRomDirectory::ReadRomFS( | ||
| 163 | std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, u64& size) { | ||
| 164 | |||
| 165 | if (filepath_romfs.empty()) { | ||
| 166 | LOG_DEBUG(Loader, "No RomFS available"); | ||
| 167 | return ResultStatus::ErrorNotUsed; | ||
| 168 | } | ||
| 169 | |||
| 170 | // We reopen the file, to allow its position to be independent | ||
| 171 | romfs_file = std::make_shared<FileUtil::IOFile>(filepath_romfs, "rb"); | ||
| 172 | if (!romfs_file->IsOpen()) { | ||
| 173 | return ResultStatus::Error; | ||
| 174 | } | ||
| 175 | |||
| 176 | offset = 0; | ||
| 177 | size = romfs_file->GetSize(); | ||
| 178 | |||
| 179 | LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset); | ||
| 180 | LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size); | ||
| 181 | |||
| 182 | // Reset read pointer | ||
| 183 | file.Seek(0, SEEK_SET); | ||
| 184 | |||
| 185 | return ResultStatus::Success; | ||
| 186 | } | ||
| 187 | |||
| 121 | } // namespace Loader | 188 | } // namespace Loader |
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h index 494220756..23295d911 100644 --- a/src/core/loader/deconstructed_rom_directory.h +++ b/src/core/loader/deconstructed_rom_directory.h | |||
| @@ -20,22 +20,28 @@ namespace Loader { | |||
| 20 | */ | 20 | */ |
| 21 | class AppLoader_DeconstructedRomDirectory final : public AppLoader { | 21 | class AppLoader_DeconstructedRomDirectory final : public AppLoader { |
| 22 | public: | 22 | public: |
| 23 | explicit AppLoader_DeconstructedRomDirectory(FileSys::VirtualFile main_file); | 23 | AppLoader_DeconstructedRomDirectory(FileUtil::IOFile&& file, std::string filepath); |
| 24 | 24 | ||
| 25 | /** | 25 | /** |
| 26 | * Returns the type of the file | 26 | * Returns the type of the file |
| 27 | * @param file std::shared_ptr<VfsFile> open file | 27 | * @param file FileUtil::IOFile open file |
| 28 | * @param filepath Path of the file that we are opening. | ||
| 28 | * @return FileType found, or FileType::Error if this loader doesn't know it | 29 | * @return FileType found, or FileType::Error if this loader doesn't know it |
| 29 | */ | 30 | */ |
| 30 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 31 | static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); |
| 31 | 32 | ||
| 32 | FileType GetFileType() override { | 33 | FileType GetFileType() override { |
| 33 | return IdentifyType(file); | 34 | return IdentifyType(file, filepath); |
| 34 | } | 35 | } |
| 35 | 36 | ||
| 36 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 37 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 37 | 38 | ||
| 39 | ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | ||
| 40 | u64& size) override; | ||
| 41 | |||
| 38 | private: | 42 | private: |
| 43 | std::string filepath_romfs; | ||
| 44 | std::string filepath; | ||
| 39 | FileSys::ProgramMetadata metadata; | 45 | FileSys::ProgramMetadata metadata; |
| 40 | }; | 46 | }; |
| 41 | 47 | ||
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp index 4bfd5f536..b69e5c6ef 100644 --- a/src/core/loader/elf.cpp +++ b/src/core/loader/elf.cpp | |||
| @@ -365,17 +365,20 @@ SectionID ElfReader::GetSectionByName(const char* name, int firstSection) const | |||
| 365 | 365 | ||
| 366 | namespace Loader { | 366 | namespace Loader { |
| 367 | 367 | ||
| 368 | AppLoader_ELF::AppLoader_ELF(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} | 368 | AppLoader_ELF::AppLoader_ELF(FileUtil::IOFile&& file, std::string filename) |
| 369 | : AppLoader(std::move(file)), filename(std::move(filename)) {} | ||
| 369 | 370 | ||
| 370 | FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) { | 371 | FileType AppLoader_ELF::IdentifyType(FileUtil::IOFile& file, const std::string&) { |
| 371 | static constexpr u16 ELF_MACHINE_ARM{0x28}; | 372 | static constexpr u16 ELF_MACHINE_ARM{0x28}; |
| 372 | 373 | ||
| 373 | u32 magic = 0; | 374 | u32 magic = 0; |
| 374 | if (4 != file->ReadObject(&magic)) | 375 | file.Seek(0, SEEK_SET); |
| 376 | if (1 != file.ReadArray<u32>(&magic, 1)) | ||
| 375 | return FileType::Error; | 377 | return FileType::Error; |
| 376 | 378 | ||
| 377 | u16 machine = 0; | 379 | u16 machine = 0; |
| 378 | if (2 != file->ReadObject(&machine, 18)) | 380 | file.Seek(18, SEEK_SET); |
| 381 | if (1 != file.ReadArray<u16>(&machine, 1)) | ||
| 379 | return FileType::Error; | 382 | return FileType::Error; |
| 380 | 383 | ||
| 381 | if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) | 384 | if (Common::MakeMagic('\x7f', 'E', 'L', 'F') == magic && ELF_MACHINE_ARM == machine) |
| @@ -388,13 +391,20 @@ ResultStatus AppLoader_ELF::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 388 | if (is_loaded) | 391 | if (is_loaded) |
| 389 | return ResultStatus::ErrorAlreadyLoaded; | 392 | return ResultStatus::ErrorAlreadyLoaded; |
| 390 | 393 | ||
| 391 | std::vector<u8> buffer = file->ReadAllBytes(); | 394 | if (!file.IsOpen()) |
| 392 | if (buffer.size() != file->GetSize()) | 395 | return ResultStatus::Error; |
| 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) | ||
| 393 | return ResultStatus::Error; | 403 | return ResultStatus::Error; |
| 394 | 404 | ||
| 395 | ElfReader elf_reader(&buffer[0]); | 405 | ElfReader elf_reader(&buffer[0]); |
| 396 | SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); | 406 | SharedPtr<CodeSet> codeset = elf_reader.LoadInto(Memory::PROCESS_IMAGE_VADDR); |
| 397 | codeset->name = file->GetName(); | 407 | codeset->name = filename; |
| 398 | 408 | ||
| 399 | process->LoadModule(codeset, codeset->entrypoint); | 409 | process->LoadModule(codeset, codeset->entrypoint); |
| 400 | process->svc_access_mask.set(); | 410 | process->svc_access_mask.set(); |
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h index b8fb982d0..ee741a789 100644 --- a/src/core/loader/elf.h +++ b/src/core/loader/elf.h | |||
| @@ -16,20 +16,24 @@ 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 | explicit AppLoader_ELF(FileSys::VirtualFile file); | 19 | AppLoader_ELF(FileUtil::IOFile&& file, std::string filename); |
| 20 | 20 | ||
| 21 | /** | 21 | /** |
| 22 | * Returns the type of the file | 22 | * Returns the type of the file |
| 23 | * @param file std::shared_ptr<VfsFile> open file | 23 | * @param file FileUtil::IOFile open file |
| 24 | * @param filepath Path of the file that we are opening. | ||
| 24 | * @return FileType found, or FileType::Error if this loader doesn't know it | 25 | * @return FileType found, or FileType::Error if this loader doesn't know it |
| 25 | */ | 26 | */ |
| 26 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 27 | static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); |
| 27 | 28 | ||
| 28 | FileType GetFileType() override { | 29 | FileType GetFileType() override { |
| 29 | return IdentifyType(file); | 30 | return IdentifyType(file, filename); |
| 30 | } | 31 | } |
| 31 | 32 | ||
| 32 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 33 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 34 | |||
| 35 | private: | ||
| 36 | std::string filename; | ||
| 33 | }; | 37 | }; |
| 34 | 38 | ||
| 35 | } // namespace Loader | 39 | } // namespace Loader |
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 1574345a1..8831d8e83 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp | |||
| @@ -6,7 +6,6 @@ | |||
| 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" | ||
| 10 | #include "core/hle/kernel/process.h" | 9 | #include "core/hle/kernel/process.h" |
| 11 | #include "core/loader/deconstructed_rom_directory.h" | 10 | #include "core/loader/deconstructed_rom_directory.h" |
| 12 | #include "core/loader/elf.h" | 11 | #include "core/loader/elf.h" |
| @@ -22,11 +21,11 @@ const std::initializer_list<Kernel::AddressMapping> default_address_mappings = { | |||
| 22 | {0x1F000000, 0x600000, false}, // entire VRAM | 21 | {0x1F000000, 0x600000, false}, // entire VRAM |
| 23 | }; | 22 | }; |
| 24 | 23 | ||
| 25 | FileType IdentifyFile(FileSys::VirtualFile file) { | 24 | FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath) { |
| 26 | FileType type; | 25 | FileType type; |
| 27 | 26 | ||
| 28 | #define CHECK_TYPE(loader) \ | 27 | #define CHECK_TYPE(loader) \ |
| 29 | type = AppLoader_##loader::IdentifyType(file); \ | 28 | type = AppLoader_##loader::IdentifyType(file, filepath); \ |
| 30 | if (FileType::Error != type) \ | 29 | if (FileType::Error != type) \ |
| 31 | return type; | 30 | return type; |
| 32 | 31 | ||
| @@ -42,22 +41,25 @@ FileType IdentifyFile(FileSys::VirtualFile file) { | |||
| 42 | } | 41 | } |
| 43 | 42 | ||
| 44 | FileType IdentifyFile(const std::string& file_name) { | 43 | FileType IdentifyFile(const std::string& file_name) { |
| 45 | return IdentifyFile(FileSys::VirtualFile(std::make_shared<FileSys::RealVfsFile>(file_name))); | 44 | FileUtil::IOFile file(file_name, "rb"); |
| 46 | } | 45 | if (!file.IsOpen()) { |
| 46 | LOG_ERROR(Loader, "Failed to load file {}", file_name); | ||
| 47 | return FileType::Unknown; | ||
| 48 | } | ||
| 47 | 49 | ||
| 48 | FileType GuessFromFilename(const std::string& name) { | 50 | return IdentifyFile(file, file_name); |
| 49 | if (name == "main") | 51 | } |
| 50 | return FileType::DeconstructedRomDirectory; | ||
| 51 | 52 | ||
| 52 | const std::string extension = Common::ToLower(FileUtil::GetExtensionFromFilename(name)); | 53 | FileType GuessFromExtension(const std::string& extension_) { |
| 54 | std::string extension = Common::ToLower(extension_); | ||
| 53 | 55 | ||
| 54 | if (extension == "elf") | 56 | if (extension == ".elf") |
| 55 | return FileType::ELF; | 57 | return FileType::ELF; |
| 56 | if (extension == "nro") | 58 | else if (extension == ".nro") |
| 57 | return FileType::NRO; | 59 | return FileType::NRO; |
| 58 | if (extension == "nso") | 60 | else if (extension == ".nso") |
| 59 | return FileType::NSO; | 61 | return FileType::NSO; |
| 60 | if (extension == "nca") | 62 | else if (extension == ".nca") |
| 61 | return FileType::NCA; | 63 | return FileType::NCA; |
| 62 | 64 | ||
| 63 | return FileType::Unknown; | 65 | return FileType::Unknown; |
| @@ -91,47 +93,58 @@ const char* GetFileTypeString(FileType type) { | |||
| 91 | * @param filepath the file full path (with name) | 93 | * @param filepath the file full path (with name) |
| 92 | * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type | 94 | * @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type |
| 93 | */ | 95 | */ |
| 94 | static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileType type) { | 96 | static std::unique_ptr<AppLoader> GetFileLoader(FileUtil::IOFile&& file, FileType type, |
| 97 | const std::string& filename, | ||
| 98 | const std::string& filepath) { | ||
| 95 | switch (type) { | 99 | switch (type) { |
| 96 | 100 | ||
| 97 | // Standard ELF file format. | 101 | // Standard ELF file format. |
| 98 | case FileType::ELF: | 102 | case FileType::ELF: |
| 99 | return std::make_unique<AppLoader_ELF>(std::move(file)); | 103 | return std::make_unique<AppLoader_ELF>(std::move(file), filename); |
| 100 | 104 | ||
| 101 | // NX NSO file format. | 105 | // NX NSO file format. |
| 102 | case FileType::NSO: | 106 | case FileType::NSO: |
| 103 | return std::make_unique<AppLoader_NSO>(std::move(file)); | 107 | return std::make_unique<AppLoader_NSO>(std::move(file), filepath); |
| 104 | 108 | ||
| 105 | // NX NRO file format. | 109 | // NX NRO file format. |
| 106 | case FileType::NRO: | 110 | case FileType::NRO: |
| 107 | return std::make_unique<AppLoader_NRO>(std::move(file)); | 111 | return std::make_unique<AppLoader_NRO>(std::move(file), filepath); |
| 108 | 112 | ||
| 109 | // NX NCA file format. | 113 | // NX NCA file format. |
| 110 | case FileType::NCA: | 114 | case FileType::NCA: |
| 111 | return std::make_unique<AppLoader_NCA>(std::move(file)); | 115 | return std::make_unique<AppLoader_NCA>(std::move(file), filepath); |
| 112 | 116 | ||
| 113 | // NX deconstructed ROM directory. | 117 | // NX deconstructed ROM directory. |
| 114 | case FileType::DeconstructedRomDirectory: | 118 | case FileType::DeconstructedRomDirectory: |
| 115 | return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file)); | 119 | return std::make_unique<AppLoader_DeconstructedRomDirectory>(std::move(file), filepath); |
| 116 | 120 | ||
| 117 | default: | 121 | default: |
| 118 | return nullptr; | 122 | return nullptr; |
| 119 | } | 123 | } |
| 120 | } | 124 | } |
| 121 | 125 | ||
| 122 | std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file) { | 126 | std::unique_ptr<AppLoader> GetLoader(const std::string& filename) { |
| 123 | FileType type = IdentifyFile(file); | 127 | FileUtil::IOFile file(filename, "rb"); |
| 124 | FileType filename_type = GuessFromFilename(file->GetName()); | 128 | if (!file.IsOpen()) { |
| 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); | ||
| 125 | 138 | ||
| 126 | if (type != filename_type) { | 139 | if (type != filename_type) { |
| 127 | LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName()); | 140 | LOG_WARNING(Loader, "File {} has a different type than its extension.", filename); |
| 128 | if (FileType::Unknown == type) | 141 | if (FileType::Unknown == type) |
| 129 | type = filename_type; | 142 | type = filename_type; |
| 130 | } | 143 | } |
| 131 | 144 | ||
| 132 | LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type)); | 145 | LOG_DEBUG(Loader, "Loading file {} as {}...", filename, GetFileTypeString(type)); |
| 133 | 146 | ||
| 134 | return GetFileLoader(std::move(file), type); | 147 | return GetFileLoader(std::move(file), type, filename_filename, filename); |
| 135 | } | 148 | } |
| 136 | 149 | ||
| 137 | } // namespace Loader | 150 | } // namespace Loader |
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 1da9e8099..b76f7b13d 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h | |||
| @@ -13,7 +13,6 @@ | |||
| 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" | ||
| 17 | #include "core/hle/kernel/kernel.h" | 16 | #include "core/hle/kernel/kernel.h" |
| 18 | 17 | ||
| 19 | namespace Kernel { | 18 | namespace Kernel { |
| @@ -37,9 +36,10 @@ enum class FileType { | |||
| 37 | /** | 36 | /** |
| 38 | * Identifies the type of a bootable file based on the magic value in its header. | 37 | * Identifies the type of a bootable file based on the magic value in its header. |
| 39 | * @param file open file | 38 | * @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(FileSys::VirtualFile file); | 42 | FileType IdentifyFile(FileUtil::IOFile& file, const std::string& filepath); |
| 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(FileSys::VirtualFile file); | |||
| 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 name | 53 | * Guess the type of a bootable file from its extension |
| 54 | * @param name String name of bootable file | 54 | * @param extension String extension 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 GuessFromFilename(const std::string& name); | 58 | FileType GuessFromExtension(const std::string& extension); |
| 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(FileSys::VirtualFile file) : file(std::move(file)) {} | 82 | AppLoader(FileUtil::IOFile&& file) : file(std::move(file)) {} |
| 83 | virtual ~AppLoader() {} | 83 | virtual ~AppLoader() {} |
| 84 | 84 | ||
| 85 | /** | 85 | /** |
| @@ -154,20 +154,26 @@ 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 file The file containing the RomFS | 157 | * @param romfs_file The file containing the RomFS |
| 158 | * @param offset The offset the romfs begins on | ||
| 159 | * @param size The size of the romfs | ||
| 158 | * @return ResultStatus result of function | 160 | * @return ResultStatus result of function |
| 159 | */ | 161 | */ |
| 160 | virtual ResultStatus ReadRomFS(FileSys::VirtualFile& dir) { | 162 | virtual ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, |
| 163 | u64& size) { | ||
| 161 | return ResultStatus::ErrorNotImplemented; | 164 | return ResultStatus::ErrorNotImplemented; |
| 162 | } | 165 | } |
| 163 | 166 | ||
| 164 | /** | 167 | /** |
| 165 | * Get the update RomFS of the application | 168 | * Get the update RomFS of the application |
| 166 | * Since the RomFS can be huge, we return a file reference instead of copying to a buffer | 169 | * Since the RomFS can be huge, we return a file reference instead of copying to a buffer |
| 167 | * @param file The file containing the RomFS | 170 | * @param romfs_file The file containing the RomFS |
| 171 | * @param offset The offset the romfs begins on | ||
| 172 | * @param size The size of the romfs | ||
| 168 | * @return ResultStatus result of function | 173 | * @return ResultStatus result of function |
| 169 | */ | 174 | */ |
| 170 | virtual ResultStatus ReadUpdateRomFS(FileSys::VirtualFile& file) { | 175 | virtual ResultStatus ReadUpdateRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, |
| 176 | u64& size) { | ||
| 171 | return ResultStatus::ErrorNotImplemented; | 177 | return ResultStatus::ErrorNotImplemented; |
| 172 | } | 178 | } |
| 173 | 179 | ||
| @@ -181,7 +187,7 @@ public: | |||
| 181 | } | 187 | } |
| 182 | 188 | ||
| 183 | protected: | 189 | protected: |
| 184 | FileSys::VirtualFile file; | 190 | FileUtil::IOFile file; |
| 185 | bool is_loaded = false; | 191 | bool is_loaded = false; |
| 186 | }; | 192 | }; |
| 187 | 193 | ||
| @@ -196,6 +202,6 @@ extern const std::initializer_list<Kernel::AddressMapping> default_address_mappi | |||
| 196 | * @param filename String filename of bootable file | 202 | * @param filename String filename of bootable file |
| 197 | * @return best loader for this file | 203 | * @return best loader for this file |
| 198 | */ | 204 | */ |
| 199 | std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file); | 205 | std::unique_ptr<AppLoader> GetLoader(const std::string& filename); |
| 200 | 206 | ||
| 201 | } // namespace Loader | 207 | } // namespace Loader |
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp index 874f42b91..da064f8e3 100644 --- a/src/core/loader/nca.cpp +++ b/src/core/loader/nca.cpp | |||
| @@ -4,11 +4,13 @@ | |||
| 4 | 4 | ||
| 5 | #include <vector> | 5 | #include <vector> |
| 6 | 6 | ||
| 7 | #include "common/common_funcs.h" | ||
| 7 | #include "common/file_util.h" | 8 | #include "common/file_util.h" |
| 8 | #include "common/logging/log.h" | 9 | #include "common/logging/log.h" |
| 10 | #include "common/swap.h" | ||
| 9 | #include "core/core.h" | 11 | #include "core/core.h" |
| 10 | #include "core/file_sys/content_archive.h" | ||
| 11 | #include "core/file_sys/program_metadata.h" | 12 | #include "core/file_sys/program_metadata.h" |
| 13 | #include "core/file_sys/romfs_factory.h" | ||
| 12 | #include "core/hle/kernel/process.h" | 14 | #include "core/hle/kernel/process.h" |
| 13 | #include "core/hle/kernel/resource_limit.h" | 15 | #include "core/hle/kernel/resource_limit.h" |
| 14 | #include "core/hle/service/filesystem/filesystem.h" | 16 | #include "core/hle/service/filesystem/filesystem.h" |
| @@ -18,15 +20,208 @@ | |||
| 18 | 20 | ||
| 19 | namespace Loader { | 21 | namespace Loader { |
| 20 | 22 | ||
| 21 | AppLoader_NCA::AppLoader_NCA(FileSys::VirtualFile file) : AppLoader(file) {} | 23 | // Media offsets in headers are stored divided by 512. Mult. by this to get real offset. |
| 24 | constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200; | ||
| 22 | 25 | ||
| 23 | FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) { | 26 | constexpr u64 SECTION_HEADER_SIZE = 0x200; |
| 24 | // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support. | 27 | constexpr u64 SECTION_HEADER_OFFSET = 0x400; |
| 25 | FileSys::NCAHeader header{}; | 28 | |
| 26 | if (sizeof(FileSys::NCAHeader) != file->ReadObject(&header)) | 29 | enum class NcaContentType : u8 { Program = 0, Meta = 1, Control = 2, Manual = 3, Data = 4 }; |
| 30 | |||
| 31 | enum class NcaSectionFilesystemType : u8 { PFS0 = 0x2, ROMFS = 0x3 }; | ||
| 32 | |||
| 33 | struct NcaSectionTableEntry { | ||
| 34 | u32_le media_offset; | ||
| 35 | u32_le media_end_offset; | ||
| 36 | INSERT_PADDING_BYTES(0x8); | ||
| 37 | }; | ||
| 38 | static_assert(sizeof(NcaSectionTableEntry) == 0x10, "NcaSectionTableEntry has incorrect size."); | ||
| 39 | |||
| 40 | struct NcaHeader { | ||
| 41 | std::array<u8, 0x100> rsa_signature_1; | ||
| 42 | std::array<u8, 0x100> rsa_signature_2; | ||
| 43 | u32_le magic; | ||
| 44 | u8 is_system; | ||
| 45 | NcaContentType content_type; | ||
| 46 | u8 crypto_type; | ||
| 47 | u8 key_index; | ||
| 48 | u64_le size; | ||
| 49 | u64_le title_id; | ||
| 50 | INSERT_PADDING_BYTES(0x4); | ||
| 51 | u32_le sdk_version; | ||
| 52 | u8 crypto_type_2; | ||
| 53 | INSERT_PADDING_BYTES(15); | ||
| 54 | std::array<u8, 0x10> rights_id; | ||
| 55 | std::array<NcaSectionTableEntry, 0x4> section_tables; | ||
| 56 | std::array<std::array<u8, 0x20>, 0x4> hash_tables; | ||
| 57 | std::array<std::array<u8, 0x10>, 0x4> key_area; | ||
| 58 | INSERT_PADDING_BYTES(0xC0); | ||
| 59 | }; | ||
| 60 | static_assert(sizeof(NcaHeader) == 0x400, "NcaHeader has incorrect size."); | ||
| 61 | |||
| 62 | struct NcaSectionHeaderBlock { | ||
| 63 | INSERT_PADDING_BYTES(3); | ||
| 64 | NcaSectionFilesystemType filesystem_type; | ||
| 65 | u8 crypto_type; | ||
| 66 | INSERT_PADDING_BYTES(3); | ||
| 67 | }; | ||
| 68 | static_assert(sizeof(NcaSectionHeaderBlock) == 0x8, "NcaSectionHeaderBlock has incorrect size."); | ||
| 69 | |||
| 70 | struct Pfs0Superblock { | ||
| 71 | NcaSectionHeaderBlock header_block; | ||
| 72 | std::array<u8, 0x20> hash; | ||
| 73 | u32_le size; | ||
| 74 | INSERT_PADDING_BYTES(4); | ||
| 75 | u64_le hash_table_offset; | ||
| 76 | u64_le hash_table_size; | ||
| 77 | u64_le pfs0_header_offset; | ||
| 78 | u64_le pfs0_size; | ||
| 79 | INSERT_PADDING_BYTES(432); | ||
| 80 | }; | ||
| 81 | static_assert(sizeof(Pfs0Superblock) == 0x200, "Pfs0Superblock has incorrect size."); | ||
| 82 | |||
| 83 | static bool IsValidNca(const NcaHeader& header) { | ||
| 84 | return header.magic == Common::MakeMagic('N', 'C', 'A', '2') || | ||
| 85 | header.magic == Common::MakeMagic('N', 'C', 'A', '3'); | ||
| 86 | } | ||
| 87 | |||
| 88 | // TODO(DarkLordZach): Add support for encrypted. | ||
| 89 | class Nca final { | ||
| 90 | std::vector<FileSys::PartitionFilesystem> pfs; | ||
| 91 | std::vector<u64> pfs_offset; | ||
| 92 | |||
| 93 | u64 romfs_offset = 0; | ||
| 94 | u64 romfs_size = 0; | ||
| 95 | |||
| 96 | boost::optional<u8> exefs_id = boost::none; | ||
| 97 | |||
| 98 | FileUtil::IOFile file; | ||
| 99 | std::string path; | ||
| 100 | |||
| 101 | u64 GetExeFsFileOffset(const std::string& file_name) const; | ||
| 102 | u64 GetExeFsFileSize(const std::string& file_name) const; | ||
| 103 | |||
| 104 | public: | ||
| 105 | ResultStatus Load(FileUtil::IOFile&& file, std::string path); | ||
| 106 | |||
| 107 | FileSys::PartitionFilesystem GetPfs(u8 id) const; | ||
| 108 | |||
| 109 | u64 GetRomFsOffset() const; | ||
| 110 | u64 GetRomFsSize() const; | ||
| 111 | |||
| 112 | std::vector<u8> GetExeFsFile(const std::string& file_name); | ||
| 113 | }; | ||
| 114 | |||
| 115 | static bool IsPfsExeFs(const FileSys::PartitionFilesystem& pfs) { | ||
| 116 | // According to switchbrew, an exefs must only contain these two files: | ||
| 117 | return pfs.GetFileSize("main") > 0 && pfs.GetFileSize("main.npdm") > 0; | ||
| 118 | } | ||
| 119 | |||
| 120 | ResultStatus Nca::Load(FileUtil::IOFile&& in_file, std::string in_path) { | ||
| 121 | file = std::move(in_file); | ||
| 122 | path = in_path; | ||
| 123 | file.Seek(0, SEEK_SET); | ||
| 124 | std::array<u8, sizeof(NcaHeader)> header_array{}; | ||
| 125 | if (sizeof(NcaHeader) != file.ReadBytes(header_array.data(), sizeof(NcaHeader))) | ||
| 126 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 127 | |||
| 128 | NcaHeader header{}; | ||
| 129 | std::memcpy(&header, header_array.data(), sizeof(NcaHeader)); | ||
| 130 | if (!IsValidNca(header)) | ||
| 131 | return ResultStatus::ErrorInvalidFormat; | ||
| 132 | |||
| 133 | int number_sections = | ||
| 134 | std::count_if(std::begin(header.section_tables), std::end(header.section_tables), | ||
| 135 | [](NcaSectionTableEntry entry) { return entry.media_offset > 0; }); | ||
| 136 | |||
| 137 | for (int i = 0; i < number_sections; ++i) { | ||
| 138 | // Seek to beginning of this section. | ||
| 139 | file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET); | ||
| 140 | std::array<u8, sizeof(NcaSectionHeaderBlock)> array{}; | ||
| 141 | if (sizeof(NcaSectionHeaderBlock) != | ||
| 142 | file.ReadBytes(array.data(), sizeof(NcaSectionHeaderBlock))) | ||
| 143 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 144 | |||
| 145 | NcaSectionHeaderBlock block{}; | ||
| 146 | std::memcpy(&block, array.data(), sizeof(NcaSectionHeaderBlock)); | ||
| 147 | |||
| 148 | if (block.filesystem_type == NcaSectionFilesystemType::ROMFS) { | ||
| 149 | romfs_offset = header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER; | ||
| 150 | romfs_size = | ||
| 151 | header.section_tables[i].media_end_offset * MEDIA_OFFSET_MULTIPLIER - romfs_offset; | ||
| 152 | } else if (block.filesystem_type == NcaSectionFilesystemType::PFS0) { | ||
| 153 | Pfs0Superblock sb{}; | ||
| 154 | // Seek back to beginning of this section. | ||
| 155 | file.Seek(SECTION_HEADER_OFFSET + i * SECTION_HEADER_SIZE, SEEK_SET); | ||
| 156 | if (sizeof(Pfs0Superblock) != file.ReadBytes(&sb, sizeof(Pfs0Superblock))) | ||
| 157 | LOG_CRITICAL(Loader, "File reader errored out during header read."); | ||
| 158 | |||
| 159 | u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * | ||
| 160 | MEDIA_OFFSET_MULTIPLIER) + | ||
| 161 | sb.pfs0_header_offset; | ||
| 162 | FileSys::PartitionFilesystem npfs{}; | ||
| 163 | ResultStatus status = npfs.Load(path, offset); | ||
| 164 | |||
| 165 | if (status == ResultStatus::Success) { | ||
| 166 | pfs.emplace_back(std::move(npfs)); | ||
| 167 | pfs_offset.emplace_back(offset); | ||
| 168 | } | ||
| 169 | } | ||
| 170 | } | ||
| 171 | |||
| 172 | for (size_t i = 0; i < pfs.size(); ++i) { | ||
| 173 | if (IsPfsExeFs(pfs[i])) | ||
| 174 | exefs_id = i; | ||
| 175 | } | ||
| 176 | |||
| 177 | return ResultStatus::Success; | ||
| 178 | } | ||
| 179 | |||
| 180 | FileSys::PartitionFilesystem Nca::GetPfs(u8 id) const { | ||
| 181 | return pfs[id]; | ||
| 182 | } | ||
| 183 | |||
| 184 | u64 Nca::GetExeFsFileOffset(const std::string& file_name) const { | ||
| 185 | if (exefs_id == boost::none) | ||
| 186 | return 0; | ||
| 187 | return pfs[*exefs_id].GetFileOffset(file_name) + pfs_offset[*exefs_id]; | ||
| 188 | } | ||
| 189 | |||
| 190 | u64 Nca::GetExeFsFileSize(const std::string& file_name) const { | ||
| 191 | if (exefs_id == boost::none) | ||
| 192 | return 0; | ||
| 193 | return pfs[*exefs_id].GetFileSize(file_name); | ||
| 194 | } | ||
| 195 | |||
| 196 | u64 Nca::GetRomFsOffset() const { | ||
| 197 | return romfs_offset; | ||
| 198 | } | ||
| 199 | |||
| 200 | u64 Nca::GetRomFsSize() const { | ||
| 201 | return romfs_size; | ||
| 202 | } | ||
| 203 | |||
| 204 | std::vector<u8> Nca::GetExeFsFile(const std::string& file_name) { | ||
| 205 | std::vector<u8> out(GetExeFsFileSize(file_name)); | ||
| 206 | file.Seek(GetExeFsFileOffset(file_name), SEEK_SET); | ||
| 207 | file.ReadBytes(out.data(), GetExeFsFileSize(file_name)); | ||
| 208 | return out; | ||
| 209 | } | ||
| 210 | |||
| 211 | AppLoader_NCA::AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath) | ||
| 212 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 213 | |||
| 214 | FileType AppLoader_NCA::IdentifyType(FileUtil::IOFile& file, const std::string&) { | ||
| 215 | file.Seek(0, SEEK_SET); | ||
| 216 | std::array<u8, 0x400> header_enc_array{}; | ||
| 217 | if (0x400 != file.ReadBytes(header_enc_array.data(), 0x400)) | ||
| 27 | return FileType::Error; | 218 | return FileType::Error; |
| 28 | 219 | ||
| 29 | if (IsValidNCA(header) && header.content_type == FileSys::NCAContentType::Program) | 220 | // TODO(DarkLordZach): Assuming everything is decrypted. Add crypto support. |
| 221 | NcaHeader header{}; | ||
| 222 | std::memcpy(&header, header_enc_array.data(), sizeof(NcaHeader)); | ||
| 223 | |||
| 224 | if (IsValidNca(header) && header.content_type == NcaContentType::Program) | ||
| 30 | return FileType::NCA; | 225 | return FileType::NCA; |
| 31 | 226 | ||
| 32 | return FileType::Error; | 227 | return FileType::Error; |
| @@ -36,22 +231,17 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 36 | if (is_loaded) { | 231 | if (is_loaded) { |
| 37 | return ResultStatus::ErrorAlreadyLoaded; | 232 | return ResultStatus::ErrorAlreadyLoaded; |
| 38 | } | 233 | } |
| 234 | if (!file.IsOpen()) { | ||
| 235 | return ResultStatus::Error; | ||
| 236 | } | ||
| 39 | 237 | ||
| 40 | nca = std::make_unique<FileSys::NCA>(file); | 238 | nca = std::make_unique<Nca>(); |
| 41 | ResultStatus result = nca->GetStatus(); | 239 | ResultStatus result = nca->Load(std::move(file), filepath); |
| 42 | if (result != ResultStatus::Success) { | 240 | if (result != ResultStatus::Success) { |
| 43 | return result; | 241 | return result; |
| 44 | } | 242 | } |
| 45 | 243 | ||
| 46 | if (nca->GetType() != FileSys::NCAContentType::Program) | 244 | result = metadata.Load(nca->GetExeFsFile("main.npdm")); |
| 47 | return ResultStatus::ErrorInvalidFormat; | ||
| 48 | |||
| 49 | auto exefs = nca->GetExeFS(); | ||
| 50 | |||
| 51 | if (exefs == nullptr) | ||
| 52 | return ResultStatus::ErrorInvalidFormat; | ||
| 53 | |||
| 54 | result = metadata.Load(exefs->GetFile("main.npdm")); | ||
| 55 | if (result != ResultStatus::Success) { | 245 | if (result != ResultStatus::Success) { |
| 56 | return result; | 246 | return result; |
| 57 | } | 247 | } |
| @@ -66,8 +256,7 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 66 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", | 256 | for (const auto& module : {"rtld", "main", "subsdk0", "subsdk1", "subsdk2", "subsdk3", |
| 67 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { | 257 | "subsdk4", "subsdk5", "subsdk6", "subsdk7", "sdk"}) { |
| 68 | const VAddr load_addr = next_load_addr; | 258 | const VAddr load_addr = next_load_addr; |
| 69 | 259 | next_load_addr = AppLoader_NSO::LoadModule(module, nca->GetExeFsFile(module), load_addr); | |
| 70 | next_load_addr = AppLoader_NSO::LoadModule(exefs->GetFile(module), load_addr); | ||
| 71 | if (next_load_addr) { | 260 | if (next_load_addr) { |
| 72 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); | 261 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", module, load_addr); |
| 73 | } else { | 262 | } else { |
| @@ -83,11 +272,28 @@ ResultStatus AppLoader_NCA::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 83 | process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), | 272 | process->Run(Memory::PROCESS_IMAGE_VADDR, metadata.GetMainThreadPriority(), |
| 84 | metadata.GetMainThreadStackSize()); | 273 | metadata.GetMainThreadStackSize()); |
| 85 | 274 | ||
| 275 | if (nca->GetRomFsSize() > 0) | ||
| 276 | Service::FileSystem::RegisterFileSystem(std::make_unique<FileSys::RomFS_Factory>(*this), | ||
| 277 | Service::FileSystem::Type::RomFS); | ||
| 278 | |||
| 86 | is_loaded = true; | 279 | is_loaded = true; |
| 280 | return ResultStatus::Success; | ||
| 281 | } | ||
| 282 | |||
| 283 | ResultStatus AppLoader_NCA::ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | ||
| 284 | u64& size) { | ||
| 285 | if (nca->GetRomFsSize() == 0) { | ||
| 286 | LOG_DEBUG(Loader, "No RomFS available"); | ||
| 287 | return ResultStatus::ErrorNotUsed; | ||
| 288 | } | ||
| 289 | |||
| 290 | romfs_file = std::make_shared<FileUtil::IOFile>(filepath, "rb"); | ||
| 291 | |||
| 292 | offset = nca->GetRomFsOffset(); | ||
| 293 | size = nca->GetRomFsSize(); | ||
| 87 | 294 | ||
| 88 | const auto romfs = nca->GetRomFS(); | 295 | LOG_DEBUG(Loader, "RomFS offset: 0x{:016X}", offset); |
| 89 | if (romfs != nullptr) | 296 | LOG_DEBUG(Loader, "RomFS size: 0x{:016X}", size); |
| 90 | Service::FileSystem::RegisterRomFS(romfs); | ||
| 91 | 297 | ||
| 92 | return ResultStatus::Success; | 298 | return ResultStatus::Success; |
| 93 | } | 299 | } |
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h index a5639f149..3b6c451d0 100644 --- a/src/core/loader/nca.h +++ b/src/core/loader/nca.h | |||
| @@ -6,37 +6,44 @@ | |||
| 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/content_archive.h" | 9 | #include "core/file_sys/partition_filesystem.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 | |||
| 16 | /// Loads an NCA file | 18 | /// Loads an NCA file |
| 17 | class AppLoader_NCA final : public AppLoader { | 19 | class AppLoader_NCA final : public AppLoader { |
| 18 | public: | 20 | public: |
| 19 | explicit AppLoader_NCA(FileSys::VirtualFile file); | 21 | AppLoader_NCA(FileUtil::IOFile&& file, std::string filepath); |
| 20 | 22 | ||
| 21 | /** | 23 | /** |
| 22 | * Returns the type of the file | 24 | * Returns the type of the file |
| 23 | * @param file std::shared_ptr<VfsFile> open file | 25 | * @param file FileUtil::IOFile open file |
| 26 | * @param filepath Path of the file that we are opening. | ||
| 24 | * @return FileType found, or FileType::Error if this loader doesn't know it | 27 | * @return FileType found, or FileType::Error if this loader doesn't know it |
| 25 | */ | 28 | */ |
| 26 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 29 | static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); |
| 27 | 30 | ||
| 28 | FileType GetFileType() override { | 31 | FileType GetFileType() override { |
| 29 | return IdentifyType(file); | 32 | return IdentifyType(file, filepath); |
| 30 | } | 33 | } |
| 31 | 34 | ||
| 32 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 35 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 33 | 36 | ||
| 37 | ResultStatus ReadRomFS(std::shared_ptr<FileUtil::IOFile>& romfs_file, u64& offset, | ||
| 38 | u64& size) override; | ||
| 39 | |||
| 34 | ~AppLoader_NCA(); | 40 | ~AppLoader_NCA(); |
| 35 | 41 | ||
| 36 | private: | 42 | private: |
| 43 | std::string filepath; | ||
| 37 | FileSys::ProgramMetadata metadata; | 44 | FileSys::ProgramMetadata metadata; |
| 38 | 45 | ||
| 39 | std::unique_ptr<FileSys::NCA> nca; | 46 | std::unique_ptr<Nca> nca; |
| 40 | }; | 47 | }; |
| 41 | 48 | ||
| 42 | } // namespace Loader | 49 | } // namespace Loader |
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp index 08b7aad7a..3853cfa1a 100644 --- a/src/core/loader/nro.cpp +++ b/src/core/loader/nro.cpp | |||
| @@ -47,12 +47,14 @@ struct ModHeader { | |||
| 47 | }; | 47 | }; |
| 48 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); | 48 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); |
| 49 | 49 | ||
| 50 | AppLoader_NRO::AppLoader_NRO(FileSys::VirtualFile file) : AppLoader(file) {} | 50 | AppLoader_NRO::AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath) |
| 51 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 51 | 52 | ||
| 52 | FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) { | 53 | FileType AppLoader_NRO::IdentifyType(FileUtil::IOFile& file, const std::string&) { |
| 53 | // Read NSO header | 54 | // Read NSO header |
| 54 | NroHeader nro_header{}; | 55 | NroHeader nro_header{}; |
| 55 | if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { | 56 | file.Seek(0, SEEK_SET); |
| 57 | if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) { | ||
| 56 | return FileType::Error; | 58 | return FileType::Error; |
| 57 | } | 59 | } |
| 58 | if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) { | 60 | if (nro_header.magic == Common::MakeMagic('N', 'R', 'O', '0')) { |
| @@ -65,10 +67,16 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 65 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | 67 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; |
| 66 | } | 68 | } |
| 67 | 69 | ||
| 68 | bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { | 70 | bool AppLoader_NRO::LoadNro(const std::string& path, VAddr load_base) { |
| 71 | FileUtil::IOFile file(path, "rb"); | ||
| 72 | if (!file.IsOpen()) { | ||
| 73 | return {}; | ||
| 74 | } | ||
| 75 | |||
| 69 | // Read NSO header | 76 | // Read NSO header |
| 70 | NroHeader nro_header{}; | 77 | NroHeader nro_header{}; |
| 71 | if (sizeof(NroHeader) != file->ReadObject(&nro_header)) { | 78 | file.Seek(0, SEEK_SET); |
| 79 | if (sizeof(NroHeader) != file.ReadBytes(&nro_header, sizeof(NroHeader))) { | ||
| 72 | return {}; | 80 | return {}; |
| 73 | } | 81 | } |
| 74 | if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { | 82 | if (nro_header.magic != Common::MakeMagic('N', 'R', 'O', '0')) { |
| @@ -77,9 +85,10 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { | |||
| 77 | 85 | ||
| 78 | // Build program image | 86 | // Build program image |
| 79 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); | 87 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); |
| 80 | std::vector<u8> program_image = file->ReadBytes(PageAlignSize(nro_header.file_size)); | 88 | std::vector<u8> program_image; |
| 81 | if (program_image.size() != PageAlignSize(nro_header.file_size)) | 89 | program_image.resize(PageAlignSize(nro_header.file_size)); |
| 82 | return {}; | 90 | file.Seek(0, SEEK_SET); |
| 91 | file.ReadBytes(program_image.data(), nro_header.file_size); | ||
| 83 | 92 | ||
| 84 | for (int i = 0; i < nro_header.segments.size(); ++i) { | 93 | for (int i = 0; i < nro_header.segments.size(); ++i) { |
| 85 | codeset->segments[i].addr = nro_header.segments[i].offset; | 94 | codeset->segments[i].addr = nro_header.segments[i].offset; |
| @@ -102,7 +111,7 @@ bool AppLoader_NRO::LoadNro(FileSys::VirtualFile file, VAddr load_base) { | |||
| 102 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); | 111 | program_image.resize(static_cast<u32>(program_image.size()) + bss_size); |
| 103 | 112 | ||
| 104 | // Load codeset for current process | 113 | // Load codeset for current process |
| 105 | codeset->name = file->GetName(); | 114 | codeset->name = path; |
| 106 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | 115 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); |
| 107 | Core::CurrentProcess()->LoadModule(codeset, load_base); | 116 | Core::CurrentProcess()->LoadModule(codeset, load_base); |
| 108 | 117 | ||
| @@ -113,11 +122,14 @@ ResultStatus AppLoader_NRO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 113 | if (is_loaded) { | 122 | if (is_loaded) { |
| 114 | return ResultStatus::ErrorAlreadyLoaded; | 123 | return ResultStatus::ErrorAlreadyLoaded; |
| 115 | } | 124 | } |
| 125 | if (!file.IsOpen()) { | ||
| 126 | return ResultStatus::Error; | ||
| 127 | } | ||
| 116 | 128 | ||
| 117 | // Load NRO | 129 | // Load NRO |
| 118 | static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; | 130 | static constexpr VAddr base_addr{Memory::PROCESS_IMAGE_VADDR}; |
| 119 | 131 | ||
| 120 | if (!LoadNro(file, base_addr)) { | 132 | if (!LoadNro(filepath, base_addr)) { |
| 121 | return ResultStatus::ErrorInvalidFormat; | 133 | return ResultStatus::ErrorInvalidFormat; |
| 122 | } | 134 | } |
| 123 | 135 | ||
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h index 2c03d06bb..599adb253 100644 --- a/src/core/loader/nro.h +++ b/src/core/loader/nro.h | |||
| @@ -15,23 +15,26 @@ 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(FileSys::VirtualFile file); | 18 | AppLoader_NRO(FileUtil::IOFile&& file, std::string filepath); |
| 19 | 19 | ||
| 20 | /** | 20 | /** |
| 21 | * Returns the type of the file | 21 | * Returns the type of the file |
| 22 | * @param file std::shared_ptr<VfsFile> open file | 22 | * @param file FileUtil::IOFile open file |
| 23 | * @param filepath Path of the file that we are opening. | ||
| 23 | * @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 |
| 24 | */ | 25 | */ |
| 25 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 26 | static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); |
| 26 | 27 | ||
| 27 | FileType GetFileType() override { | 28 | FileType GetFileType() override { |
| 28 | return IdentifyType(file); | 29 | return IdentifyType(file, filepath); |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 31 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 32 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 32 | 33 | ||
| 33 | private: | 34 | private: |
| 34 | bool LoadNro(FileSys::VirtualFile file, VAddr load_base); | 35 | bool LoadNro(const std::string& path, VAddr load_base); |
| 36 | |||
| 37 | std::string filepath; | ||
| 35 | }; | 38 | }; |
| 36 | 39 | ||
| 37 | } // namespace Loader | 40 | } // namespace Loader |
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp index 2ed12a5ab..7f84e4b1b 100644 --- a/src/core/loader/nso.cpp +++ b/src/core/loader/nso.cpp | |||
| @@ -37,7 +37,6 @@ struct NsoHeader { | |||
| 37 | std::array<u32_le, 3> segments_compressed_size; | 37 | std::array<u32_le, 3> segments_compressed_size; |
| 38 | }; | 38 | }; |
| 39 | static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); | 39 | static_assert(sizeof(NsoHeader) == 0x6c, "NsoHeader has incorrect size."); |
| 40 | static_assert(std::is_trivially_copyable_v<NsoHeader>, "NsoHeader isn't trivially copyable."); | ||
| 41 | 40 | ||
| 42 | struct ModHeader { | 41 | struct ModHeader { |
| 43 | u32_le magic; | 42 | u32_le magic; |
| @@ -50,11 +49,15 @@ struct ModHeader { | |||
| 50 | }; | 49 | }; |
| 51 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); | 50 | static_assert(sizeof(ModHeader) == 0x1c, "ModHeader has incorrect size."); |
| 52 | 51 | ||
| 53 | AppLoader_NSO::AppLoader_NSO(FileSys::VirtualFile file) : AppLoader(std::move(file)) {} | 52 | AppLoader_NSO::AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath) |
| 53 | : AppLoader(std::move(file)), filepath(std::move(filepath)) {} | ||
| 54 | 54 | ||
| 55 | FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) { | 55 | FileType AppLoader_NSO::IdentifyType(FileUtil::IOFile& file, const std::string&) { |
| 56 | u32 magic = 0; | 56 | u32 magic = 0; |
| 57 | file->ReadObject(&magic); | 57 | file.Seek(0, SEEK_SET); |
| 58 | if (1 != file.ReadArray<u32>(&magic, 1)) { | ||
| 59 | return FileType::Error; | ||
| 60 | } | ||
| 58 | 61 | ||
| 59 | if (Common::MakeMagic('N', 'S', 'O', '0') == magic) { | 62 | if (Common::MakeMagic('N', 'S', 'O', '0') == magic) { |
| 60 | return FileType::NSO; | 63 | return FileType::NSO; |
| @@ -95,27 +98,80 @@ static constexpr u32 PageAlignSize(u32 size) { | |||
| 95 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; | 98 | return (size + Memory::PAGE_MASK) & ~Memory::PAGE_MASK; |
| 96 | } | 99 | } |
| 97 | 100 | ||
| 98 | VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { | 101 | VAddr AppLoader_NSO::LoadModule(const std::string& name, const std::vector<u8>& file_data, |
| 99 | if (file == nullptr) | 102 | VAddr load_base) { |
| 103 | if (file_data.size() < sizeof(NsoHeader)) | ||
| 100 | return {}; | 104 | return {}; |
| 101 | 105 | ||
| 102 | if (file->GetSize() < sizeof(NsoHeader)) | 106 | NsoHeader nso_header; |
| 107 | std::memcpy(&nso_header, file_data.data(), sizeof(NsoHeader)); | ||
| 108 | |||
| 109 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) | ||
| 103 | return {}; | 110 | return {}; |
| 104 | 111 | ||
| 105 | NsoHeader nso_header{}; | 112 | // Build program image |
| 106 | if (sizeof(NsoHeader) != file->ReadObject(&nso_header)) | 113 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); |
| 114 | std::vector<u8> program_image; | ||
| 115 | for (int i = 0; i < nso_header.segments.size(); ++i) { | ||
| 116 | std::vector<u8> compressed_data(nso_header.segments_compressed_size[i]); | ||
| 117 | for (int j = 0; j < nso_header.segments_compressed_size[i]; ++j) | ||
| 118 | compressed_data[j] = file_data[nso_header.segments[i].offset + j]; | ||
| 119 | std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]); | ||
| 120 | program_image.resize(nso_header.segments[i].location); | ||
| 121 | program_image.insert(program_image.end(), data.begin(), data.end()); | ||
| 122 | codeset->segments[i].addr = nso_header.segments[i].location; | ||
| 123 | codeset->segments[i].offset = nso_header.segments[i].location; | ||
| 124 | codeset->segments[i].size = PageAlignSize(static_cast<u32>(data.size())); | ||
| 125 | } | ||
| 126 | |||
| 127 | // MOD header pointer is at .text offset + 4 | ||
| 128 | u32 module_offset; | ||
| 129 | std::memcpy(&module_offset, program_image.data() + 4, sizeof(u32)); | ||
| 130 | |||
| 131 | // Read MOD header | ||
| 132 | ModHeader mod_header{}; | ||
| 133 | // Default .bss to size in segment header if MOD0 section doesn't exist | ||
| 134 | u32 bss_size{PageAlignSize(nso_header.segments[2].bss_size)}; | ||
| 135 | std::memcpy(&mod_header, program_image.data() + module_offset, sizeof(ModHeader)); | ||
| 136 | const bool has_mod_header{mod_header.magic == Common::MakeMagic('M', 'O', 'D', '0')}; | ||
| 137 | if (has_mod_header) { | ||
| 138 | // Resize program image to include .bss section and page align each section | ||
| 139 | bss_size = PageAlignSize(mod_header.bss_end_offset - mod_header.bss_start_offset); | ||
| 140 | } | ||
| 141 | codeset->data.size += bss_size; | ||
| 142 | const u32 image_size{PageAlignSize(static_cast<u32>(program_image.size()) + bss_size)}; | ||
| 143 | program_image.resize(image_size); | ||
| 144 | |||
| 145 | // Load codeset for current process | ||
| 146 | codeset->name = name; | ||
| 147 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | ||
| 148 | Core::CurrentProcess()->LoadModule(codeset, load_base); | ||
| 149 | |||
| 150 | return load_base + image_size; | ||
| 151 | } | ||
| 152 | |||
| 153 | VAddr AppLoader_NSO::LoadModule(const std::string& path, VAddr load_base) { | ||
| 154 | FileUtil::IOFile file(path, "rb"); | ||
| 155 | if (!file.IsOpen()) { | ||
| 107 | return {}; | 156 | return {}; |
| 157 | } | ||
| 108 | 158 | ||
| 109 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) | 159 | // Read NSO header |
| 160 | NsoHeader nso_header{}; | ||
| 161 | file.Seek(0, SEEK_SET); | ||
| 162 | if (sizeof(NsoHeader) != file.ReadBytes(&nso_header, sizeof(NsoHeader))) { | ||
| 163 | return {}; | ||
| 164 | } | ||
| 165 | if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) { | ||
| 110 | return {}; | 166 | return {}; |
| 167 | } | ||
| 111 | 168 | ||
| 112 | // Build program image | 169 | // Build program image |
| 113 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); | 170 | Kernel::SharedPtr<Kernel::CodeSet> codeset = Kernel::CodeSet::Create(""); |
| 114 | std::vector<u8> program_image; | 171 | std::vector<u8> program_image; |
| 115 | for (int i = 0; i < nso_header.segments.size(); ++i) { | 172 | for (int i = 0; i < nso_header.segments.size(); ++i) { |
| 116 | const std::vector<u8> compressed_data = | 173 | std::vector<u8> data = |
| 117 | file->ReadBytes(nso_header.segments_compressed_size[i], nso_header.segments[i].offset); | 174 | ReadSegment(file, nso_header.segments[i], nso_header.segments_compressed_size[i]); |
| 118 | std::vector<u8> data = DecompressSegment(compressed_data, nso_header.segments[i]); | ||
| 119 | program_image.resize(nso_header.segments[i].location); | 175 | program_image.resize(nso_header.segments[i].location); |
| 120 | program_image.insert(program_image.end(), data.begin(), data.end()); | 176 | program_image.insert(program_image.end(), data.begin(), data.end()); |
| 121 | codeset->segments[i].addr = nso_header.segments[i].location; | 177 | codeset->segments[i].addr = nso_header.segments[i].location; |
| @@ -142,7 +198,7 @@ VAddr AppLoader_NSO::LoadModule(FileSys::VirtualFile file, VAddr load_base) { | |||
| 142 | program_image.resize(image_size); | 198 | program_image.resize(image_size); |
| 143 | 199 | ||
| 144 | // Load codeset for current process | 200 | // Load codeset for current process |
| 145 | codeset->name = file->GetName(); | 201 | codeset->name = path; |
| 146 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); | 202 | codeset->memory = std::make_shared<std::vector<u8>>(std::move(program_image)); |
| 147 | Core::CurrentProcess()->LoadModule(codeset, load_base); | 203 | Core::CurrentProcess()->LoadModule(codeset, load_base); |
| 148 | 204 | ||
| @@ -153,10 +209,13 @@ ResultStatus AppLoader_NSO::Load(Kernel::SharedPtr<Kernel::Process>& process) { | |||
| 153 | if (is_loaded) { | 209 | if (is_loaded) { |
| 154 | return ResultStatus::ErrorAlreadyLoaded; | 210 | return ResultStatus::ErrorAlreadyLoaded; |
| 155 | } | 211 | } |
| 212 | if (!file.IsOpen()) { | ||
| 213 | return ResultStatus::Error; | ||
| 214 | } | ||
| 156 | 215 | ||
| 157 | // Load module | 216 | // Load module |
| 158 | LoadModule(file, Memory::PROCESS_IMAGE_VADDR); | 217 | LoadModule(filepath, Memory::PROCESS_IMAGE_VADDR); |
| 159 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", file->GetName(), Memory::PROCESS_IMAGE_VADDR); | 218 | LOG_DEBUG(Loader, "loaded module {} @ 0x{:X}", filepath, Memory::PROCESS_IMAGE_VADDR); |
| 160 | 219 | ||
| 161 | process->svc_access_mask.set(); | 220 | process->svc_access_mask.set(); |
| 162 | process->address_mappings = default_address_mappings; | 221 | process->address_mappings = default_address_mappings; |
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h index 3f7567500..386f4d39a 100644 --- a/src/core/loader/nso.h +++ b/src/core/loader/nso.h | |||
| @@ -15,22 +15,29 @@ 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 | explicit AppLoader_NSO(FileSys::VirtualFile file); | 18 | AppLoader_NSO(FileUtil::IOFile&& file, std::string filepath); |
| 19 | 19 | ||
| 20 | /** | 20 | /** |
| 21 | * Returns the type of the file | 21 | * Returns the type of the file |
| 22 | * @param file std::shared_ptr<VfsFile> open file | 22 | * @param file FileUtil::IOFile open file |
| 23 | * @param filepath Path of the file that we are opening. | ||
| 23 | * @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 |
| 24 | */ | 25 | */ |
| 25 | static FileType IdentifyType(const FileSys::VirtualFile& file); | 26 | static FileType IdentifyType(FileUtil::IOFile& file, const std::string& filepath); |
| 26 | 27 | ||
| 27 | FileType GetFileType() override { | 28 | FileType GetFileType() override { |
| 28 | return IdentifyType(file); | 29 | return IdentifyType(file, filepath); |
| 29 | } | 30 | } |
| 30 | 31 | ||
| 31 | static VAddr LoadModule(FileSys::VirtualFile file, VAddr load_base); | 32 | static VAddr LoadModule(const std::string& name, const std::vector<u8>& file_data, |
| 33 | VAddr load_base); | ||
| 34 | |||
| 35 | static VAddr LoadModule(const std::string& path, VAddr load_base); | ||
| 32 | 36 | ||
| 33 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; | 37 | ResultStatus Load(Kernel::SharedPtr<Kernel::Process>& process) override; |
| 38 | |||
| 39 | private: | ||
| 40 | std::string filepath; | ||
| 34 | }; | 41 | }; |
| 35 | 42 | ||
| 36 | } // namespace Loader | 43 | } // namespace Loader |
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h index b42913121..ab978c2e2 100644 --- a/src/video_core/engines/shader_bytecode.h +++ b/src/video_core/engines/shader_bytecode.h | |||
| @@ -194,6 +194,13 @@ enum class UniformType : u64 { | |||
| 194 | Double = 5, | 194 | Double = 5, |
| 195 | }; | 195 | }; |
| 196 | 196 | ||
| 197 | enum class IMinMaxExchange : u64 { | ||
| 198 | None = 0, | ||
| 199 | XLo = 1, | ||
| 200 | XMed = 2, | ||
| 201 | XHi = 3, | ||
| 202 | }; | ||
| 203 | |||
| 197 | union Instruction { | 204 | union Instruction { |
| 198 | Instruction& operator=(const Instruction& instr) { | 205 | Instruction& operator=(const Instruction& instr) { |
| 199 | value = instr.value; | 206 | value = instr.value; |
| @@ -279,6 +286,13 @@ union Instruction { | |||
| 279 | } alu_integer; | 286 | } alu_integer; |
| 280 | 287 | ||
| 281 | union { | 288 | union { |
| 289 | BitField<39, 3, u64> pred; | ||
| 290 | BitField<42, 1, u64> negate_pred; | ||
| 291 | BitField<43, 2, IMinMaxExchange> exchange; | ||
| 292 | BitField<48, 1, u64> is_signed; | ||
| 293 | } imnmx; | ||
| 294 | |||
| 295 | union { | ||
| 282 | BitField<54, 1, u64> saturate; | 296 | BitField<54, 1, u64> saturate; |
| 283 | BitField<56, 1, u64> negate_a; | 297 | BitField<56, 1, u64> negate_a; |
| 284 | } iadd32i; | 298 | } iadd32i; |
| @@ -673,7 +687,7 @@ private: | |||
| 673 | INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), | 687 | INST("1101101---------", Id::TLDS, Type::Memory, "TLDS"), |
| 674 | INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), | 688 | INST("111000110000----", Id::EXIT, Type::Trivial, "EXIT"), |
| 675 | INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), | 689 | INST("11100000--------", Id::IPA, Type::Trivial, "IPA"), |
| 676 | INST("001100101-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), | 690 | INST("0011001-1-------", Id::FFMA_IMM, Type::Ffma, "FFMA_IMM"), |
| 677 | INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), | 691 | INST("010010011-------", Id::FFMA_CR, Type::Ffma, "FFMA_CR"), |
| 678 | INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), | 692 | INST("010100011-------", Id::FFMA_RC, Type::Ffma, "FFMA_RC"), |
| 679 | INST("010110011-------", Id::FFMA_RR, Type::Ffma, "FFMA_RR"), | 693 | INST("010110011-------", Id::FFMA_RR, Type::Ffma, "FFMA_RR"), |
| @@ -709,9 +723,9 @@ private: | |||
| 709 | INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"), | 723 | INST("0100110001100---", Id::FMNMX_C, Type::Arithmetic, "FMNMX_C"), |
| 710 | INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"), | 724 | INST("0101110001100---", Id::FMNMX_R, Type::Arithmetic, "FMNMX_R"), |
| 711 | INST("0011100-01100---", Id::FMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"), | 725 | INST("0011100-01100---", Id::FMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"), |
| 712 | INST("0100110000100---", Id::IMNMX_C, Type::Arithmetic, "FMNMX_IMM"), | 726 | INST("0100110000100---", Id::IMNMX_C, Type::ArithmeticInteger, "IMNMX_C"), |
| 713 | INST("0101110000100---", Id::IMNMX_R, Type::Arithmetic, "FMNMX_IMM"), | 727 | INST("0101110000100---", Id::IMNMX_R, Type::ArithmeticInteger, "IMNMX_R"), |
| 714 | INST("0011100-00100---", Id::IMNMX_IMM, Type::Arithmetic, "FMNMX_IMM"), | 728 | INST("0011100-00100---", Id::IMNMX_IMM, Type::ArithmeticInteger, "IMNMX_IMM"), |
| 715 | INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"), | 729 | INST("0100110000000---", Id::BFE_C, Type::Bfe, "BFE_C"), |
| 716 | INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"), | 730 | INST("0101110000000---", Id::BFE_R, Type::Bfe, "BFE_R"), |
| 717 | INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"), | 731 | INST("0011100-00000---", Id::BFE_IMM, Type::Bfe, "BFE_IMM"), |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index bacb389e1..ea138d402 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp | |||
| @@ -775,10 +775,13 @@ void RasterizerOpenGL::SyncCullMode() { | |||
| 775 | state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face); | 775 | state.cull.front_face = MaxwellToGL::FrontFace(regs.cull.front_face); |
| 776 | state.cull.mode = MaxwellToGL::CullFace(regs.cull.cull_face); | 776 | state.cull.mode = MaxwellToGL::CullFace(regs.cull.cull_face); |
| 777 | 777 | ||
| 778 | const bool flip_triangles{regs.screen_y_control.triangle_rast_flip == 0 || | ||
| 779 | regs.viewport_transform[0].scale_y < 0.0f}; | ||
| 780 | |||
| 778 | // If the GPU is configured to flip the rasterized triangles, then we need to flip the | 781 | // If the GPU is configured to flip the rasterized triangles, then we need to flip the |
| 779 | // notion of front and back. Note: We flip the triangles when the value of the register is 0 | 782 | // notion of front and back. Note: We flip the triangles when the value of the register is 0 |
| 780 | // because OpenGL already does it for us. | 783 | // because OpenGL already does it for us. |
| 781 | if (regs.screen_y_control.triangle_rast_flip == 0) { | 784 | if (flip_triangles) { |
| 782 | if (state.cull.front_face == GL_CCW) | 785 | if (state.cull.front_face == GL_CCW) |
| 783 | state.cull.front_face = GL_CW; | 786 | state.cull.front_face = GL_CW; |
| 784 | else if (state.cull.front_face == GL_CW) | 787 | else if (state.cull.front_face == GL_CW) |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp index 57d7763ff..323ff7408 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.cpp | |||
| @@ -102,6 +102,8 @@ static constexpr std::array<FormatTuple, SurfaceParams::MaxPixelFormat> tex_form | |||
| 102 | {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, | 102 | {GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, |
| 103 | true}, // DXT45 | 103 | true}, // DXT45 |
| 104 | {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN1 | 104 | {GL_COMPRESSED_RED_RGTC1, GL_RED, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, true}, // DXN1 |
| 105 | {GL_COMPRESSED_RGBA_BPTC_UNORM_ARB, GL_RGB, GL_UNSIGNED_INT_8_8_8_8, ComponentType::UNorm, | ||
| 106 | true}, // BC7U | ||
| 105 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 | 107 | {GL_RGBA8, GL_RGBA, GL_UNSIGNED_BYTE, ComponentType::UNorm, false}, // ASTC_2D_4X4 |
| 106 | 108 | ||
| 107 | // DepthStencil formats | 109 | // DepthStencil formats |
| @@ -191,8 +193,9 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), | |||
| 191 | MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>, | 193 | MortonCopy<true, PixelFormat::R11FG11FB10F>, MortonCopy<true, PixelFormat::RGBA32UI>, |
| 192 | MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>, | 194 | MortonCopy<true, PixelFormat::DXT1>, MortonCopy<true, PixelFormat::DXT23>, |
| 193 | MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>, | 195 | MortonCopy<true, PixelFormat::DXT45>, MortonCopy<true, PixelFormat::DXN1>, |
| 194 | MortonCopy<true, PixelFormat::ASTC_2D_4X4>, MortonCopy<true, PixelFormat::Z24S8>, | 196 | MortonCopy<true, PixelFormat::BC7U>, MortonCopy<true, PixelFormat::ASTC_2D_4X4>, |
| 195 | MortonCopy<true, PixelFormat::S8Z24>, MortonCopy<true, PixelFormat::Z32F>, | 197 | MortonCopy<true, PixelFormat::Z24S8>, MortonCopy<true, PixelFormat::S8Z24>, |
| 198 | MortonCopy<true, PixelFormat::Z32F>, | ||
| 196 | }; | 199 | }; |
| 197 | 200 | ||
| 198 | static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), | 201 | static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), |
| @@ -206,7 +209,8 @@ static constexpr std::array<void (*)(u32, u32, u32, u8*, Tegra::GPUVAddr), | |||
| 206 | MortonCopy<false, PixelFormat::RGBA16F>, | 209 | MortonCopy<false, PixelFormat::RGBA16F>, |
| 207 | MortonCopy<false, PixelFormat::R11FG11FB10F>, | 210 | MortonCopy<false, PixelFormat::R11FG11FB10F>, |
| 208 | MortonCopy<false, PixelFormat::RGBA32UI>, | 211 | MortonCopy<false, PixelFormat::RGBA32UI>, |
| 209 | // TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1 formats is not yet supported | 212 | // TODO(Subv): Swizzling the DXT1/DXT23/DXT45/DXN1/BC7U formats is not yet supported |
| 213 | nullptr, | ||
| 210 | nullptr, | 214 | nullptr, |
| 211 | nullptr, | 215 | nullptr, |
| 212 | nullptr, | 216 | nullptr, |
diff --git a/src/video_core/renderer_opengl/gl_rasterizer_cache.h b/src/video_core/renderer_opengl/gl_rasterizer_cache.h index b4d7f8ebe..1bedae992 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer_cache.h +++ b/src/video_core/renderer_opengl/gl_rasterizer_cache.h | |||
| @@ -35,14 +35,15 @@ struct SurfaceParams { | |||
| 35 | DXT23 = 9, | 35 | DXT23 = 9, |
| 36 | DXT45 = 10, | 36 | DXT45 = 10, |
| 37 | DXN1 = 11, // This is also known as BC4 | 37 | DXN1 = 11, // This is also known as BC4 |
| 38 | ASTC_2D_4X4 = 12, | 38 | BC7U = 12, |
| 39 | ASTC_2D_4X4 = 13, | ||
| 39 | 40 | ||
| 40 | MaxColorFormat, | 41 | MaxColorFormat, |
| 41 | 42 | ||
| 42 | // DepthStencil formats | 43 | // DepthStencil formats |
| 43 | Z24S8 = 13, | 44 | Z24S8 = 14, |
| 44 | S8Z24 = 14, | 45 | S8Z24 = 15, |
| 45 | Z32F = 15, | 46 | Z32F = 16, |
| 46 | 47 | ||
| 47 | MaxDepthStencilFormat, | 48 | MaxDepthStencilFormat, |
| 48 | 49 | ||
| @@ -92,6 +93,7 @@ struct SurfaceParams { | |||
| 92 | 4, // DXT23 | 93 | 4, // DXT23 |
| 93 | 4, // DXT45 | 94 | 4, // DXT45 |
| 94 | 4, // DXN1 | 95 | 4, // DXN1 |
| 96 | 4, // BC7U | ||
| 95 | 4, // ASTC_2D_4X4 | 97 | 4, // ASTC_2D_4X4 |
| 96 | 1, // Z24S8 | 98 | 1, // Z24S8 |
| 97 | 1, // S8Z24 | 99 | 1, // S8Z24 |
| @@ -119,6 +121,7 @@ struct SurfaceParams { | |||
| 119 | 128, // DXT23 | 121 | 128, // DXT23 |
| 120 | 128, // DXT45 | 122 | 128, // DXT45 |
| 121 | 64, // DXN1 | 123 | 64, // DXN1 |
| 124 | 128, // BC7U | ||
| 122 | 32, // ASTC_2D_4X4 | 125 | 32, // ASTC_2D_4X4 |
| 123 | 32, // Z24S8 | 126 | 32, // Z24S8 |
| 124 | 32, // S8Z24 | 127 | 32, // S8Z24 |
| @@ -192,6 +195,8 @@ struct SurfaceParams { | |||
| 192 | return PixelFormat::DXT45; | 195 | return PixelFormat::DXT45; |
| 193 | case Tegra::Texture::TextureFormat::DXN1: | 196 | case Tegra::Texture::TextureFormat::DXN1: |
| 194 | return PixelFormat::DXN1; | 197 | return PixelFormat::DXN1; |
| 198 | case Tegra::Texture::TextureFormat::BC7U: | ||
| 199 | return PixelFormat::BC7U; | ||
| 195 | case Tegra::Texture::TextureFormat::ASTC_2D_4X4: | 200 | case Tegra::Texture::TextureFormat::ASTC_2D_4X4: |
| 196 | return PixelFormat::ASTC_2D_4X4; | 201 | return PixelFormat::ASTC_2D_4X4; |
| 197 | default: | 202 | default: |
| @@ -227,6 +232,8 @@ struct SurfaceParams { | |||
| 227 | return Tegra::Texture::TextureFormat::DXT45; | 232 | return Tegra::Texture::TextureFormat::DXT45; |
| 228 | case PixelFormat::DXN1: | 233 | case PixelFormat::DXN1: |
| 229 | return Tegra::Texture::TextureFormat::DXN1; | 234 | return Tegra::Texture::TextureFormat::DXN1; |
| 235 | case PixelFormat::BC7U: | ||
| 236 | return Tegra::Texture::TextureFormat::BC7U; | ||
| 230 | case PixelFormat::ASTC_2D_4X4: | 237 | case PixelFormat::ASTC_2D_4X4: |
| 231 | return Tegra::Texture::TextureFormat::ASTC_2D_4X4; | 238 | return Tegra::Texture::TextureFormat::ASTC_2D_4X4; |
| 232 | default: | 239 | default: |
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp index c5d27ec80..c29cabb84 100644 --- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp +++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp | |||
| @@ -1150,6 +1150,20 @@ private: | |||
| 1150 | WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b); | 1150 | WriteLogicOperation(instr.gpr0, instr.alu.lop.operation, op_a, op_b); |
| 1151 | break; | 1151 | break; |
| 1152 | } | 1152 | } |
| 1153 | case OpCode::Id::IMNMX_C: | ||
| 1154 | case OpCode::Id::IMNMX_R: | ||
| 1155 | case OpCode::Id::IMNMX_IMM: { | ||
| 1156 | ASSERT_MSG(instr.imnmx.exchange == Tegra::Shader::IMinMaxExchange::None, | ||
| 1157 | "Unimplemented"); | ||
| 1158 | std::string condition = | ||
| 1159 | GetPredicateCondition(instr.imnmx.pred, instr.imnmx.negate_pred != 0); | ||
| 1160 | std::string parameters = op_a + ',' + op_b; | ||
| 1161 | regs.SetRegisterToInteger(instr.gpr0, instr.imnmx.is_signed, 0, | ||
| 1162 | '(' + condition + ") ? min(" + parameters + ") : max(" + | ||
| 1163 | parameters + ')', | ||
| 1164 | 1, 1); | ||
| 1165 | break; | ||
| 1166 | } | ||
| 1153 | default: { | 1167 | default: { |
| 1154 | LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", | 1168 | LOG_CRITICAL(HW_GPU, "Unhandled ArithmeticInteger instruction: {}", |
| 1155 | opcode->GetName()); | 1169 | opcode->GetName()); |
diff --git a/src/video_core/textures/decoders.cpp b/src/video_core/textures/decoders.cpp index d5ab4e4f9..b3937b2fe 100644 --- a/src/video_core/textures/decoders.cpp +++ b/src/video_core/textures/decoders.cpp | |||
| @@ -52,6 +52,7 @@ u32 BytesPerPixel(TextureFormat format) { | |||
| 52 | return 8; | 52 | return 8; |
| 53 | case TextureFormat::DXT23: | 53 | case TextureFormat::DXT23: |
| 54 | case TextureFormat::DXT45: | 54 | case TextureFormat::DXT45: |
| 55 | case TextureFormat::BC7U: | ||
| 55 | // In this case a 'pixel' actually refers to a 4x4 tile. | 56 | // In this case a 'pixel' actually refers to a 4x4 tile. |
| 56 | return 16; | 57 | return 16; |
| 57 | case TextureFormat::ASTC_2D_4X4: | 58 | case TextureFormat::ASTC_2D_4X4: |
| @@ -98,6 +99,7 @@ std::vector<u8> UnswizzleTexture(VAddr address, TextureFormat format, u32 width, | |||
| 98 | case TextureFormat::DXT23: | 99 | case TextureFormat::DXT23: |
| 99 | case TextureFormat::DXT45: | 100 | case TextureFormat::DXT45: |
| 100 | case TextureFormat::DXN1: | 101 | case TextureFormat::DXN1: |
| 102 | case TextureFormat::BC7U: | ||
| 101 | // In the DXT and DXN formats, each 4x4 tile is swizzled instead of just individual pixel | 103 | // In the DXT and DXN formats, each 4x4 tile is swizzled instead of just individual pixel |
| 102 | // values. | 104 | // values. |
| 103 | CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data, | 105 | CopySwizzledData(width / 4, height / 4, bytes_per_pixel, bytes_per_pixel, data, |
| @@ -155,6 +157,7 @@ std::vector<u8> DecodeTexture(const std::vector<u8>& texture_data, TextureFormat | |||
| 155 | case TextureFormat::DXT23: | 157 | case TextureFormat::DXT23: |
| 156 | case TextureFormat::DXT45: | 158 | case TextureFormat::DXT45: |
| 157 | case TextureFormat::DXN1: | 159 | case TextureFormat::DXN1: |
| 160 | case TextureFormat::BC7U: | ||
| 158 | case TextureFormat::ASTC_2D_4X4: | 161 | case TextureFormat::ASTC_2D_4X4: |
| 159 | case TextureFormat::A8R8G8B8: | 162 | case TextureFormat::A8R8G8B8: |
| 160 | case TextureFormat::A2B10G10R10: | 163 | case TextureFormat::A2B10G10R10: |
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp index 5c17cd0d9..833085559 100644 --- a/src/yuzu/bootmanager.cpp +++ b/src/yuzu/bootmanager.cpp | |||
| @@ -127,13 +127,14 @@ void GRenderWindow::moveContext() { | |||
| 127 | } | 127 | } |
| 128 | 128 | ||
| 129 | void GRenderWindow::SwapBuffers() { | 129 | void GRenderWindow::SwapBuffers() { |
| 130 | #if !defined(QT_NO_DEBUG) | 130 | // In our multi-threaded QGLWidget use case we shouldn't need to call `makeCurrent`, |
| 131 | // Qt debug runtime prints a bogus warning on the console if you haven't called makeCurrent | 131 | // since we never call `doneCurrent` in this thread. |
| 132 | // since the last time you called swapBuffers. This presumably means something if you're using | 132 | // However: |
| 133 | // QGLWidget the "regular" way, but in our multi-threaded use case is harmless since we never | 133 | // - The Qt debug runtime prints a bogus warning on the console if `makeCurrent` wasn't called |
| 134 | // call doneCurrent in this thread. | 134 | // since the last time `swapBuffers` was executed; |
| 135 | // - On macOS, if `makeCurrent` isn't called explicitely, resizing the buffer breaks. | ||
| 135 | child->makeCurrent(); | 136 | child->makeCurrent(); |
| 136 | #endif | 137 | |
| 137 | child->swapBuffers(); | 138 | child->swapBuffers(); |
| 138 | } | 139 | } |
| 139 | 140 | ||
diff --git a/src/yuzu/debugger/console.cpp b/src/yuzu/debugger/console.cpp index e3d2d975f..320898f6a 100644 --- a/src/yuzu/debugger/console.cpp +++ b/src/yuzu/debugger/console.cpp | |||
| @@ -14,6 +14,13 @@ | |||
| 14 | 14 | ||
| 15 | namespace Debugger { | 15 | namespace Debugger { |
| 16 | void ToggleConsole() { | 16 | void ToggleConsole() { |
| 17 | static bool console_shown = false; | ||
| 18 | if (console_shown == UISettings::values.show_console) { | ||
| 19 | return; | ||
| 20 | } else { | ||
| 21 | console_shown = UISettings::values.show_console; | ||
| 22 | } | ||
| 23 | |||
| 17 | #if defined(_WIN32) && !defined(_DEBUG) | 24 | #if defined(_WIN32) && !defined(_DEBUG) |
| 18 | FILE* temp; | 25 | FILE* temp; |
| 19 | if (UISettings::values.show_console) { | 26 | if (UISettings::values.show_console) { |
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index be6cd6da4..5a708dc73 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp | |||
| @@ -12,7 +12,6 @@ | |||
| 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" | ||
| 16 | #include "core/loader/loader.h" | 15 | #include "core/loader/loader.h" |
| 17 | #include "game_list.h" | 16 | #include "game_list.h" |
| 18 | #include "game_list_p.h" | 17 | #include "game_list_p.h" |
| @@ -405,8 +404,7 @@ void GameListWorker::AddFstEntriesToGameList(const std::string& dir_path, unsign | |||
| 405 | bool is_dir = FileUtil::IsDirectory(physical_name); | 404 | bool is_dir = FileUtil::IsDirectory(physical_name); |
| 406 | if (!is_dir && | 405 | if (!is_dir && |
| 407 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { | 406 | (HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) { |
| 408 | std::unique_ptr<Loader::AppLoader> loader = | 407 | std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(physical_name); |
| 409 | Loader::GetLoader(std::make_shared<FileSys::RealVfsFile>(physical_name)); | ||
| 410 | if (!loader) | 408 | if (!loader) |
| 411 | return true; | 409 | return true; |
| 412 | 410 | ||
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 05a8ae6d2..9ce8d7c27 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp | |||
| @@ -908,8 +908,6 @@ void GMainWindow::UpdateUITheme() { | |||
| 908 | #endif | 908 | #endif |
| 909 | 909 | ||
| 910 | int main(int argc, char* argv[]) { | 910 | int main(int argc, char* argv[]) { |
| 911 | Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); | ||
| 912 | |||
| 913 | MicroProfileOnThreadCreate("Frontend"); | 911 | MicroProfileOnThreadCreate("Frontend"); |
| 914 | SCOPE_EXIT({ MicroProfileShutdown(); }); | 912 | SCOPE_EXIT({ MicroProfileShutdown(); }); |
| 915 | 913 | ||
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp index e6f0bbe8f..ec73f08bd 100644 --- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp +++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp | |||
| @@ -126,7 +126,7 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | |||
| 126 | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); | 126 | SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI); |
| 127 | 127 | ||
| 128 | if (render_window == nullptr) { | 128 | if (render_window == nullptr) { |
| 129 | LOG_CRITICAL(Frontend, "Failed to create SDL2 window! Exiting..."); | 129 | LOG_CRITICAL(Frontend, "Failed to create SDL2 window! {}", SDL_GetError()); |
| 130 | exit(1); | 130 | exit(1); |
| 131 | } | 131 | } |
| 132 | 132 | ||
| @@ -137,12 +137,12 @@ EmuWindow_SDL2::EmuWindow_SDL2(bool fullscreen) { | |||
| 137 | gl_context = SDL_GL_CreateContext(render_window); | 137 | gl_context = SDL_GL_CreateContext(render_window); |
| 138 | 138 | ||
| 139 | if (gl_context == nullptr) { | 139 | if (gl_context == nullptr) { |
| 140 | LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! Exiting..."); | 140 | LOG_CRITICAL(Frontend, "Failed to create SDL2 GL context! {}", SDL_GetError()); |
| 141 | exit(1); | 141 | exit(1); |
| 142 | } | 142 | } |
| 143 | 143 | ||
| 144 | if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { | 144 | if (!gladLoadGLLoader(static_cast<GLADloadproc>(SDL_GL_GetProcAddress))) { |
| 145 | LOG_CRITICAL(Frontend, "Failed to initialize GL functions! Exiting..."); | 145 | LOG_CRITICAL(Frontend, "Failed to initialize GL functions! {}", SDL_GetError()); |
| 146 | exit(1); | 146 | exit(1); |
| 147 | } | 147 | } |
| 148 | 148 | ||
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp index 8ddd202d8..f126bd277 100644 --- a/src/yuzu_cmd/yuzu.cpp +++ b/src/yuzu_cmd/yuzu.cpp | |||
| @@ -22,10 +22,8 @@ | |||
| 22 | #include "yuzu_cmd/config.h" | 22 | #include "yuzu_cmd/config.h" |
| 23 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" | 23 | #include "yuzu_cmd/emu_window/emu_window_sdl2.h" |
| 24 | 24 | ||
| 25 | #ifdef _MSC_VER | ||
| 26 | #include <getopt.h> | ||
| 27 | #else | ||
| 28 | #include <getopt.h> | 25 | #include <getopt.h> |
| 26 | #ifndef _MSC_VER | ||
| 29 | #include <unistd.h> | 27 | #include <unistd.h> |
| 30 | #endif | 28 | #endif |
| 31 | 29 | ||
| @@ -127,6 +125,7 @@ int main(int argc, char** argv) { | |||
| 127 | #endif | 125 | #endif |
| 128 | 126 | ||
| 129 | Log::Filter log_filter(Log::Level::Debug); | 127 | Log::Filter log_filter(Log::Level::Debug); |
| 128 | log_filter.ParseFilterString(Settings::values.log_filter); | ||
| 130 | Log::SetGlobalFilter(log_filter); | 129 | Log::SetGlobalFilter(log_filter); |
| 131 | 130 | ||
| 132 | Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); | 131 | Log::AddBackend(std::make_unique<Log::ColorConsoleBackend>()); |
| @@ -142,8 +141,6 @@ int main(int argc, char** argv) { | |||
| 142 | return -1; | 141 | return -1; |
| 143 | } | 142 | } |
| 144 | 143 | ||
| 145 | log_filter.ParseFilterString(Settings::values.log_filter); | ||
| 146 | |||
| 147 | // Apply the command line arguments | 144 | // Apply the command line arguments |
| 148 | Settings::values.gdbstub_port = gdb_port; | 145 | Settings::values.gdbstub_port = gdb_port; |
| 149 | Settings::values.use_gdbstub = use_gdbstub; | 146 | Settings::values.use_gdbstub = use_gdbstub; |