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