summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/core.cpp24
-rw-r--r--src/core/core.h8
-rw-r--r--src/core/crypto/partition_data_manager.cpp4
-rw-r--r--src/core/crypto/partition_data_manager.h1
-rw-r--r--src/core/file_sys/bis_factory.cpp101
-rw-r--r--src/core/file_sys/bis_factory.h38
-rw-r--r--src/core/file_sys/card_image.cpp24
-rw-r--r--src/core/file_sys/card_image.h9
-rw-r--r--src/core/file_sys/content_archive.cpp8
-rw-r--r--src/core/file_sys/content_archive.h2
-rw-r--r--src/core/file_sys/patch_manager.cpp41
-rw-r--r--src/core/file_sys/registered_cache.cpp188
-rw-r--r--src/core/file_sys/registered_cache.h25
-rw-r--r--src/core/file_sys/romfs_factory.cpp16
-rw-r--r--src/core/file_sys/romfs_factory.h4
-rw-r--r--src/core/file_sys/savedata_factory.cpp68
-rw-r--r--src/core/file_sys/savedata_factory.h6
-rw-r--r--src/core/file_sys/sdmc_factory.cpp27
-rw-r--r--src/core/file_sys/sdmc_factory.h13
-rw-r--r--src/core/file_sys/submission_package.cpp3
-rw-r--r--src/core/hle/service/am/am.cpp24
-rw-r--r--src/core/hle/service/am/applet_ae.h4
-rw-r--r--src/core/hle/service/am/applet_oe.h4
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp329
-rw-r--r--src/core/hle/service/filesystem/filesystem.h116
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp80
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.h4
-rw-r--r--src/core/hle/service/ns/ns.cpp4
-rw-r--r--src/core/hle/service/ns/ns.h14
-rw-r--r--src/core/hle/service/ns/pl_u.cpp5
-rw-r--r--src/core/hle/service/ns/pl_u.h14
-rw-r--r--src/core/hle/service/service.cpp3
-rw-r--r--src/core/hle/service/service.h8
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp4
-rw-r--r--src/core/loader/nca.cpp4
-rw-r--r--src/core/loader/nro.cpp4
-rw-r--r--src/core/loader/nsp.cpp4
-rw-r--r--src/core/loader/xci.cpp4
-rw-r--r--src/core/settings.cpp5
-rw-r--r--src/core/settings.h34
-rw-r--r--src/yuzu/CMakeLists.txt3
-rw-r--r--src/yuzu/configuration/config.cpp70
-rw-r--r--src/yuzu/configuration/configure.ui11
-rw-r--r--src/yuzu/configuration/configure_debug.cpp4
-rw-r--r--src/yuzu/configuration/configure_debug.ui80
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp4
-rw-r--r--src/yuzu/configuration/configure_filesystem.cpp177
-rw-r--r--src/yuzu/configuration/configure_filesystem.h43
-rw-r--r--src/yuzu/configuration/configure_filesystem.ui395
-rw-r--r--src/yuzu/configuration/configure_general.cpp1
-rw-r--r--src/yuzu/main.cpp41
-rw-r--r--src/yuzu_cmd/config.cpp23
-rw-r--r--src/yuzu_cmd/default_ini.h14
-rw-r--r--src/yuzu_cmd/yuzu.cpp2
-rw-r--r--src/yuzu_tester/yuzu.cpp4
55 files changed, 1885 insertions, 265 deletions
diff --git a/src/core/core.cpp b/src/core/core.cpp
index 9ab174de2..f22244cf7 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -14,8 +14,13 @@
14#include "core/core_cpu.h" 14#include "core/core_cpu.h"
15#include "core/core_timing.h" 15#include "core/core_timing.h"
16#include "core/cpu_core_manager.h" 16#include "core/cpu_core_manager.h"
17#include "core/file_sys/bis_factory.h"
18#include "core/file_sys/card_image.h"
17#include "core/file_sys/mode.h" 19#include "core/file_sys/mode.h"
18#include "core/file_sys/registered_cache.h" 20#include "core/file_sys/registered_cache.h"
21#include "core/file_sys/romfs_factory.h"
22#include "core/file_sys/savedata_factory.h"
23#include "core/file_sys/sdmc_factory.h"
19#include "core/file_sys/vfs_concat.h" 24#include "core/file_sys/vfs_concat.h"
20#include "core/file_sys/vfs_real.h" 25#include "core/file_sys/vfs_real.h"
21#include "core/gdbstub/gdbstub.h" 26#include "core/gdbstub/gdbstub.h"
@@ -27,6 +32,7 @@
27#include "core/hle/kernel/thread.h" 32#include "core/hle/kernel/thread.h"
28#include "core/hle/service/am/applets/applets.h" 33#include "core/hle/service/am/applets/applets.h"
29#include "core/hle/service/apm/controller.h" 34#include "core/hle/service/apm/controller.h"
35#include "core/hle/service/filesystem/filesystem.h"
30#include "core/hle/service/glue/manager.h" 36#include "core/hle/service/glue/manager.h"
31#include "core/hle/service/service.h" 37#include "core/hle/service/service.h"
32#include "core/hle/service/sm/sm.h" 38#include "core/hle/service/sm/sm.h"
@@ -202,6 +208,15 @@ struct System::Impl {
202 main_process->Run(load_parameters->main_thread_priority, 208 main_process->Run(load_parameters->main_thread_priority,
203 load_parameters->main_thread_stack_size); 209 load_parameters->main_thread_stack_size);
204 210
211 if (Settings::values.gamecard_inserted) {
212 if (Settings::values.gamecard_current_game) {
213 fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
214 } else if (!Settings::values.gamecard_path.empty()) {
215 fs_controller.SetGameCard(
216 GetGameFileFromPath(virtual_filesystem, Settings::values.gamecard_path));
217 }
218 }
219
205 u64 title_id{0}; 220 u64 title_id{0};
206 if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) { 221 if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
207 LOG_ERROR(Core, "Failed to find title id for ROM (Error {})", 222 LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
@@ -304,6 +319,7 @@ struct System::Impl {
304 FileSys::VirtualFilesystem virtual_filesystem; 319 FileSys::VirtualFilesystem virtual_filesystem;
305 /// ContentProviderUnion instance 320 /// ContentProviderUnion instance
306 std::unique_ptr<FileSys::ContentProviderUnion> content_provider; 321 std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
322 Service::FileSystem::FileSystemController fs_controller;
307 /// AppLoader used to load the current executing application 323 /// AppLoader used to load the current executing application
308 std::unique_ptr<Loader::AppLoader> app_loader; 324 std::unique_ptr<Loader::AppLoader> app_loader;
309 std::unique_ptr<VideoCore::RendererBase> renderer; 325 std::unique_ptr<VideoCore::RendererBase> renderer;
@@ -571,6 +587,14 @@ const FileSys::ContentProvider& System::GetContentProvider() const {
571 return *impl->content_provider; 587 return *impl->content_provider;
572} 588}
573 589
590Service::FileSystem::FileSystemController& System::GetFileSystemController() {
591 return impl->fs_controller;
592}
593
594const Service::FileSystem::FileSystemController& System::GetFileSystemController() const {
595 return impl->fs_controller;
596}
597
574void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, 598void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
575 FileSys::ContentProvider* provider) { 599 FileSys::ContentProvider* provider) {
576 impl->content_provider->SetSlot(slot, provider); 600 impl->content_provider->SetSlot(slot, provider);
diff --git a/src/core/core.h b/src/core/core.h
index 0138d93b0..bb2962fdd 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -47,6 +47,10 @@ namespace APM {
47class Controller; 47class Controller;
48} 48}
49 49
50namespace FileSystem {
51class FileSystemController;
52} // namespace FileSystem
53
50namespace Glue { 54namespace Glue {
51class ARPManager; 55class ARPManager;
52} 56}
@@ -299,6 +303,10 @@ public:
299 303
300 const FileSys::ContentProvider& GetContentProvider() const; 304 const FileSys::ContentProvider& GetContentProvider() const;
301 305
306 Service::FileSystem::FileSystemController& GetFileSystemController();
307
308 const Service::FileSystem::FileSystemController& GetFileSystemController() const;
309
302 void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot, 310 void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
303 FileSys::ContentProvider* provider); 311 FileSys::ContentProvider* provider);
304 312
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index 01a969be9..594cd82c5 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -480,6 +480,10 @@ void PartitionDataManager::DecryptProdInfo(std::array<u8, 0x20> bis_key) {
480 prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key); 480 prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
481} 481}
482 482
483FileSys::VirtualFile PartitionDataManager::GetDecryptedProdInfo() const {
484 return prodinfo_decrypted;
485}
486
483std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const { 487std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
484 std::array<u8, 0x240> out{}; 488 std::array<u8, 0x240> out{};
485 if (prodinfo_decrypted != nullptr) 489 if (prodinfo_decrypted != nullptr)
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
index 0ad007c72..7a7b5d038 100644
--- a/src/core/crypto/partition_data_manager.h
+++ b/src/core/crypto/partition_data_manager.h
@@ -84,6 +84,7 @@ public:
84 bool HasProdInfo() const; 84 bool HasProdInfo() const;
85 FileSys::VirtualFile GetProdInfoRaw() const; 85 FileSys::VirtualFile GetProdInfoRaw() const;
86 void DecryptProdInfo(std::array<u8, 0x20> bis_key); 86 void DecryptProdInfo(std::array<u8, 0x20> bis_key);
87 FileSys::VirtualFile GetDecryptedProdInfo() const;
87 std::array<u8, 0x240> GetETicketExtendedKek() const; 88 std::array<u8, 0x240> GetETicketExtendedKek() const;
88 89
89private: 90private:
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index e29f70b3a..8f758d6d9 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -3,8 +3,12 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <fmt/format.h> 5#include <fmt/format.h>
6#include "common/file_util.h"
7#include "core/core.h"
6#include "core/file_sys/bis_factory.h" 8#include "core/file_sys/bis_factory.h"
9#include "core/file_sys/mode.h"
7#include "core/file_sys/registered_cache.h" 10#include "core/file_sys/registered_cache.h"
11#include "core/settings.h"
8 12
9namespace FileSys { 13namespace FileSys {
10 14
@@ -14,10 +18,22 @@ BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir
14 sysnand_cache(std::make_unique<RegisteredCache>( 18 sysnand_cache(std::make_unique<RegisteredCache>(
15 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), 19 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
16 usrnand_cache(std::make_unique<RegisteredCache>( 20 usrnand_cache(std::make_unique<RegisteredCache>(
17 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} 21 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))),
22 sysnand_placeholder(std::make_unique<PlaceholderCache>(
23 GetOrCreateDirectoryRelative(nand_root, "/system/Contents/placehld"))),
24 usrnand_placeholder(std::make_unique<PlaceholderCache>(
25 GetOrCreateDirectoryRelative(nand_root, "/user/Contents/placehld"))) {}
18 26
19BISFactory::~BISFactory() = default; 27BISFactory::~BISFactory() = default;
20 28
29VirtualDir BISFactory::GetSystemNANDContentDirectory() const {
30 return GetOrCreateDirectoryRelative(nand_root, "/system/Contents");
31}
32
33VirtualDir BISFactory::GetUserNANDContentDirectory() const {
34 return GetOrCreateDirectoryRelative(nand_root, "/user/Contents");
35}
36
21RegisteredCache* BISFactory::GetSystemNANDContents() const { 37RegisteredCache* BISFactory::GetSystemNANDContents() const {
22 return sysnand_cache.get(); 38 return sysnand_cache.get();
23} 39}
@@ -26,9 +42,17 @@ RegisteredCache* BISFactory::GetUserNANDContents() const {
26 return usrnand_cache.get(); 42 return usrnand_cache.get();
27} 43}
28 44
45PlaceholderCache* BISFactory::GetSystemNANDPlaceholder() const {
46 return sysnand_placeholder.get();
47}
48
49PlaceholderCache* BISFactory::GetUserNANDPlaceholder() const {
50 return usrnand_placeholder.get();
51}
52
29VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { 53VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
30 // LayeredFS doesn't work on updates and title id-less homebrew 54 // LayeredFS doesn't work on updates and title id-less homebrew
31 if (title_id == 0 || (title_id & 0x800) > 0) 55 if (title_id == 0 || (title_id & 0xFFF) == 0x800)
32 return nullptr; 56 return nullptr;
33 return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); 57 return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
34} 58}
@@ -39,4 +63,77 @@ VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
39 return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id)); 63 return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
40} 64}
41 65
66VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
67 switch (id) {
68 case BisPartitionId::CalibrationFile:
69 return GetOrCreateDirectoryRelative(nand_root, "/prodinfof");
70 case BisPartitionId::SafeMode:
71 return GetOrCreateDirectoryRelative(nand_root, "/safe");
72 case BisPartitionId::System:
73 return GetOrCreateDirectoryRelative(nand_root, "/system");
74 case BisPartitionId::User:
75 return GetOrCreateDirectoryRelative(nand_root, "/user");
76 default:
77 return nullptr;
78 }
79}
80
81VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
82 Core::Crypto::KeyManager keys;
83 Core::Crypto::PartitionDataManager pdm{
84 Core::System::GetInstance().GetFilesystem()->OpenDirectory(
85 FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
86 keys.PopulateFromPartitionData(pdm);
87
88 switch (id) {
89 case BisPartitionId::CalibrationBinary:
90 return pdm.GetDecryptedProdInfo();
91 case BisPartitionId::BootConfigAndPackage2Part1:
92 case BisPartitionId::BootConfigAndPackage2Part2:
93 case BisPartitionId::BootConfigAndPackage2Part3:
94 case BisPartitionId::BootConfigAndPackage2Part4:
95 case BisPartitionId::BootConfigAndPackage2Part5:
96 case BisPartitionId::BootConfigAndPackage2Part6: {
97 const auto new_id = static_cast<u8>(id) -
98 static_cast<u8>(BisPartitionId::BootConfigAndPackage2Part1) +
99 static_cast<u8>(Core::Crypto::Package2Type::NormalMain);
100 return pdm.GetPackage2Raw(static_cast<Core::Crypto::Package2Type>(new_id));
101 }
102 default:
103 return nullptr;
104 }
105}
106
107VirtualDir BISFactory::GetImageDirectory() const {
108 return GetOrCreateDirectoryRelative(nand_root, "/user/Album");
109}
110
111u64 BISFactory::GetSystemNANDFreeSpace() const {
112 const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
113 if (sys_dir == nullptr)
114 return 0;
115
116 return GetSystemNANDTotalSpace() - sys_dir->GetSize();
117}
118
119u64 BISFactory::GetSystemNANDTotalSpace() const {
120 return static_cast<u64>(Settings::values.nand_system_size);
121}
122
123u64 BISFactory::GetUserNANDFreeSpace() const {
124 const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
125 if (usr_dir == nullptr)
126 return 0;
127
128 return GetUserNANDTotalSpace() - usr_dir->GetSize();
129}
130
131u64 BISFactory::GetUserNANDTotalSpace() const {
132 return static_cast<u64>(Settings::values.nand_user_size);
133}
134
135u64 BISFactory::GetFullNANDTotalSpace() const {
136 return static_cast<u64>(Settings::values.nand_total_size);
137}
138
42} // namespace FileSys 139} // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 453c11ad2..bdfe728c9 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -10,7 +10,25 @@
10 10
11namespace FileSys { 11namespace FileSys {
12 12
13enum class BisPartitionId : u32 {
14 UserDataRoot = 20,
15 CalibrationBinary = 27,
16 CalibrationFile = 28,
17 BootConfigAndPackage2Part1 = 21,
18 BootConfigAndPackage2Part2 = 22,
19 BootConfigAndPackage2Part3 = 23,
20 BootConfigAndPackage2Part4 = 24,
21 BootConfigAndPackage2Part5 = 25,
22 BootConfigAndPackage2Part6 = 26,
23 SafeMode = 29,
24 System = 31,
25 SystemProperEncryption = 32,
26 SystemProperPartition = 33,
27 User = 30,
28};
29
13class RegisteredCache; 30class RegisteredCache;
31class PlaceholderCache;
14 32
15/// File system interface to the Built-In Storage 33/// File system interface to the Built-In Storage
16/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND 34/// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND
@@ -20,12 +38,29 @@ public:
20 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root); 38 explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
21 ~BISFactory(); 39 ~BISFactory();
22 40
41 VirtualDir GetSystemNANDContentDirectory() const;
42 VirtualDir GetUserNANDContentDirectory() const;
43
23 RegisteredCache* GetSystemNANDContents() const; 44 RegisteredCache* GetSystemNANDContents() const;
24 RegisteredCache* GetUserNANDContents() const; 45 RegisteredCache* GetUserNANDContents() const;
25 46
47 PlaceholderCache* GetSystemNANDPlaceholder() const;
48 PlaceholderCache* GetUserNANDPlaceholder() const;
49
26 VirtualDir GetModificationLoadRoot(u64 title_id) const; 50 VirtualDir GetModificationLoadRoot(u64 title_id) const;
27 VirtualDir GetModificationDumpRoot(u64 title_id) const; 51 VirtualDir GetModificationDumpRoot(u64 title_id) const;
28 52
53 VirtualDir OpenPartition(BisPartitionId id) const;
54 VirtualFile OpenPartitionStorage(BisPartitionId id) const;
55
56 VirtualDir GetImageDirectory() const;
57
58 u64 GetSystemNANDFreeSpace() const;
59 u64 GetSystemNANDTotalSpace() const;
60 u64 GetUserNANDFreeSpace() const;
61 u64 GetUserNANDTotalSpace() const;
62 u64 GetFullNANDTotalSpace() const;
63
29private: 64private:
30 VirtualDir nand_root; 65 VirtualDir nand_root;
31 VirtualDir load_root; 66 VirtualDir load_root;
@@ -33,6 +68,9 @@ private:
33 68
34 std::unique_ptr<RegisteredCache> sysnand_cache; 69 std::unique_ptr<RegisteredCache> sysnand_cache;
35 std::unique_ptr<RegisteredCache> usrnand_cache; 70 std::unique_ptr<RegisteredCache> usrnand_cache;
71
72 std::unique_ptr<PlaceholderCache> sysnand_placeholder;
73 std::unique_ptr<PlaceholderCache> usrnand_placeholder;
36}; 74};
37 75
38} // namespace FileSys 76} // namespace FileSys
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 626ed0042..db54113a0 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -12,12 +12,16 @@
12#include "core/file_sys/content_archive.h" 12#include "core/file_sys/content_archive.h"
13#include "core/file_sys/nca_metadata.h" 13#include "core/file_sys/nca_metadata.h"
14#include "core/file_sys/partition_filesystem.h" 14#include "core/file_sys/partition_filesystem.h"
15#include "core/file_sys/romfs.h"
15#include "core/file_sys/submission_package.h" 16#include "core/file_sys/submission_package.h"
17#include "core/file_sys/vfs_concat.h"
16#include "core/file_sys/vfs_offset.h" 18#include "core/file_sys/vfs_offset.h"
19#include "core/file_sys/vfs_vector.h"
17#include "core/loader/loader.h" 20#include "core/loader/loader.h"
18 21
19namespace FileSys { 22namespace FileSys {
20 23
24constexpr u64 GAMECARD_CERTIFICATE_OFFSET = 0x7000;
21constexpr std::array partition_names{ 25constexpr std::array partition_names{
22 "update", 26 "update",
23 "normal", 27 "normal",
@@ -175,6 +179,26 @@ VirtualDir XCI::GetParentDirectory() const {
175 return file->GetContainingDirectory(); 179 return file->GetContainingDirectory();
176} 180}
177 181
182VirtualDir XCI::ConcatenatedPseudoDirectory() {
183 const auto out = std::make_shared<VectorVfsDirectory>();
184 for (const auto& part_id : {XCIPartition::Normal, XCIPartition::Logo, XCIPartition::Secure}) {
185 const auto& part = GetPartition(part_id);
186 if (part == nullptr)
187 continue;
188
189 for (const auto& file : part->GetFiles())
190 out->AddFile(file);
191 }
192
193 return out;
194}
195
196std::array<u8, 0x200> XCI::GetCertificate() const {
197 std::array<u8, 0x200> out;
198 file->Read(out.data(), out.size(), GAMECARD_CERTIFICATE_OFFSET);
199 return out;
200}
201
178Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { 202Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
179 const auto partition_index = static_cast<std::size_t>(part); 203 const auto partition_index = static_cast<std::size_t>(part);
180 const auto& partition = partitions[partition_index]; 204 const auto& partition = partitions[partition_index];
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index a350496f7..3e6b92ff3 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -91,6 +91,8 @@ public:
91 VirtualDir GetLogoPartition() const; 91 VirtualDir GetLogoPartition() const;
92 92
93 u64 GetProgramTitleID() const; 93 u64 GetProgramTitleID() const;
94 u32 GetSystemUpdateVersion();
95 u64 GetSystemUpdateTitleID() const;
94 96
95 bool HasProgramNCA() const; 97 bool HasProgramNCA() const;
96 VirtualFile GetProgramNCAFile() const; 98 VirtualFile GetProgramNCAFile() const;
@@ -106,6 +108,11 @@ public:
106 108
107 VirtualDir GetParentDirectory() const override; 109 VirtualDir GetParentDirectory() const override;
108 110
111 // Creates a directory that contains all the NCAs in the gamecard
112 VirtualDir ConcatenatedPseudoDirectory();
113
114 std::array<u8, 0x200> GetCertificate() const;
115
109private: 116private:
110 Loader::ResultStatus AddNCAFromPartition(XCIPartition part); 117 Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
111 118
@@ -120,6 +127,8 @@ private:
120 std::shared_ptr<NCA> program; 127 std::shared_ptr<NCA> program;
121 std::vector<std::shared_ptr<NCA>> ncas; 128 std::vector<std::shared_ptr<NCA>> ncas;
122 129
130 u64 update_normal_partition_end;
131
123 Core::Crypto::KeyManager keys; 132 Core::Crypto::KeyManager keys;
124}; 133};
125} // namespace FileSys 134} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index ce5c69b41..ea5c92f61 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -528,6 +528,14 @@ u64 NCA::GetTitleId() const {
528 return header.title_id; 528 return header.title_id;
529} 529}
530 530
531std::array<u8, 16> NCA::GetRightsId() const {
532 return header.rights_id;
533}
534
535u32 NCA::GetSDKVersion() const {
536 return header.sdk_version;
537}
538
531bool NCA::IsUpdate() const { 539bool NCA::IsUpdate() const {
532 return is_update; 540 return is_update;
533} 541}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 15b9e6624..e249079b5 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -112,6 +112,8 @@ public:
112 112
113 NCAContentType GetType() const; 113 NCAContentType GetType() const;
114 u64 GetTitleId() const; 114 u64 GetTitleId() const;
115 std::array<u8, 0x10> GetRightsId() const;
116 u32 GetSDKVersion() const;
115 bool IsUpdate() const; 117 bool IsUpdate() const;
116 118
117 VirtualFile GetRomFS() const; 119 VirtualFile GetRomFS() const;
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index a8f80e2c6..c1dd0c6d7 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -63,7 +63,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
63 63
64 if (Settings::values.dump_exefs) { 64 if (Settings::values.dump_exefs) {
65 LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); 65 LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
66 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); 66 const auto dump_dir =
67 Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
67 if (dump_dir != nullptr) { 68 if (dump_dir != nullptr) {
68 const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); 69 const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
69 VfsRawCopyD(exefs, exefs_dir); 70 VfsRawCopyD(exefs, exefs_dir);
@@ -88,7 +89,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
88 } 89 }
89 90
90 // LayeredExeFS 91 // LayeredExeFS
91 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 92 const auto load_dir =
93 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
92 if (load_dir != nullptr && load_dir->GetSize() > 0) { 94 if (load_dir != nullptr && load_dir->GetSize() > 0) {
93 auto patch_dirs = load_dir->GetSubdirectories(); 95 auto patch_dirs = load_dir->GetSubdirectories();
94 std::sort( 96 std::sort(
@@ -174,7 +176,8 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
174 if (Settings::values.dump_nso) { 176 if (Settings::values.dump_nso) {
175 LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id, 177 LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
176 title_id); 178 title_id);
177 const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); 179 const auto dump_dir =
180 Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
178 if (dump_dir != nullptr) { 181 if (dump_dir != nullptr) {
179 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); 182 const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
180 const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id)); 183 const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
@@ -186,7 +189,13 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
186 189
187 LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); 190 LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
188 191
189 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 192 const auto load_dir =
193 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
194 if (load_dir == nullptr) {
195 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
196 return nso;
197 }
198
190 auto patch_dirs = load_dir->GetSubdirectories(); 199 auto patch_dirs = load_dir->GetSubdirectories();
191 std::sort(patch_dirs.begin(), patch_dirs.end(), 200 std::sort(patch_dirs.begin(), patch_dirs.end(),
192 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 201 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -224,7 +233,13 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
224 233
225 LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); 234 LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
226 235
227 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 236 const auto load_dir =
237 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
238 if (load_dir == nullptr) {
239 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
240 return false;
241 }
242
228 auto patch_dirs = load_dir->GetSubdirectories(); 243 auto patch_dirs = load_dir->GetSubdirectories();
229 std::sort(patch_dirs.begin(), patch_dirs.end(), 244 std::sort(patch_dirs.begin(), patch_dirs.end(),
230 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 245 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -258,7 +273,13 @@ static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& syst
258 273
259std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system, 274std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
260 const std::array<u8, 32>& build_id_) const { 275 const std::array<u8, 32>& build_id_) const {
261 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 276 const auto load_dir =
277 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
278 if (load_dir == nullptr) {
279 LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
280 return {};
281 }
282
262 auto patch_dirs = load_dir->GetSubdirectories(); 283 auto patch_dirs = load_dir->GetSubdirectories();
263 std::sort(patch_dirs.begin(), patch_dirs.end(), 284 std::sort(patch_dirs.begin(), patch_dirs.end(),
264 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); 285 [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -284,7 +305,8 @@ std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
284} 305}
285 306
286static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { 307static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
287 const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 308 const auto load_dir =
309 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
288 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || 310 if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
289 load_dir == nullptr || load_dir->GetSize() <= 0) { 311 load_dir == nullptr || load_dir->GetSize() <= 0) {
290 return; 312 return;
@@ -393,6 +415,8 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
393 415
394std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( 416std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
395 VirtualFile update_raw) const { 417 VirtualFile update_raw) const {
418 if (title_id == 0)
419 return {};
396 std::map<std::string, std::string, std::less<>> out; 420 std::map<std::string, std::string, std::less<>> out;
397 const auto& installed = Core::System::GetInstance().GetContentProvider(); 421 const auto& installed = Core::System::GetInstance().GetContentProvider();
398 const auto& disabled = Settings::values.disabled_addons[title_id]; 422 const auto& disabled = Settings::values.disabled_addons[title_id];
@@ -423,7 +447,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
423 } 447 }
424 448
425 // General Mods (LayeredFS and IPS) 449 // General Mods (LayeredFS and IPS)
426 const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id); 450 const auto mod_dir =
451 Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
427 if (mod_dir != nullptr && mod_dir->GetSize() > 0) { 452 if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
428 for (const auto& mod : mod_dir->GetSubdirectories()) { 453 for (const auto& mod : mod_dir->GetSubdirectories()) {
429 std::string types; 454 std::string types;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 3725b10f7..ac3fbd849 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -3,6 +3,7 @@
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include <algorithm> 5#include <algorithm>
6#include <random>
6#include <regex> 7#include <regex>
7#include <mbedtls/sha256.h> 8#include <mbedtls/sha256.h>
8#include "common/assert.h" 9#include "common/assert.h"
@@ -48,18 +49,21 @@ static bool FollowsTwoDigitDirFormat(std::string_view name) {
48static bool FollowsNcaIdFormat(std::string_view name) { 49static bool FollowsNcaIdFormat(std::string_view name) {
49 static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript | 50 static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript |
50 std::regex_constants::icase); 51 std::regex_constants::icase);
51 return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex); 52 static const std::regex nca_id_cnmt_regex(
53 "[0-9A-F]{32}\\.cnmt.nca", std::regex_constants::ECMAScript | std::regex_constants::icase);
54 return (name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex)) ||
55 (name.size() == 41 && std::regex_match(name.begin(), name.end(), nca_id_cnmt_regex));
52} 56}
53 57
54static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper, 58static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
55 bool within_two_digit) { 59 bool within_two_digit, bool cnmt_suffix) {
56 if (!within_two_digit) { 60 if (!within_two_digit)
57 return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper)); 61 return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca",
58 } 62 Common::HexToString(nca_id, second_hex_upper));
59 63
60 Core::Crypto::SHA256Hash hash{}; 64 Core::Crypto::SHA256Hash hash{};
61 mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); 65 mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
62 return fmt::format("/000000{:02X}/{}.nca", hash[0], 66 return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0],
63 Common::HexToString(nca_id, second_hex_upper)); 67 Common::HexToString(nca_id, second_hex_upper));
64} 68}
65 69
@@ -127,6 +131,156 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
127 return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt); 131 return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
128} 132}
129 133
134PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
135
136bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
137 const auto path = GetRelativePathFromNcaID(id, false, true, false);
138
139 if (dir->GetFileRelative(path) != nullptr) {
140 return false;
141 }
142
143 Core::Crypto::SHA256Hash hash{};
144 mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
145 const auto dirname = fmt::format("000000{:02X}", hash[0]);
146
147 const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
148
149 if (dir2 == nullptr)
150 return false;
151
152 const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexToString(id, false)));
153
154 if (file == nullptr)
155 return false;
156
157 return file->Resize(size);
158}
159
160bool PlaceholderCache::Delete(const NcaID& id) const {
161 const auto path = GetRelativePathFromNcaID(id, false, true, false);
162
163 if (dir->GetFileRelative(path) == nullptr) {
164 return false;
165 }
166
167 Core::Crypto::SHA256Hash hash{};
168 mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
169 const auto dirname = fmt::format("000000{:02X}", hash[0]);
170
171 const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
172
173 const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
174
175 return res;
176}
177
178bool PlaceholderCache::Exists(const NcaID& id) const {
179 const auto path = GetRelativePathFromNcaID(id, false, true, false);
180
181 return dir->GetFileRelative(path) != nullptr;
182}
183
184bool PlaceholderCache::Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const {
185 const auto path = GetRelativePathFromNcaID(id, false, true, false);
186 const auto file = dir->GetFileRelative(path);
187
188 if (file == nullptr)
189 return false;
190
191 return file->WriteBytes(data, offset) == data.size();
192}
193
194bool PlaceholderCache::Register(RegisteredCache* cache, const NcaID& placeholder,
195 const NcaID& install) const {
196 const auto path = GetRelativePathFromNcaID(placeholder, false, true, false);
197 const auto file = dir->GetFileRelative(path);
198
199 if (file == nullptr)
200 return false;
201
202 const auto res = cache->RawInstallNCA(NCA{file}, &VfsRawCopy, false, install);
203
204 if (res != InstallResult::Success)
205 return false;
206
207 return Delete(placeholder);
208}
209
210bool PlaceholderCache::CleanAll() const {
211 return dir->GetParentDirectory()->CleanSubdirectoryRecursive(dir->GetName());
212}
213
214std::optional<std::array<u8, 0x10>> PlaceholderCache::GetRightsID(const NcaID& id) const {
215 const auto path = GetRelativePathFromNcaID(id, false, true, false);
216 const auto file = dir->GetFileRelative(path);
217
218 if (file == nullptr)
219 return std::nullopt;
220
221 NCA nca{file};
222
223 if (nca.GetStatus() != Loader::ResultStatus::Success &&
224 nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
225 return std::nullopt;
226 }
227
228 const auto rights_id = nca.GetRightsId();
229 if (rights_id == NcaID{})
230 return std::nullopt;
231
232 return rights_id;
233}
234
235u64 PlaceholderCache::Size(const NcaID& id) const {
236 const auto path = GetRelativePathFromNcaID(id, false, true, false);
237 const auto file = dir->GetFileRelative(path);
238
239 if (file == nullptr)
240 return 0;
241
242 return file->GetSize();
243}
244
245bool PlaceholderCache::SetSize(const NcaID& id, u64 new_size) const {
246 const auto path = GetRelativePathFromNcaID(id, false, true, false);
247 const auto file = dir->GetFileRelative(path);
248
249 if (file == nullptr)
250 return false;
251
252 return file->Resize(new_size);
253}
254
255std::vector<NcaID> PlaceholderCache::List() const {
256 std::vector<NcaID> out;
257 for (const auto& sdir : dir->GetSubdirectories()) {
258 for (const auto& file : sdir->GetFiles()) {
259 const auto name = file->GetName();
260 if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' &&
261 name[35] == 'a') {
262 out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
263 }
264 }
265 }
266 return out;
267}
268
269NcaID PlaceholderCache::Generate() {
270 std::random_device device;
271 std::mt19937 gen(device());
272 std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
273
274 NcaID out{};
275
276 const auto v1 = distribution(gen);
277 const auto v2 = distribution(gen);
278 std::memcpy(out.data(), &v1, sizeof(u64));
279 std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64));
280
281 return out;
282}
283
130VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, 284VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
131 std::string_view path) const { 285 std::string_view path) const {
132 const auto file = dir->GetFileRelative(path); 286 const auto file = dir->GetFileRelative(path);
@@ -169,14 +323,18 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
169 323
170VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { 324VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
171 VirtualFile file; 325 VirtualFile file;
172 // Try all four modes of file storage: 326 // Try all five relevant modes of file storage:
173 // (bit 1 = uppercase/lower, bit 0 = within a two-digit dir) 327 // (bit 2 = uppercase/lower, bit 1 = within a two-digit dir, bit 0 = .cnmt suffix)
174 // 00: /000000**/{:032X}.nca 328 // 000: /000000**/{:032X}.nca
175 // 01: /{:032X}.nca 329 // 010: /{:032X}.nca
176 // 10: /000000**/{:032x}.nca 330 // 100: /000000**/{:032x}.nca
177 // 11: /{:032x}.nca 331 // 110: /{:032x}.nca
178 for (u8 i = 0; i < 4; ++i) { 332 // 111: /{:032x}.cnmt.nca
179 const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0); 333 for (u8 i = 0; i < 8; ++i) {
334 if ((i % 2) == 1 && i != 7)
335 continue;
336 const auto path =
337 GetRelativePathFromNcaID(id, (i & 0b100) == 0, (i & 0b010) == 0, (i & 0b001) == 0b001);
180 file = OpenFileOrDirectoryConcat(dir, path); 338 file = OpenFileOrDirectoryConcat(dir, path);
181 if (file != nullptr) 339 if (file != nullptr)
182 return file; 340 return file;
@@ -472,7 +630,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
472 memcpy(id.data(), hash.data(), 16); 630 memcpy(id.data(), hash.data(), 16);
473 } 631 }
474 632
475 std::string path = GetRelativePathFromNcaID(id, false, true); 633 std::string path = GetRelativePathFromNcaID(id, false, true, false);
476 634
477 if (GetFileAtID(id) != nullptr && !overwrite_if_exists) { 635 if (GetFileAtID(id) != nullptr && !overwrite_if_exists) {
478 LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping..."); 636 LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping...");
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 4398d63e1..d1eec240e 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -25,6 +25,8 @@ enum class NCAContentType : u8;
25enum class TitleType : u8; 25enum class TitleType : u8;
26 26
27struct ContentRecord; 27struct ContentRecord;
28struct MetaRecord;
29class RegisteredCache;
28 30
29using NcaID = std::array<u8, 0x10>; 31using NcaID = std::array<u8, 0x10>;
30using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; 32using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
@@ -89,6 +91,27 @@ protected:
89 Core::Crypto::KeyManager keys; 91 Core::Crypto::KeyManager keys;
90}; 92};
91 93
94class PlaceholderCache {
95public:
96 explicit PlaceholderCache(VirtualDir dir);
97
98 bool Create(const NcaID& id, u64 size) const;
99 bool Delete(const NcaID& id) const;
100 bool Exists(const NcaID& id) const;
101 bool Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const;
102 bool Register(RegisteredCache* cache, const NcaID& placeholder, const NcaID& install) const;
103 bool CleanAll() const;
104 std::optional<std::array<u8, 0x10>> GetRightsID(const NcaID& id) const;
105 u64 Size(const NcaID& id) const;
106 bool SetSize(const NcaID& id, u64 new_size) const;
107 std::vector<NcaID> List() const;
108
109 static NcaID Generate();
110
111private:
112 VirtualDir dir;
113};
114
92/* 115/*
93 * A class that catalogues NCAs in the registered directory structure. 116 * A class that catalogues NCAs in the registered directory structure.
94 * Nintendo's registered format follows this structure: 117 * Nintendo's registered format follows this structure:
@@ -103,6 +126,8 @@ protected:
103 * when 4GB splitting can be ignored.) 126 * when 4GB splitting can be ignored.)
104 */ 127 */
105class RegisteredCache : public ContentProvider { 128class RegisteredCache : public ContentProvider {
129 friend class PlaceholderCache;
130
106public: 131public:
107 // Parsing function defines the conversion from raw file to NCA. If there are other steps 132 // Parsing function defines the conversion from raw file to NCA. If there are other steps
108 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom 133 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index b2ccb2926..84cd4684c 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -7,6 +7,7 @@
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/core.h" 9#include "core/core.h"
10#include "core/file_sys/card_image.h"
10#include "core/file_sys/content_archive.h" 11#include "core/file_sys/content_archive.h"
11#include "core/file_sys/nca_metadata.h" 12#include "core/file_sys/nca_metadata.h"
12#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
@@ -34,7 +35,7 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
34 this->update_raw = std::move(update_raw); 35 this->update_raw = std::move(update_raw);
35} 36}
36 37
37ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { 38ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const {
38 if (!updatable) 39 if (!updatable)
39 return MakeResult<VirtualFile>(file); 40 return MakeResult<VirtualFile>(file);
40 41
@@ -43,7 +44,8 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
43 patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); 44 patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
44} 45}
45 46
46ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { 47ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
48 ContentRecordType type) const {
47 std::shared_ptr<NCA> res; 49 std::shared_ptr<NCA> res;
48 50
49 switch (storage) { 51 switch (storage) {
@@ -51,13 +53,17 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte
51 res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type); 53 res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
52 break; 54 break;
53 case StorageId::NandSystem: 55 case StorageId::NandSystem:
54 res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type); 56 res =
57 Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry(
58 title_id, type);
55 break; 59 break;
56 case StorageId::NandUser: 60 case StorageId::NandUser:
57 res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type); 61 res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry(
62 title_id, type);
58 break; 63 break;
59 case StorageId::SdCard: 64 case StorageId::SdCard:
60 res = Service::FileSystem::GetSDMCContents()->GetEntry(title_id, type); 65 res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry(
66 title_id, type);
61 break; 67 break;
62 default: 68 default:
63 UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage)); 69 UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index 7724c0b23..da63a313a 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -33,8 +33,8 @@ public:
33 ~RomFSFactory(); 33 ~RomFSFactory();
34 34
35 void SetPackedUpdate(VirtualFile update_raw); 35 void SetPackedUpdate(VirtualFile update_raw);
36 ResultVal<VirtualFile> OpenCurrentProcess(); 36 ResultVal<VirtualFile> OpenCurrentProcess() const;
37 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); 37 ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const;
38 38
39private: 39private:
40 VirtualFile file; 40 VirtualFile file;
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 7974b031d..f77cc02ac 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -15,22 +15,8 @@ namespace FileSys {
15 15
16constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; 16constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
17 17
18std::string SaveDataDescriptor::DebugInfo() const { 18namespace {
19 return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, " 19void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
20 "rank={}, index={}]",
21 static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
22 static_cast<u8>(rank), index);
23}
24
25SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
26 // Delete all temporary storages
27 // On hardware, it is expected that temporary storage be empty at first use.
28 dir->DeleteSubdirectoryRecursive("temp");
29}
30
31SaveDataFactory::~SaveDataFactory() = default;
32
33ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
34 if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { 20 if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
35 if (meta.zero_1 != 0) { 21 if (meta.zero_1 != 0) {
36 LOG_WARNING(Service_FS, 22 LOG_WARNING(Service_FS,
@@ -65,23 +51,51 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDat
65 "non-zero ({:016X}{:016X})", 51 "non-zero ({:016X}{:016X})",
66 meta.user_id[1], meta.user_id[0]); 52 meta.user_id[1], meta.user_id[0]);
67 } 53 }
54}
55} // Anonymous namespace
68 56
69 std::string save_directory = 57std::string SaveDataDescriptor::DebugInfo() const {
70 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); 58 return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, "
59 "save_id={:016X}, "
60 "rank={}, index={}]",
61 static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
62 static_cast<u8>(rank), index);
63}
71 64
72 // TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods. 65SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
73 // But, user_ids don't match so this works for now. 66 // Delete all temporary storages
67 // On hardware, it is expected that temporary storage be empty at first use.
68 dir->DeleteSubdirectoryRecursive("temp");
69}
74 70
75 auto out = dir->GetDirectoryRelative(save_directory); 71SaveDataFactory::~SaveDataFactory() = default;
72
73ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
74 const SaveDataDescriptor& meta) const {
75 PrintSaveDataDescriptorWarnings(meta);
76
77 const auto save_directory =
78 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
79
80 auto out = dir->CreateDirectoryRelative(save_directory);
76 81
82 // Return an error if the save data doesn't actually exist.
77 if (out == nullptr) { 83 if (out == nullptr) {
78 // TODO(bunnei): This is a work-around to always create a save data directory if it does not 84 // TODO(DarkLordZach): Find out correct error code.
79 // already exist. This is a hack, as we do not understand yet how this works on hardware. 85 return ResultCode(-1);
80 // Without a save data directory, many games will assert on boot. This should not have any
81 // bad side-effects.
82 out = dir->CreateDirectoryRelative(save_directory);
83 } 86 }
84 87
88 return MakeResult<VirtualDir>(std::move(out));
89}
90
91ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
92 const SaveDataDescriptor& meta) const {
93
94 const auto save_directory =
95 GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
96
97 auto out = dir->GetDirectoryRelative(save_directory);
98
85 // Return an error if the save data doesn't actually exist. 99 // Return an error if the save data doesn't actually exist.
86 if (out == nullptr) { 100 if (out == nullptr) {
87 // TODO(Subv): Find out correct error code. 101 // TODO(Subv): Find out correct error code.
@@ -152,7 +166,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
152} 166}
153 167
154void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, 168void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
155 SaveDataSize new_value) { 169 SaveDataSize new_value) const {
156 const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0); 170 const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
157 const auto dir = GetOrCreateDirectoryRelative(this->dir, path); 171 const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
158 172
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index b73654571..991e57aa1 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -64,7 +64,8 @@ public:
64 explicit SaveDataFactory(VirtualDir dir); 64 explicit SaveDataFactory(VirtualDir dir);
65 ~SaveDataFactory(); 65 ~SaveDataFactory();
66 66
67 ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta); 67 ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
68 ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
68 69
69 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; 70 VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
70 71
@@ -73,7 +74,8 @@ public:
73 u128 user_id, u64 save_id); 74 u128 user_id, u64 save_id);
74 75
75 SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; 76 SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
76 void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value); 77 void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
78 SaveDataSize new_value) const;
77 79
78private: 80private:
79 VirtualDir dir; 81 VirtualDir dir;
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index bd3a57058..5113a1ca6 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -6,6 +6,7 @@
6#include "core/file_sys/registered_cache.h" 6#include "core/file_sys/registered_cache.h"
7#include "core/file_sys/sdmc_factory.h" 7#include "core/file_sys/sdmc_factory.h"
8#include "core/file_sys/xts_archive.h" 8#include "core/file_sys/xts_archive.h"
9#include "core/settings.h"
9 10
10namespace FileSys { 11namespace FileSys {
11 12
@@ -14,16 +15,38 @@ SDMCFactory::SDMCFactory(VirtualDir dir_)
14 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), 15 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
15 [](const VirtualFile& file, const NcaID& id) { 16 [](const VirtualFile& file, const NcaID& id) {
16 return NAX{file, id}.GetDecrypted(); 17 return NAX{file, id}.GetDecrypted();
17 })) {} 18 })),
19 placeholder(std::make_unique<PlaceholderCache>(
20 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {}
18 21
19SDMCFactory::~SDMCFactory() = default; 22SDMCFactory::~SDMCFactory() = default;
20 23
21ResultVal<VirtualDir> SDMCFactory::Open() { 24ResultVal<VirtualDir> SDMCFactory::Open() const {
22 return MakeResult<VirtualDir>(dir); 25 return MakeResult<VirtualDir>(dir);
23} 26}
24 27
28VirtualDir SDMCFactory::GetSDMCContentDirectory() const {
29 return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents");
30}
31
25RegisteredCache* SDMCFactory::GetSDMCContents() const { 32RegisteredCache* SDMCFactory::GetSDMCContents() const {
26 return contents.get(); 33 return contents.get();
27} 34}
28 35
36PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const {
37 return placeholder.get();
38}
39
40VirtualDir SDMCFactory::GetImageDirectory() const {
41 return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album");
42}
43
44u64 SDMCFactory::GetSDMCFreeSpace() const {
45 return GetSDMCTotalSpace() - dir->GetSize();
46}
47
48u64 SDMCFactory::GetSDMCTotalSpace() const {
49 return static_cast<u64>(Settings::values.sdmc_size);
50}
51
29} // namespace FileSys 52} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index 42794ba5b..42dc4e08a 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -11,6 +11,7 @@
11namespace FileSys { 11namespace FileSys {
12 12
13class RegisteredCache; 13class RegisteredCache;
14class PlaceholderCache;
14 15
15/// File system interface to the SDCard archive 16/// File system interface to the SDCard archive
16class SDMCFactory { 17class SDMCFactory {
@@ -18,13 +19,23 @@ public:
18 explicit SDMCFactory(VirtualDir dir); 19 explicit SDMCFactory(VirtualDir dir);
19 ~SDMCFactory(); 20 ~SDMCFactory();
20 21
21 ResultVal<VirtualDir> Open(); 22 ResultVal<VirtualDir> Open() const;
23
24 VirtualDir GetSDMCContentDirectory() const;
25
22 RegisteredCache* GetSDMCContents() const; 26 RegisteredCache* GetSDMCContents() const;
27 PlaceholderCache* GetSDMCPlaceholder() const;
28
29 VirtualDir GetImageDirectory() const;
30
31 u64 GetSDMCFreeSpace() const;
32 u64 GetSDMCTotalSpace() const;
23 33
24private: 34private:
25 VirtualDir dir; 35 VirtualDir dir;
26 36
27 std::unique_ptr<RegisteredCache> contents; 37 std::unique_ptr<RegisteredCache> contents;
38 std::unique_ptr<PlaceholderCache> placeholder;
28}; 39};
29 40
30} // namespace FileSys 41} // namespace FileSys
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 730221fd6..ef3084681 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -248,7 +248,8 @@ void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
248 248
249void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { 249void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
250 for (const auto& outer_file : files) { 250 for (const auto& outer_file : files) {
251 if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") { 251 if (outer_file->GetName().size() < 9 ||
252 outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
252 continue; 253 continue;
253 } 254 }
254 255
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index aa2c83937..6c594dcaf 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1143,13 +1143,21 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
1143 1143
1144void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) { 1144void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
1145 IPC::RequestParser rp{ctx}; 1145 IPC::RequestParser rp{ctx};
1146 u128 uid = rp.PopRaw<u128>(); // What does this do? 1146 u128 user_id = rp.PopRaw<u128>();
1147 LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]); 1147
1148 LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
1149
1150 FileSys::SaveDataDescriptor descriptor{};
1151 descriptor.title_id = Core::CurrentProcess()->GetTitleID();
1152 descriptor.user_id = user_id;
1153 descriptor.type = FileSys::SaveDataType::SaveData;
1154 const auto res = system.GetFileSystemController().CreateSaveData(
1155 FileSys::SaveDataSpaceId::NandUser, descriptor);
1148 1156
1149 IPC::ResponseBuilder rb{ctx, 4}; 1157 IPC::ResponseBuilder rb{ctx, 4};
1150 rb.Push(RESULT_SUCCESS); 1158 rb.Push(res.Code());
1151 rb.Push<u64>(0); 1159 rb.Push<u64>(0);
1152} // namespace Service::AM 1160}
1153 1161
1154void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) { 1162void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
1155 // Takes an input u32 Result, no output. 1163 // Takes an input u32 Result, no output.
@@ -1261,8 +1269,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
1261 "new_journal={:016X}", 1269 "new_journal={:016X}",
1262 static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size); 1270 static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
1263 1271
1264 const auto title_id = system.CurrentProcess()->GetTitleID(); 1272 system.GetFileSystemController().WriteSaveDataSize(
1265 FileSystem::WriteSaveDataSize(type, title_id, user_id, {new_normal_size, new_journal_size}); 1273 type, system.CurrentProcess()->GetTitleID(), user_id, {new_normal_size, new_journal_size});
1266 1274
1267 IPC::ResponseBuilder rb{ctx, 4}; 1275 IPC::ResponseBuilder rb{ctx, 4};
1268 rb.Push(RESULT_SUCCESS); 1276 rb.Push(RESULT_SUCCESS);
@@ -1281,8 +1289,8 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
1281 LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type), 1289 LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
1282 user_id[1], user_id[0]); 1290 user_id[1], user_id[0]);
1283 1291
1284 const auto title_id = system.CurrentProcess()->GetTitleID(); 1292 const auto size = system.GetFileSystemController().ReadSaveDataSize(
1285 const auto size = FileSystem::ReadSaveDataSize(type, title_id, user_id); 1293 type, system.CurrentProcess()->GetTitleID(), user_id);
1286 1294
1287 IPC::ResponseBuilder rb{ctx, 6}; 1295 IPC::ResponseBuilder rb{ctx, 6};
1288 rb.Push(RESULT_SUCCESS); 1296 rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 9e006cd9d..0e0d10858 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -9,6 +9,10 @@
9#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
10 10
11namespace Service { 11namespace Service {
12namespace FileSystem {
13class FileSystemController;
14}
15
12namespace NVFlinger { 16namespace NVFlinger {
13class NVFlinger; 17class NVFlinger;
14} 18}
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 22c05419d..99a65e7b5 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -9,6 +9,10 @@
9#include "core/hle/service/service.h" 9#include "core/hle/service/service.h"
10 10
11namespace Service { 11namespace Service {
12namespace FileSystem {
13class FileSystemController;
14}
15
12namespace NVFlinger { 16namespace NVFlinger {
13class NVFlinger; 17class NVFlinger;
14} 18}
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 8ce110dd1..14cd0e322 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -8,6 +8,7 @@
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "core/core.h" 9#include "core/core.h"
10#include "core/file_sys/bis_factory.h" 10#include "core/file_sys/bis_factory.h"
11#include "core/file_sys/card_image.h"
11#include "core/file_sys/control_metadata.h" 12#include "core/file_sys/control_metadata.h"
12#include "core/file_sys/errors.h" 13#include "core/file_sys/errors.h"
13#include "core/file_sys/mode.h" 14#include "core/file_sys/mode.h"
@@ -25,14 +26,10 @@
25#include "core/hle/service/filesystem/fsp_pr.h" 26#include "core/hle/service/filesystem/fsp_pr.h"
26#include "core/hle/service/filesystem/fsp_srv.h" 27#include "core/hle/service/filesystem/fsp_srv.h"
27#include "core/loader/loader.h" 28#include "core/loader/loader.h"
29#include "core/settings.h"
28 30
29namespace Service::FileSystem { 31namespace Service::FileSystem {
30 32
31// Size of emulated sd card free space, reported in bytes.
32// Just using 32GB because thats reasonable
33// TODO(DarkLordZach): Eventually make this configurable in settings.
34constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
35
36// A default size for normal/journal save data size if application control metadata cannot be found. 33// A default size for normal/journal save data size if application control metadata cannot be found.
37// This should be large enough to satisfy even the most extreme requirements (~4.2GB) 34// This should be large enough to satisfy even the most extreme requirements (~4.2GB)
38constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000; 35constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
@@ -226,13 +223,6 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
226 return MakeResult(dir); 223 return MakeResult(dir);
227} 224}
228 225
229u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
230 if (backing->IsWritable())
231 return EMULATED_SD_REPORTED_SIZE;
232
233 return 0;
234}
235
236ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType( 226ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
237 const std::string& path_) const { 227 const std::string& path_) const {
238 std::string path(FileUtil::SanitizePath(path_)); 228 std::string path(FileUtil::SanitizePath(path_));
@@ -251,44 +241,39 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
251 return FileSys::ERROR_PATH_NOT_FOUND; 241 return FileSys::ERROR_PATH_NOT_FOUND;
252} 242}
253 243
254/** 244FileSystemController::FileSystemController() = default;
255 * Map of registered file systems, identified by type. Once an file system is registered here, it 245
256 * is never removed until UnregisterFileSystems is called. 246FileSystemController::~FileSystemController() = default;
257 */
258static std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
259static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
260static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
261static std::unique_ptr<FileSys::BISFactory> bis_factory;
262 247
263ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) { 248ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
264 ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second RomFS");
265 romfs_factory = std::move(factory); 249 romfs_factory = std::move(factory);
266 LOG_DEBUG(Service_FS, "Registered RomFS"); 250 LOG_DEBUG(Service_FS, "Registered RomFS");
267 return RESULT_SUCCESS; 251 return RESULT_SUCCESS;
268} 252}
269 253
270ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) { 254ResultCode FileSystemController::RegisterSaveData(
271 ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second save data"); 255 std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
256 ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data");
272 save_data_factory = std::move(factory); 257 save_data_factory = std::move(factory);
273 LOG_DEBUG(Service_FS, "Registered save data"); 258 LOG_DEBUG(Service_FS, "Registered save data");
274 return RESULT_SUCCESS; 259 return RESULT_SUCCESS;
275} 260}
276 261
277ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) { 262ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
278 ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC"); 263 ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
279 sdmc_factory = std::move(factory); 264 sdmc_factory = std::move(factory);
280 LOG_DEBUG(Service_FS, "Registered SDMC"); 265 LOG_DEBUG(Service_FS, "Registered SDMC");
281 return RESULT_SUCCESS; 266 return RESULT_SUCCESS;
282} 267}
283 268
284ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) { 269ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
285 ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS"); 270 ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
286 bis_factory = std::move(factory); 271 bis_factory = std::move(factory);
287 LOG_DEBUG(Service_FS, "Registered BIS"); 272 LOG_DEBUG(Service_FS, "Registered BIS");
288 return RESULT_SUCCESS; 273 return RESULT_SUCCESS;
289} 274}
290 275
291void SetPackedUpdate(FileSys::VirtualFile update_raw) { 276void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) {
292 LOG_TRACE(Service_FS, "Setting packed update for romfs"); 277 LOG_TRACE(Service_FS, "Setting packed update for romfs");
293 278
294 if (romfs_factory == nullptr) 279 if (romfs_factory == nullptr)
@@ -297,7 +282,7 @@ void SetPackedUpdate(FileSys::VirtualFile update_raw) {
297 romfs_factory->SetPackedUpdate(std::move(update_raw)); 282 romfs_factory->SetPackedUpdate(std::move(update_raw));
298} 283}
299 284
300ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() { 285ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() const {
301 LOG_TRACE(Service_FS, "Opening RomFS for current process"); 286 LOG_TRACE(Service_FS, "Opening RomFS for current process");
302 287
303 if (romfs_factory == nullptr) { 288 if (romfs_factory == nullptr) {
@@ -308,8 +293,8 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
308 return romfs_factory->OpenCurrentProcess(); 293 return romfs_factory->OpenCurrentProcess();
309} 294}
310 295
311ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id, 296ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
312 FileSys::ContentRecordType type) { 297 u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const {
313 LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}", 298 LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
314 title_id, static_cast<u8>(storage_id), static_cast<u8>(type)); 299 title_id, static_cast<u8>(storage_id), static_cast<u8>(type));
315 300
@@ -321,8 +306,20 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
321 return romfs_factory->Open(title_id, storage_id, type); 306 return romfs_factory->Open(title_id, storage_id, type);
322} 307}
323 308
324ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space, 309ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
325 const FileSys::SaveDataDescriptor& descriptor) { 310 FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const {
311 LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}",
312 static_cast<u8>(space), save_struct.DebugInfo());
313
314 if (save_data_factory == nullptr) {
315 return FileSys::ERROR_ENTITY_NOT_FOUND;
316 }
317
318 return save_data_factory->Create(space, save_struct);
319}
320
321ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
322 FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const {
326 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}", 323 LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
327 static_cast<u8>(space), descriptor.DebugInfo()); 324 static_cast<u8>(space), descriptor.DebugInfo());
328 325
@@ -333,7 +330,8 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
333 return save_data_factory->Open(space, descriptor); 330 return save_data_factory->Open(space, descriptor);
334} 331}
335 332
336ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) { 333ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace(
334 FileSys::SaveDataSpaceId space) const {
337 LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space)); 335 LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
338 336
339 if (save_data_factory == nullptr) { 337 if (save_data_factory == nullptr) {
@@ -343,7 +341,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space)
343 return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space)); 341 return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
344} 342}
345 343
346ResultVal<FileSys::VirtualDir> OpenSDMC() { 344ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() const {
347 LOG_TRACE(Service_FS, "Opening SDMC"); 345 LOG_TRACE(Service_FS, "Opening SDMC");
348 346
349 if (sdmc_factory == nullptr) { 347 if (sdmc_factory == nullptr) {
@@ -353,7 +351,92 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
353 return sdmc_factory->Open(); 351 return sdmc_factory->Open();
354} 352}
355 353
356FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) { 354ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition(
355 FileSys::BisPartitionId id) const {
356 LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", static_cast<u32>(id));
357
358 if (bis_factory == nullptr) {
359 return FileSys::ERROR_ENTITY_NOT_FOUND;
360 }
361
362 auto part = bis_factory->OpenPartition(id);
363 if (part == nullptr) {
364 return FileSys::ERROR_INVALID_ARGUMENT;
365 }
366
367 return MakeResult<FileSys::VirtualDir>(std::move(part));
368}
369
370ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
371 FileSys::BisPartitionId id) const {
372 LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", static_cast<u32>(id));
373
374 if (bis_factory == nullptr) {
375 return FileSys::ERROR_ENTITY_NOT_FOUND;
376 }
377
378 auto part = bis_factory->OpenPartitionStorage(id);
379 if (part == nullptr) {
380 return FileSys::ERROR_INVALID_ARGUMENT;
381 }
382
383 return MakeResult<FileSys::VirtualFile>(std::move(part));
384}
385
386u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const {
387 switch (id) {
388 case FileSys::StorageId::None:
389 case FileSys::StorageId::GameCard:
390 return 0;
391 case FileSys::StorageId::SdCard:
392 if (sdmc_factory == nullptr)
393 return 0;
394 return sdmc_factory->GetSDMCFreeSpace();
395 case FileSys::StorageId::Host:
396 if (bis_factory == nullptr)
397 return 0;
398 return bis_factory->GetSystemNANDFreeSpace() + bis_factory->GetUserNANDFreeSpace();
399 case FileSys::StorageId::NandSystem:
400 if (bis_factory == nullptr)
401 return 0;
402 return bis_factory->GetSystemNANDFreeSpace();
403 case FileSys::StorageId::NandUser:
404 if (bis_factory == nullptr)
405 return 0;
406 return bis_factory->GetUserNANDFreeSpace();
407 }
408
409 return 0;
410}
411
412u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const {
413 switch (id) {
414 case FileSys::StorageId::None:
415 case FileSys::StorageId::GameCard:
416 return 0;
417 case FileSys::StorageId::SdCard:
418 if (sdmc_factory == nullptr)
419 return 0;
420 return sdmc_factory->GetSDMCTotalSpace();
421 case FileSys::StorageId::Host:
422 if (bis_factory == nullptr)
423 return 0;
424 return bis_factory->GetFullNANDTotalSpace();
425 case FileSys::StorageId::NandSystem:
426 if (bis_factory == nullptr)
427 return 0;
428 return bis_factory->GetSystemNANDTotalSpace();
429 case FileSys::StorageId::NandUser:
430 if (bis_factory == nullptr)
431 return 0;
432 return bis_factory->GetUserNANDTotalSpace();
433 }
434
435 return 0;
436}
437
438FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type,
439 u64 title_id, u128 user_id) const {
357 if (save_data_factory == nullptr) { 440 if (save_data_factory == nullptr) {
358 return {0, 0}; 441 return {0, 0};
359 } 442 }
@@ -385,13 +468,32 @@ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
385 return value; 468 return value;
386} 469}
387 470
388void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id, 471void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
389 FileSys::SaveDataSize new_value) { 472 FileSys::SaveDataSize new_value) const {
390 if (save_data_factory != nullptr) 473 if (save_data_factory != nullptr)
391 save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value); 474 save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
392} 475}
393 476
394FileSys::RegisteredCache* GetSystemNANDContents() { 477void FileSystemController::SetGameCard(FileSys::VirtualFile file) {
478 gamecard = std::make_unique<FileSys::XCI>(file);
479 const auto dir = gamecard->ConcatenatedPseudoDirectory();
480 gamecard_registered = std::make_unique<FileSys::RegisteredCache>(dir);
481 gamecard_placeholder = std::make_unique<FileSys::PlaceholderCache>(dir);
482}
483
484FileSys::XCI* FileSystemController::GetGameCard() const {
485 return gamecard.get();
486}
487
488FileSys::RegisteredCache* FileSystemController::GetGameCardContents() const {
489 return gamecard_registered.get();
490}
491
492FileSys::PlaceholderCache* FileSystemController::GetGameCardPlaceholder() const {
493 return gamecard_placeholder.get();
494}
495
496FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() const {
395 LOG_TRACE(Service_FS, "Opening System NAND Contents"); 497 LOG_TRACE(Service_FS, "Opening System NAND Contents");
396 498
397 if (bis_factory == nullptr) 499 if (bis_factory == nullptr)
@@ -400,7 +502,7 @@ FileSys::RegisteredCache* GetSystemNANDContents() {
400 return bis_factory->GetSystemNANDContents(); 502 return bis_factory->GetSystemNANDContents();
401} 503}
402 504
403FileSys::RegisteredCache* GetUserNANDContents() { 505FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() const {
404 LOG_TRACE(Service_FS, "Opening User NAND Contents"); 506 LOG_TRACE(Service_FS, "Opening User NAND Contents");
405 507
406 if (bis_factory == nullptr) 508 if (bis_factory == nullptr)
@@ -409,7 +511,7 @@ FileSys::RegisteredCache* GetUserNANDContents() {
409 return bis_factory->GetUserNANDContents(); 511 return bis_factory->GetUserNANDContents();
410} 512}
411 513
412FileSys::RegisteredCache* GetSDMCContents() { 514FileSys::RegisteredCache* FileSystemController::GetSDMCContents() const {
413 LOG_TRACE(Service_FS, "Opening SDMC Contents"); 515 LOG_TRACE(Service_FS, "Opening SDMC Contents");
414 516
415 if (sdmc_factory == nullptr) 517 if (sdmc_factory == nullptr)
@@ -418,7 +520,143 @@ FileSys::RegisteredCache* GetSDMCContents() {
418 return sdmc_factory->GetSDMCContents(); 520 return sdmc_factory->GetSDMCContents();
419} 521}
420 522
421FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) { 523FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() const {
524 LOG_TRACE(Service_FS, "Opening System NAND Placeholder");
525
526 if (bis_factory == nullptr)
527 return nullptr;
528
529 return bis_factory->GetSystemNANDPlaceholder();
530}
531
532FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() const {
533 LOG_TRACE(Service_FS, "Opening User NAND Placeholder");
534
535 if (bis_factory == nullptr)
536 return nullptr;
537
538 return bis_factory->GetUserNANDPlaceholder();
539}
540
541FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() const {
542 LOG_TRACE(Service_FS, "Opening SDMC Placeholder");
543
544 if (sdmc_factory == nullptr)
545 return nullptr;
546
547 return sdmc_factory->GetSDMCPlaceholder();
548}
549
550FileSys::RegisteredCache* FileSystemController::GetRegisteredCacheForStorage(
551 FileSys::StorageId id) const {
552 switch (id) {
553 case FileSys::StorageId::None:
554 case FileSys::StorageId::Host:
555 UNIMPLEMENTED();
556 return nullptr;
557 case FileSys::StorageId::GameCard:
558 return GetGameCardContents();
559 case FileSys::StorageId::NandSystem:
560 return GetSystemNANDContents();
561 case FileSys::StorageId::NandUser:
562 return GetUserNANDContents();
563 case FileSys::StorageId::SdCard:
564 return GetSDMCContents();
565 }
566
567 return nullptr;
568}
569
570FileSys::PlaceholderCache* FileSystemController::GetPlaceholderCacheForStorage(
571 FileSys::StorageId id) const {
572 switch (id) {
573 case FileSys::StorageId::None:
574 case FileSys::StorageId::Host:
575 UNIMPLEMENTED();
576 return nullptr;
577 case FileSys::StorageId::GameCard:
578 return GetGameCardPlaceholder();
579 case FileSys::StorageId::NandSystem:
580 return GetSystemNANDPlaceholder();
581 case FileSys::StorageId::NandUser:
582 return GetUserNANDPlaceholder();
583 case FileSys::StorageId::SdCard:
584 return GetSDMCPlaceholder();
585 }
586
587 return nullptr;
588}
589
590FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() const {
591 LOG_TRACE(Service_FS, "Opening system NAND content directory");
592
593 if (bis_factory == nullptr)
594 return nullptr;
595
596 return bis_factory->GetSystemNANDContentDirectory();
597}
598
599FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() const {
600 LOG_TRACE(Service_FS, "Opening user NAND content directory");
601
602 if (bis_factory == nullptr)
603 return nullptr;
604
605 return bis_factory->GetUserNANDContentDirectory();
606}
607
608FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() const {
609 LOG_TRACE(Service_FS, "Opening SDMC content directory");
610
611 if (sdmc_factory == nullptr)
612 return nullptr;
613
614 return sdmc_factory->GetSDMCContentDirectory();
615}
616
617FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() const {
618 LOG_TRACE(Service_FS, "Opening NAND image directory");
619
620 if (bis_factory == nullptr)
621 return nullptr;
622
623 return bis_factory->GetImageDirectory();
624}
625
626FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() const {
627 LOG_TRACE(Service_FS, "Opening SDMC image directory");
628
629 if (sdmc_factory == nullptr)
630 return nullptr;
631
632 return sdmc_factory->GetImageDirectory();
633}
634
635FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId id) const {
636 switch (id) {
637 case ContentStorageId::System:
638 return GetSystemNANDContentDirectory();
639 case ContentStorageId::User:
640 return GetUserNANDContentDirectory();
641 case ContentStorageId::SdCard:
642 return GetSDMCContentDirectory();
643 }
644
645 return nullptr;
646}
647
648FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id) const {
649 switch (id) {
650 case ImageDirectoryId::NAND:
651 return GetNANDImageDirectory();
652 case ImageDirectoryId::SdCard:
653 return GetSDMCImageDirectory();
654 }
655
656 return nullptr;
657}
658
659FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id) const {
422 LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id); 660 LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);
423 661
424 if (bis_factory == nullptr) 662 if (bis_factory == nullptr)
@@ -427,7 +665,7 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
427 return bis_factory->GetModificationLoadRoot(title_id); 665 return bis_factory->GetModificationLoadRoot(title_id);
428} 666}
429 667
430FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) { 668FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const {
431 LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id); 669 LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
432 670
433 if (bis_factory == nullptr) 671 if (bis_factory == nullptr)
@@ -436,7 +674,7 @@ FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
436 return bis_factory->GetModificationDumpRoot(title_id); 674 return bis_factory->GetModificationDumpRoot(title_id);
437} 675}
438 676
439void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) { 677void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
440 if (overwrite) { 678 if (overwrite) {
441 bis_factory = nullptr; 679 bis_factory = nullptr;
442 save_data_factory = nullptr; 680 save_data_factory = nullptr;
@@ -473,11 +711,10 @@ void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
473} 711}
474 712
475void InstallInterfaces(Core::System& system) { 713void InstallInterfaces(Core::System& system) {
476 romfs_factory = nullptr;
477 CreateFactories(*system.GetFilesystem(), false);
478 std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager()); 714 std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager());
479 std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager()); 715 std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager());
480 std::make_shared<FSP_SRV>(system.GetReporter())->InstallAsService(system.ServiceManager()); 716 std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter())
717 ->InstallAsService(system.ServiceManager());
481} 718}
482 719
483} // namespace Service::FileSystem 720} // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 3849dd89e..3e0c03ec0 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -14,10 +14,13 @@ namespace FileSys {
14class BISFactory; 14class BISFactory;
15class RegisteredCache; 15class RegisteredCache;
16class RegisteredCacheUnion; 16class RegisteredCacheUnion;
17class PlaceholderCache;
17class RomFSFactory; 18class RomFSFactory;
18class SaveDataFactory; 19class SaveDataFactory;
19class SDMCFactory; 20class SDMCFactory;
21class XCI;
20 22
23enum class BisPartitionId : u32;
21enum class ContentRecordType : u8; 24enum class ContentRecordType : u8;
22enum class Mode : u32; 25enum class Mode : u32;
23enum class SaveDataSpaceId : u8; 26enum class SaveDataSpaceId : u8;
@@ -36,34 +39,91 @@ class ServiceManager;
36 39
37namespace FileSystem { 40namespace FileSystem {
38 41
39ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory); 42enum class ContentStorageId : u32 {
40ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory); 43 System,
41ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory); 44 User,
42ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory); 45 SdCard,
43 46};
44void SetPackedUpdate(FileSys::VirtualFile update_raw);
45ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
46ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
47 FileSys::ContentRecordType type);
48ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
49 const FileSys::SaveDataDescriptor& descriptor);
50ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
51ResultVal<FileSys::VirtualDir> OpenSDMC();
52
53FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
54void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
55 FileSys::SaveDataSize new_value);
56 47
57FileSys::RegisteredCache* GetSystemNANDContents(); 48enum class ImageDirectoryId : u32 {
58FileSys::RegisteredCache* GetUserNANDContents(); 49 NAND,
59FileSys::RegisteredCache* GetSDMCContents(); 50 SdCard,
51};
60 52
61FileSys::VirtualDir GetModificationLoadRoot(u64 title_id); 53class FileSystemController {
62FileSys::VirtualDir GetModificationDumpRoot(u64 title_id); 54public:
55 FileSystemController();
56 ~FileSystemController();
57
58 ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
59 ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
60 ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
61 ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
62
63 void SetPackedUpdate(FileSys::VirtualFile update_raw);
64 ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const;
65 ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
66 FileSys::ContentRecordType type) const;
67 ResultVal<FileSys::VirtualDir> CreateSaveData(
68 FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
69 ResultVal<FileSys::VirtualDir> OpenSaveData(
70 FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
71 ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const;
72 ResultVal<FileSys::VirtualDir> OpenSDMC() const;
73 ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const;
74 ResultVal<FileSys::VirtualFile> OpenBISPartitionStorage(FileSys::BisPartitionId id) const;
75
76 u64 GetFreeSpaceSize(FileSys::StorageId id) const;
77 u64 GetTotalSpaceSize(FileSys::StorageId id) const;
78
79 FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
80 u128 user_id) const;
81 void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
82 FileSys::SaveDataSize new_value) const;
83
84 void SetGameCard(FileSys::VirtualFile file);
85 FileSys::XCI* GetGameCard() const;
86
87 FileSys::RegisteredCache* GetSystemNANDContents() const;
88 FileSys::RegisteredCache* GetUserNANDContents() const;
89 FileSys::RegisteredCache* GetSDMCContents() const;
90 FileSys::RegisteredCache* GetGameCardContents() const;
91
92 FileSys::PlaceholderCache* GetSystemNANDPlaceholder() const;
93 FileSys::PlaceholderCache* GetUserNANDPlaceholder() const;
94 FileSys::PlaceholderCache* GetSDMCPlaceholder() const;
95 FileSys::PlaceholderCache* GetGameCardPlaceholder() const;
96
97 FileSys::RegisteredCache* GetRegisteredCacheForStorage(FileSys::StorageId id) const;
98 FileSys::PlaceholderCache* GetPlaceholderCacheForStorage(FileSys::StorageId id) const;
99
100 FileSys::VirtualDir GetSystemNANDContentDirectory() const;
101 FileSys::VirtualDir GetUserNANDContentDirectory() const;
102 FileSys::VirtualDir GetSDMCContentDirectory() const;
103
104 FileSys::VirtualDir GetNANDImageDirectory() const;
105 FileSys::VirtualDir GetSDMCImageDirectory() const;
106
107 FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const;
108 FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const;
109
110 FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const;
111 FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const;
112
113 // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
114 // above is called.
115 void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
63 116
64// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function 117private:
65// above is called. 118 std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
66void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true); 119 std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
120 std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
121 std::unique_ptr<FileSys::BISFactory> bis_factory;
122
123 std::unique_ptr<FileSys::XCI> gamecard;
124 std::unique_ptr<FileSys::RegisteredCache> gamecard_registered;
125 std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder;
126};
67 127
68void InstallInterfaces(Core::System& system); 128void InstallInterfaces(Core::System& system);
69 129
@@ -160,12 +220,6 @@ public:
160 ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path); 220 ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path);
161 221
162 /** 222 /**
163 * Get the free space
164 * @return The number of free bytes in the archive
165 */
166 u64 GetFreeSpaceSize() const;
167
168 /**
169 * Get the type of the specified path 223 * Get the type of the specified path
170 * @return The type of the specified path or error code 224 * @return The type of the specified path or error code
171 */ 225 */
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index d3cd46a9b..eb982ad49 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -19,6 +19,7 @@
19#include "core/file_sys/mode.h" 19#include "core/file_sys/mode.h"
20#include "core/file_sys/nca_metadata.h" 20#include "core/file_sys/nca_metadata.h"
21#include "core/file_sys/patch_manager.h" 21#include "core/file_sys/patch_manager.h"
22#include "core/file_sys/romfs_factory.h"
22#include "core/file_sys/savedata_factory.h" 23#include "core/file_sys/savedata_factory.h"
23#include "core/file_sys/system_archive/system_archive.h" 24#include "core/file_sys/system_archive/system_archive.h"
24#include "core/file_sys/vfs.h" 25#include "core/file_sys/vfs.h"
@@ -30,6 +31,18 @@
30 31
31namespace Service::FileSystem { 32namespace Service::FileSystem {
32 33
34struct SizeGetter {
35 std::function<u64()> get_free_size;
36 std::function<u64()> get_total_size;
37
38 static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) {
39 return {
40 [&fsc, id] { return fsc.GetFreeSpaceSize(id); },
41 [&fsc, id] { return fsc.GetTotalSpaceSize(id); },
42 };
43 }
44};
45
33enum class FileSystemType : u8 { 46enum class FileSystemType : u8 {
34 Invalid0 = 0, 47 Invalid0 = 0,
35 Invalid1 = 1, 48 Invalid1 = 1,
@@ -289,8 +302,8 @@ private:
289 302
290class IFileSystem final : public ServiceFramework<IFileSystem> { 303class IFileSystem final : public ServiceFramework<IFileSystem> {
291public: 304public:
292 explicit IFileSystem(FileSys::VirtualDir backend) 305 explicit IFileSystem(FileSys::VirtualDir backend, SizeGetter size)
293 : ServiceFramework("IFileSystem"), backend(std::move(backend)) { 306 : ServiceFramework("IFileSystem"), backend(std::move(backend)), size(std::move(size)) {
294 static const FunctionInfo functions[] = { 307 static const FunctionInfo functions[] = {
295 {0, &IFileSystem::CreateFile, "CreateFile"}, 308 {0, &IFileSystem::CreateFile, "CreateFile"},
296 {1, &IFileSystem::DeleteFile, "DeleteFile"}, 309 {1, &IFileSystem::DeleteFile, "DeleteFile"},
@@ -467,14 +480,31 @@ public:
467 rb.Push(RESULT_SUCCESS); 480 rb.Push(RESULT_SUCCESS);
468 } 481 }
469 482
483 void GetFreeSpaceSize(Kernel::HLERequestContext& ctx) {
484 LOG_DEBUG(Service_FS, "called");
485
486 IPC::ResponseBuilder rb{ctx, 4};
487 rb.Push(RESULT_SUCCESS);
488 rb.Push(size.get_free_size());
489 }
490
491 void GetTotalSpaceSize(Kernel::HLERequestContext& ctx) {
492 LOG_DEBUG(Service_FS, "called");
493
494 IPC::ResponseBuilder rb{ctx, 4};
495 rb.Push(RESULT_SUCCESS);
496 rb.Push(size.get_total_size());
497 }
498
470private: 499private:
471 VfsDirectoryServiceWrapper backend; 500 VfsDirectoryServiceWrapper backend;
501 SizeGetter size;
472}; 502};
473 503
474class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> { 504class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
475public: 505public:
476 explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space) 506 explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space, FileSystemController& fsc)
477 : ServiceFramework("ISaveDataInfoReader") { 507 : ServiceFramework("ISaveDataInfoReader"), fsc(fsc) {
478 static const FunctionInfo functions[] = { 508 static const FunctionInfo functions[] = {
479 {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"}, 509 {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
480 }; 510 };
@@ -520,8 +550,13 @@ private:
520 } 550 }
521 551
522 void FindAllSaves(FileSys::SaveDataSpaceId space) { 552 void FindAllSaves(FileSys::SaveDataSpaceId space) {
523 const auto save_root = OpenSaveDataSpace(space); 553 const auto save_root = fsc.OpenSaveDataSpace(space);
524 ASSERT(save_root.Succeeded()); 554
555 if (save_root.Failed() || *save_root == nullptr) {
556 LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!",
557 static_cast<u8>(space));
558 return;
559 }
525 560
526 for (const auto& type : (*save_root)->GetSubdirectories()) { 561 for (const auto& type : (*save_root)->GetSubdirectories()) {
527 if (type->GetName() == "save") { 562 if (type->GetName() == "save") {
@@ -610,11 +645,13 @@ private:
610 }; 645 };
611 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size."); 646 static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
612 647
648 FileSystemController& fsc;
613 std::vector<SaveDataInfo> info; 649 std::vector<SaveDataInfo> info;
614 u64 next_entry_index = 0; 650 u64 next_entry_index = 0;
615}; 651};
616 652
617FSP_SRV::FSP_SRV(const Core::Reporter& reporter) : ServiceFramework("fsp-srv"), reporter(reporter) { 653FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
654 : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) {
618 // clang-format off 655 // clang-format off
619 static const FunctionInfo functions[] = { 656 static const FunctionInfo functions[] = {
620 {0, nullptr, "OpenFileSystem"}, 657 {0, nullptr, "OpenFileSystem"},
@@ -754,7 +791,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
754void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) { 791void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
755 LOG_DEBUG(Service_FS, "called"); 792 LOG_DEBUG(Service_FS, "called");
756 793
757 IFileSystem filesystem(OpenSDMC().Unwrap()); 794 IFileSystem filesystem(fsc.OpenSDMC().Unwrap(),
795 SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
758 796
759 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 797 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
760 rb.Push(RESULT_SUCCESS); 798 rb.Push(RESULT_SUCCESS);
@@ -768,8 +806,10 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
768 auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>(); 806 auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
769 u128 uid = rp.PopRaw<u128>(); 807 u128 uid = rp.PopRaw<u128>();
770 808
771 LOG_WARNING(Service_FS, "(STUBBED) called save_struct = {}, uid = {:016X}{:016X}", 809 LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
772 save_struct.DebugInfo(), uid[1], uid[0]); 810 uid[1], uid[0]);
811
812 fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, save_struct);
773 813
774 IPC::ResponseBuilder rb{ctx, 2}; 814 IPC::ResponseBuilder rb{ctx, 2};
775 rb.Push(RESULT_SUCCESS); 815 rb.Push(RESULT_SUCCESS);
@@ -786,14 +826,24 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
786 IPC::RequestParser rp{ctx}; 826 IPC::RequestParser rp{ctx};
787 const auto parameters = rp.PopRaw<Parameters>(); 827 const auto parameters = rp.PopRaw<Parameters>();
788 828
789 auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor); 829 auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
790 if (dir.Failed()) { 830 if (dir.Failed()) {
791 IPC::ResponseBuilder rb{ctx, 2, 0, 0}; 831 IPC::ResponseBuilder rb{ctx, 2, 0, 0};
792 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND); 832 rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
793 return; 833 return;
794 } 834 }
795 835
796 IFileSystem filesystem(std::move(dir.Unwrap())); 836 FileSys::StorageId id;
837 if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) {
838 id = FileSys::StorageId::NandUser;
839 } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem ||
840 parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) {
841 id = FileSys::StorageId::SdCard;
842 } else {
843 id = FileSys::StorageId::NandSystem;
844 }
845
846 IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id));
797 847
798 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 848 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
799 rb.Push(RESULT_SUCCESS); 849 rb.Push(RESULT_SUCCESS);
@@ -812,7 +862,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext&
812 862
813 IPC::ResponseBuilder rb{ctx, 2, 0, 1}; 863 IPC::ResponseBuilder rb{ctx, 2, 0, 1};
814 rb.Push(RESULT_SUCCESS); 864 rb.Push(RESULT_SUCCESS);
815 rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space)); 865 rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc));
816} 866}
817 867
818void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) { 868void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
@@ -836,7 +886,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
836void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) { 886void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
837 LOG_DEBUG(Service_FS, "called"); 887 LOG_DEBUG(Service_FS, "called");
838 888
839 auto romfs = OpenRomFSCurrentProcess(); 889 auto romfs = fsc.OpenRomFSCurrentProcess();
840 if (romfs.Failed()) { 890 if (romfs.Failed()) {
841 // TODO (bunnei): Find the right error code to use here 891 // TODO (bunnei): Find the right error code to use here
842 LOG_CRITICAL(Service_FS, "no file system interface available!"); 892 LOG_CRITICAL(Service_FS, "no file system interface available!");
@@ -861,7 +911,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
861 LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}", 911 LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
862 static_cast<u8>(storage_id), unknown, title_id); 912 static_cast<u8>(storage_id), unknown, title_id);
863 913
864 auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data); 914 auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
865 915
866 if (data.Failed()) { 916 if (data.Failed()) {
867 const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id); 917 const auto archive = FileSys::SystemArchive::SynthesizeSystemArchive(title_id);
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index b5486a193..d52b55999 100644
--- a/src/core/hle/service/filesystem/fsp_srv.h
+++ b/src/core/hle/service/filesystem/fsp_srv.h
@@ -32,7 +32,7 @@ enum class LogMode : u32 {
32 32
33class FSP_SRV final : public ServiceFramework<FSP_SRV> { 33class FSP_SRV final : public ServiceFramework<FSP_SRV> {
34public: 34public:
35 explicit FSP_SRV(const Core::Reporter& reporter); 35 explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter);
36 ~FSP_SRV() override; 36 ~FSP_SRV() override;
37 37
38private: 38private:
@@ -51,6 +51,8 @@ private:
51 void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx); 51 void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
52 void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx); 52 void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
53 53
54 FileSystemController& fsc;
55
54 FileSys::VirtualFile romfs; 56 FileSys::VirtualFile romfs;
55 u64 current_process_id = 0; 57 u64 current_process_id = 0;
56 u32 access_log_program_index = 0; 58 u32 access_log_program_index = 0;
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index ce88a2941..13121c4f1 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -617,7 +617,7 @@ public:
617 } 617 }
618}; 618};
619 619
620void InstallInterfaces(SM::ServiceManager& service_manager) { 620void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc) {
621 std::make_shared<NS>("ns:am2")->InstallAsService(service_manager); 621 std::make_shared<NS>("ns:am2")->InstallAsService(service_manager);
622 std::make_shared<NS>("ns:ec")->InstallAsService(service_manager); 622 std::make_shared<NS>("ns:ec")->InstallAsService(service_manager);
623 std::make_shared<NS>("ns:rid")->InstallAsService(service_manager); 623 std::make_shared<NS>("ns:rid")->InstallAsService(service_manager);
@@ -628,7 +628,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
628 std::make_shared<NS_SU>()->InstallAsService(service_manager); 628 std::make_shared<NS_SU>()->InstallAsService(service_manager);
629 std::make_shared<NS_VM>()->InstallAsService(service_manager); 629 std::make_shared<NS_VM>()->InstallAsService(service_manager);
630 630
631 std::make_shared<PL_U>()->InstallAsService(service_manager); 631 std::make_shared<PL_U>(fsc)->InstallAsService(service_manager);
632} 632}
633 633
634} // namespace Service::NS 634} // namespace Service::NS
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 0e8256cb4..d067e7a9a 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -6,7 +6,13 @@
6 6
7#include "core/hle/service/service.h" 7#include "core/hle/service/service.h"
8 8
9namespace Service::NS { 9namespace Service {
10
11namespace FileSystem {
12class FileSystemController;
13} // namespace FileSystem
14
15namespace NS {
10 16
11class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> { 17class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
12public: 18public:
@@ -91,6 +97,8 @@ private:
91}; 97};
92 98
93/// Registers all NS services with the specified service manager. 99/// Registers all NS services with the specified service manager.
94void InstallInterfaces(SM::ServiceManager& service_manager); 100void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc);
101
102} // namespace NS
95 103
96} // namespace Service::NS 104} // namespace Service
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 2a522136d..9d49f36e8 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -150,7 +150,8 @@ struct PL_U::Impl {
150 std::vector<FontRegion> shared_font_regions; 150 std::vector<FontRegion> shared_font_regions;
151}; 151};
152 152
153PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} { 153PL_U::PL_U(FileSystem::FileSystemController& fsc)
154 : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
154 static const FunctionInfo functions[] = { 155 static const FunctionInfo functions[] = {
155 {0, &PL_U::RequestLoad, "RequestLoad"}, 156 {0, &PL_U::RequestLoad, "RequestLoad"},
156 {1, &PL_U::GetLoadState, "GetLoadState"}, 157 {1, &PL_U::GetLoadState, "GetLoadState"},
@@ -161,7 +162,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
161 }; 162 };
162 RegisterHandlers(functions); 163 RegisterHandlers(functions);
163 // Attempt to load shared font data from disk 164 // Attempt to load shared font data from disk
164 const auto* nand = FileSystem::GetSystemNANDContents(); 165 const auto* nand = fsc.GetSystemNANDContents();
165 std::size_t offset = 0; 166 std::size_t offset = 0;
166 // Rebuild shared fonts from data ncas 167 // Rebuild shared fonts from data ncas
167 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard), 168 if (nand->HasEntry(static_cast<u64>(FontArchives::Standard),
diff --git a/src/core/hle/service/ns/pl_u.h b/src/core/hle/service/ns/pl_u.h
index 253f26a2a..35ca424d2 100644
--- a/src/core/hle/service/ns/pl_u.h
+++ b/src/core/hle/service/ns/pl_u.h
@@ -7,11 +7,17 @@
7#include <memory> 7#include <memory>
8#include "core/hle/service/service.h" 8#include "core/hle/service/service.h"
9 9
10namespace Service::NS { 10namespace Service {
11
12namespace FileSystem {
13class FileSystemController;
14} // namespace FileSystem
15
16namespace NS {
11 17
12class PL_U final : public ServiceFramework<PL_U> { 18class PL_U final : public ServiceFramework<PL_U> {
13public: 19public:
14 PL_U(); 20 PL_U(FileSystem::FileSystemController& fsc);
15 ~PL_U() override; 21 ~PL_U() override;
16 22
17private: 23private:
@@ -26,4 +32,6 @@ private:
26 std::unique_ptr<Impl> impl; 32 std::unique_ptr<Impl> impl;
27}; 33};
28 34
29} // namespace Service::NS 35} // namespace NS
36
37} // namespace Service
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 3a0f8c3f6..454387467 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -199,6 +199,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
199 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it 199 // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
200 // here and pass it into the respective InstallInterfaces functions. 200 // here and pass it into the respective InstallInterfaces functions.
201 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming()); 201 auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming());
202 system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
202 203
203 SM::ServiceManager::InstallInterfaces(sm); 204 SM::ServiceManager::InstallInterfaces(sm);
204 205
@@ -235,7 +236,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
235 NIFM::InstallInterfaces(*sm); 236 NIFM::InstallInterfaces(*sm);
236 NIM::InstallInterfaces(*sm); 237 NIM::InstallInterfaces(*sm);
237 NPNS::InstallInterfaces(*sm); 238 NPNS::InstallInterfaces(*sm);
238 NS::InstallInterfaces(*sm); 239 NS::InstallInterfaces(*sm, system.GetFileSystemController());
239 Nvidia::InstallInterfaces(*sm, *nv_flinger, system); 240 Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
240 PCIe::InstallInterfaces(*sm); 241 PCIe::InstallInterfaces(*sm);
241 PCTL::InstallInterfaces(*sm); 242 PCTL::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index c6c4bdae5..aef964861 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -18,10 +18,6 @@ namespace Core {
18class System; 18class System;
19} 19}
20 20
21namespace FileSys {
22class VfsFilesystem;
23}
24
25namespace Kernel { 21namespace Kernel {
26class ClientPort; 22class ClientPort;
27class ServerPort; 23class ServerPort;
@@ -31,6 +27,10 @@ class HLERequestContext;
31 27
32namespace Service { 28namespace Service {
33 29
30namespace FileSystem {
31class FileSystemController;
32} // namespace FileSystem
33
34namespace SM { 34namespace SM {
35class ServiceManager; 35class ServiceManager;
36} 36}
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index f9e88be2b..d19c3623c 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -7,6 +7,7 @@
7#include "common/common_funcs.h" 7#include "common/common_funcs.h"
8#include "common/file_util.h" 8#include "common/file_util.h"
9#include "common/logging/log.h" 9#include "common/logging/log.h"
10#include "core/core.h"
10#include "core/file_sys/content_archive.h" 11#include "core/file_sys/content_archive.h"
11#include "core/file_sys/control_metadata.h" 12#include "core/file_sys/control_metadata.h"
12#include "core/file_sys/patch_manager.h" 13#include "core/file_sys/patch_manager.h"
@@ -176,7 +177,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
176 // Register the RomFS if a ".romfs" file was found 177 // Register the RomFS if a ".romfs" file was found
177 if (romfs_iter != files.end() && *romfs_iter != nullptr) { 178 if (romfs_iter != files.end() && *romfs_iter != nullptr) {
178 romfs = *romfs_iter; 179 romfs = *romfs_iter;
179 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 180 Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
181 std::make_unique<FileSys::RomFSFactory>(*this));
180 } 182 }
181 183
182 is_loaded = true; 184 is_loaded = true;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 0f65fb637..5a0469978 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -6,6 +6,7 @@
6 6
7#include "common/file_util.h" 7#include "common/file_util.h"
8#include "common/logging/log.h" 8#include "common/logging/log.h"
9#include "core/core.h"
9#include "core/file_sys/content_archive.h" 10#include "core/file_sys/content_archive.h"
10#include "core/file_sys/romfs_factory.h" 11#include "core/file_sys/romfs_factory.h"
11#include "core/hle/kernel/process.h" 12#include "core/hle/kernel/process.h"
@@ -57,7 +58,8 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
57 } 58 }
58 59
59 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) { 60 if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
60 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 61 Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
62 std::make_unique<FileSys::RomFSFactory>(*this));
61 } 63 }
62 64
63 is_loaded = true; 65 is_loaded = true;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 3a5361fdd..175898b91 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -10,6 +10,7 @@
10#include "common/file_util.h" 10#include "common/file_util.h"
11#include "common/logging/log.h" 11#include "common/logging/log.h"
12#include "common/swap.h" 12#include "common/swap.h"
13#include "core/core.h"
13#include "core/file_sys/control_metadata.h" 14#include "core/file_sys/control_metadata.h"
14#include "core/file_sys/romfs_factory.h" 15#include "core/file_sys/romfs_factory.h"
15#include "core/file_sys/vfs_offset.h" 16#include "core/file_sys/vfs_offset.h"
@@ -214,7 +215,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
214 } 215 }
215 216
216 if (romfs != nullptr) { 217 if (romfs != nullptr) {
217 Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this)); 218 Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
219 std::make_unique<FileSys::RomFSFactory>(*this));
218 } 220 }
219 221
220 is_loaded = true; 222 is_loaded = true;
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 35c82c99d..13950fc08 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -5,6 +5,7 @@
5#include <vector> 5#include <vector>
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/core.h"
8#include "core/file_sys/card_image.h" 9#include "core/file_sys/card_image.h"
9#include "core/file_sys/content_archive.h" 10#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h" 11#include "core/file_sys/control_metadata.h"
@@ -105,7 +106,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
105 106
106 FileSys::VirtualFile update_raw; 107 FileSys::VirtualFile update_raw;
107 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { 108 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
108 Service::FileSystem::SetPackedUpdate(std::move(update_raw)); 109 Core::System::GetInstance().GetFileSystemController().SetPackedUpdate(
110 std::move(update_raw));
109 } 111 }
110 112
111 is_loaded = true; 113 is_loaded = true;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 5e8553db9..7186ad1ff 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -5,6 +5,7 @@
5#include <vector> 5#include <vector>
6 6
7#include "common/common_types.h" 7#include "common/common_types.h"
8#include "core/core.h"
8#include "core/file_sys/card_image.h" 9#include "core/file_sys/card_image.h"
9#include "core/file_sys/content_archive.h" 10#include "core/file_sys/content_archive.h"
10#include "core/file_sys/control_metadata.h" 11#include "core/file_sys/control_metadata.h"
@@ -72,7 +73,8 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
72 73
73 FileSys::VirtualFile update_raw; 74 FileSys::VirtualFile update_raw;
74 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) { 75 if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
75 Service::FileSystem::SetPackedUpdate(std::move(update_raw)); 76 Core::System::GetInstance().GetFileSystemController().SetPackedUpdate(
77 std::move(update_raw));
76 } 78 }
77 79
78 is_loaded = true; 80 is_loaded = true;
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 0dd1632ac..7de3fd1e5 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.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 "common/file_util.h"
5#include "core/core.h" 6#include "core/core.h"
6#include "core/gdbstub/gdbstub.h" 7#include "core/gdbstub/gdbstub.h"
7#include "core/hle/service/hid/hid.h" 8#include "core/hle/service/hid/hid.h"
@@ -97,8 +98,8 @@ void LogSettings() {
97 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching); 98 LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
98 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id); 99 LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
99 LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd); 100 LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
100 LogSetting("DataStorage_NandDir", Settings::values.nand_dir); 101 LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
101 LogSetting("DataStorage_SdmcDir", Settings::values.sdmc_dir); 102 LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
102 LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub); 103 LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
103 LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port); 104 LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
104 LogSetting("Debugging_ProgramArgs", Settings::values.program_args); 105 LogSetting("Debugging_ProgramArgs", Settings::values.program_args);
diff --git a/src/core/settings.h b/src/core/settings.h
index d4b70ec4c..47bddfb30 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -346,6 +346,31 @@ struct TouchscreenInput {
346 u32 rotation_angle; 346 u32 rotation_angle;
347}; 347};
348 348
349enum class NANDTotalSize : u64 {
350 S29_1GB = 0x747C00000ULL,
351};
352
353enum class NANDUserSize : u64 {
354 S26GB = 0x680000000ULL,
355};
356
357enum class NANDSystemSize : u64 {
358 S2_5GB = 0xA0000000,
359};
360
361enum class SDMCSize : u64 {
362 S1GB = 0x40000000,
363 S2GB = 0x80000000,
364 S4GB = 0x100000000ULL,
365 S8GB = 0x200000000ULL,
366 S16GB = 0x400000000ULL,
367 S32GB = 0x800000000ULL,
368 S64GB = 0x1000000000ULL,
369 S128GB = 0x2000000000ULL,
370 S256GB = 0x4000000000ULL,
371 S1TB = 0x10000000000ULL,
372};
373
349struct Values { 374struct Values {
350 // System 375 // System
351 bool use_docked_mode; 376 bool use_docked_mode;
@@ -382,8 +407,13 @@ struct Values {
382 407
383 // Data Storage 408 // Data Storage
384 bool use_virtual_sd; 409 bool use_virtual_sd;
385 std::string nand_dir; 410 bool gamecard_inserted;
386 std::string sdmc_dir; 411 bool gamecard_current_game;
412 std::string gamecard_path;
413 NANDTotalSize nand_total_size;
414 NANDSystemSize nand_system_size;
415 NANDUserSize nand_user_size;
416 SDMCSize sdmc_size;
387 417
388 // Renderer 418 // Renderer
389 float resolution_factor; 419 float resolution_factor;
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index f051e17b4..dc6fa07fc 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -33,6 +33,9 @@ add_executable(yuzu
33 configuration/configure_debug.ui 33 configuration/configure_debug.ui
34 configuration/configure_dialog.cpp 34 configuration/configure_dialog.cpp
35 configuration/configure_dialog.h 35 configuration/configure_dialog.h
36 configuration/configure_filesystem.cpp
37 configuration/configure_filesystem.h
38 configuration/configure_filesystem.ui
36 configuration/configure_gamelist.cpp 39 configuration/configure_gamelist.cpp
37 configuration/configure_gamelist.h 40 configuration/configure_gamelist.h
38 configuration/configure_gamelist.ui 41 configuration/configure_gamelist.ui
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 3f54f54fb..92d9fb161 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -459,6 +459,49 @@ void Config::ReadDataStorageValues() {
459 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))) 459 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)))
460 .toString() 460 .toString()
461 .toStdString()); 461 .toStdString());
462 FileUtil::GetUserPath(
463 FileUtil::UserPath::LoadDir,
464 qt_config
465 ->value(QStringLiteral("load_directory"),
466 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)))
467 .toString()
468 .toStdString());
469 FileUtil::GetUserPath(
470 FileUtil::UserPath::DumpDir,
471 qt_config
472 ->value(QStringLiteral("dump_directory"),
473 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)))
474 .toString()
475 .toStdString());
476 FileUtil::GetUserPath(
477 FileUtil::UserPath::CacheDir,
478 qt_config
479 ->value(QStringLiteral("cache_directory"),
480 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)))
481 .toString()
482 .toStdString());
483 Settings::values.gamecard_inserted =
484 ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool();
485 Settings::values.gamecard_current_game =
486 ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
487 Settings::values.gamecard_path =
488 ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString();
489 Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
490 ReadSetting(QStringLiteral("nand_total_size"),
491 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)))
492 .toULongLong());
493 Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
494 ReadSetting(QStringLiteral("nand_user_size"),
495 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)))
496 .toULongLong());
497 Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
498 ReadSetting(QStringLiteral("nand_system_size"),
499 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)))
500 .toULongLong());
501 Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
502 ReadSetting(QStringLiteral("sdmc_size"),
503 QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)))
504 .toULongLong());
462 505
463 qt_config->endGroup(); 506 qt_config->endGroup();
464} 507}
@@ -875,7 +918,32 @@ void Config::SaveDataStorageValues() {
875 WriteSetting(QStringLiteral("sdmc_directory"), 918 WriteSetting(QStringLiteral("sdmc_directory"),
876 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)), 919 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
877 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 920 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
878 921 WriteSetting(QStringLiteral("load_directory"),
922 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)),
923 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
924 WriteSetting(QStringLiteral("dump_directory"),
925 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)),
926 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
927 WriteSetting(QStringLiteral("cache_directory"),
928 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)),
929 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
930 WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false);
931 WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
932 false);
933 WriteSetting(QStringLiteral("gamecard_path"),
934 QString::fromStdString(Settings::values.gamecard_path), QStringLiteral(""));
935 WriteSetting(QStringLiteral("nand_total_size"),
936 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_total_size)),
937 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)));
938 WriteSetting(QStringLiteral("nand_user_size"),
939 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_user_size)),
940 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)));
941 WriteSetting(QStringLiteral("nand_system_size"),
942 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_system_size)),
943 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)));
944 WriteSetting(QStringLiteral("sdmc_size"),
945 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.sdmc_size)),
946 QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)));
879 qt_config->endGroup(); 947 qt_config->endGroup();
880} 948}
881 949
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 267717bc9..49fadd0ef 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -63,6 +63,11 @@
63 <string>Profiles</string> 63 <string>Profiles</string>
64 </attribute> 64 </attribute>
65 </widget> 65 </widget>
66 <widget class="ConfigureFilesystem" name="filesystemTab">
67 <attribute name="title">
68 <string>Filesystem</string>
69 </attribute>
70 </widget>
66 <widget class="ConfigureInputSimple" name="inputTab"> 71 <widget class="ConfigureInputSimple" name="inputTab">
67 <attribute name="title"> 72 <attribute name="title">
68 <string>Input</string> 73 <string>Input</string>
@@ -126,6 +131,12 @@
126 <container>1</container> 131 <container>1</container>
127 </customwidget> 132 </customwidget>
128 <customwidget> 133 <customwidget>
134 <class>ConfigureFilesystem</class>
135 <extends>QWidget</extends>
136 <header>configuration/configure_filesystem.h</header>
137 <container>1</container>
138 </customwidget>
139 <customwidget>
129 <class>ConfigureAudio</class> 140 <class>ConfigureAudio</class>
130 <extends>QWidget</extends> 141 <extends>QWidget</extends>
131 <header>configuration/configure_audio.h</header> 142 <header>configuration/configure_audio.h</header>
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 5b7e03056..90c1f9459 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -34,8 +34,6 @@ void ConfigureDebug::SetConfiguration() {
34 ui->toggle_console->setChecked(UISettings::values.show_console); 34 ui->toggle_console->setChecked(UISettings::values.show_console);
35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter)); 35 ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args)); 36 ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
37 ui->dump_exefs->setChecked(Settings::values.dump_exefs);
38 ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
39 ui->reporting_services->setChecked(Settings::values.reporting_services); 37 ui->reporting_services->setChecked(Settings::values.reporting_services);
40 ui->quest_flag->setChecked(Settings::values.quest_flag); 38 ui->quest_flag->setChecked(Settings::values.quest_flag);
41} 39}
@@ -46,8 +44,6 @@ void ConfigureDebug::ApplyConfiguration() {
46 UISettings::values.show_console = ui->toggle_console->isChecked(); 44 UISettings::values.show_console = ui->toggle_console->isChecked();
47 Settings::values.log_filter = ui->log_filter_edit->text().toStdString(); 45 Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
48 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString(); 46 Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
49 Settings::values.dump_exefs = ui->dump_exefs->isChecked();
50 Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked();
51 Settings::values.reporting_services = ui->reporting_services->isChecked(); 47 Settings::values.reporting_services = ui->reporting_services->isChecked();
52 Settings::values.quest_flag = ui->quest_flag->isChecked(); 48 Settings::values.quest_flag = ui->quest_flag->isChecked();
53 Debugger::ToggleConsole(); 49 Debugger::ToggleConsole();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 7e109cef0..ce49569bb 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -103,58 +103,6 @@
103 </item> 103 </item>
104 </layout> 104 </layout>
105 </item> 105 </item>
106 </layout>
107 </widget>
108 </item>
109 <item>
110 <widget class="QGroupBox" name="groupBox_3">
111 <property name="title">
112 <string>Homebrew</string>
113 </property>
114 <layout class="QVBoxLayout" name="verticalLayout_5">
115 <item>
116 <layout class="QHBoxLayout" name="horizontalLayout_4">
117 <item>
118 <widget class="QLabel" name="label_3">
119 <property name="text">
120 <string>Arguments String</string>
121 </property>
122 </widget>
123 </item>
124 <item>
125 <widget class="QLineEdit" name="homebrew_args_edit"/>
126 </item>
127 </layout>
128 </item>
129 </layout>
130 </widget>
131 </item>
132 <item>
133 <widget class="QGroupBox" name="groupBox_4">
134 <property name="title">
135 <string>Dump</string>
136 </property>
137 <layout class="QVBoxLayout" name="verticalLayout_6">
138 <item>
139 <widget class="QCheckBox" name="dump_decompressed_nso">
140 <property name="whatsThis">
141 <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
142 </property>
143 <property name="text">
144 <string>Dump Decompressed NSOs</string>
145 </property>
146 </widget>
147 </item>
148 <item>
149 <widget class="QCheckBox" name="dump_exefs">
150 <property name="whatsThis">
151 <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
152 </property>
153 <property name="text">
154 <string>Dump ExeFS</string>
155 </property>
156 </widget>
157 </item>
158 <item> 106 <item>
159 <widget class="QCheckBox" name="reporting_services"> 107 <widget class="QCheckBox" name="reporting_services">
160 <property name="text"> 108 <property name="text">
@@ -163,7 +111,7 @@
163 </widget> 111 </widget>
164 </item> 112 </item>
165 <item> 113 <item>
166 <widget class="QLabel" name="label_3"> 114 <widget class="QLabel" name="label">
167 <property name="font"> 115 <property name="font">
168 <font> 116 <font>
169 <italic>true</italic> 117 <italic>true</italic>
@@ -197,10 +145,36 @@
197 </widget> 145 </widget>
198 </item> 146 </item>
199 <item> 147 <item>
148 <widget class="QGroupBox" name="groupBox_3">
149 <property name="title">
150 <string>Homebrew</string>
151 </property>
152 <layout class="QVBoxLayout" name="verticalLayout_5">
153 <item>
154 <layout class="QHBoxLayout" name="horizontalLayout_4">
155 <item>
156 <widget class="QLabel" name="label_3">
157 <property name="text">
158 <string>Arguments String</string>
159 </property>
160 </widget>
161 </item>
162 <item>
163 <widget class="QLineEdit" name="homebrew_args_edit"/>
164 </item>
165 </layout>
166 </item>
167 </layout>
168 </widget>
169 </item>
170 <item>
200 <spacer name="verticalSpacer"> 171 <spacer name="verticalSpacer">
201 <property name="orientation"> 172 <property name="orientation">
202 <enum>Qt::Vertical</enum> 173 <enum>Qt::Vertical</enum>
203 </property> 174 </property>
175 <property name="sizeType">
176 <enum>QSizePolicy::Expanding</enum>
177 </property>
204 <property name="sizeHint" stdset="0"> 178 <property name="sizeHint" stdset="0">
205 <size> 179 <size>
206 <width>20</width> 180 <width>20</width>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 775e3f2ea..7c875ae87 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -37,6 +37,7 @@ void ConfigureDialog::ApplyConfiguration() {
37 ui->gameListTab->ApplyConfiguration(); 37 ui->gameListTab->ApplyConfiguration();
38 ui->systemTab->ApplyConfiguration(); 38 ui->systemTab->ApplyConfiguration();
39 ui->profileManagerTab->ApplyConfiguration(); 39 ui->profileManagerTab->ApplyConfiguration();
40 ui->filesystemTab->applyConfiguration();
40 ui->inputTab->ApplyConfiguration(); 41 ui->inputTab->ApplyConfiguration();
41 ui->hotkeysTab->ApplyConfiguration(registry); 42 ui->hotkeysTab->ApplyConfiguration(registry);
42 ui->graphicsTab->ApplyConfiguration(); 43 ui->graphicsTab->ApplyConfiguration();
@@ -73,7 +74,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
73void ConfigureDialog::PopulateSelectionList() { 74void ConfigureDialog::PopulateSelectionList() {
74 const std::array<std::pair<QString, QList<QWidget*>>, 4> items{ 75 const std::array<std::pair<QString, QList<QWidget*>>, 4> items{
75 {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}}, 76 {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}},
76 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->audioTab}}, 77 {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->filesystemTab, ui->audioTab}},
77 {tr("Graphics"), {ui->graphicsTab}}, 78 {tr("Graphics"), {ui->graphicsTab}},
78 {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}}, 79 {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}},
79 }; 80 };
@@ -106,6 +107,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
106 {ui->debugTab, tr("Debug")}, 107 {ui->debugTab, tr("Debug")},
107 {ui->webTab, tr("Web")}, 108 {ui->webTab, tr("Web")},
108 {ui->gameListTab, tr("Game List")}, 109 {ui->gameListTab, tr("Game List")},
110 {ui->filesystemTab, tr("Filesystem")},
109 }; 111 };
110 112
111 [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget); 113 [[maybe_unused]] const QSignalBlocker blocker(ui->tabWidget);
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp
new file mode 100644
index 000000000..29f540eb7
--- /dev/null
+++ b/src/yuzu/configuration/configure_filesystem.cpp
@@ -0,0 +1,177 @@
1// Copyright 2019 yuzu Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <QFileDialog>
6#include <QMessageBox>
7#include "common/common_paths.h"
8#include "common/file_util.h"
9#include "core/settings.h"
10#include "ui_configure_filesystem.h"
11#include "yuzu/configuration/configure_filesystem.h"
12#include "yuzu/uisettings.h"
13
14namespace {
15
16template <typename T>
17void SetComboBoxFromData(QComboBox* combo_box, T data) {
18 const auto index = combo_box->findData(QVariant::fromValue(static_cast<u64>(data)));
19 if (index >= combo_box->count() || index < 0)
20 return;
21
22 combo_box->setCurrentIndex(index);
23}
24
25} // Anonymous namespace
26
27ConfigureFilesystem::ConfigureFilesystem(QWidget* parent)
28 : QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) {
29 ui->setupUi(this);
30 this->setConfiguration();
31
32 connect(ui->nand_directory_button, &QToolButton::pressed, this,
33 [this] { SetDirectory(DirectoryTarget::NAND, ui->nand_directory_edit); });
34 connect(ui->sdmc_directory_button, &QToolButton::pressed, this,
35 [this] { SetDirectory(DirectoryTarget::SD, ui->sdmc_directory_edit); });
36 connect(ui->gamecard_path_button, &QToolButton::pressed, this,
37 [this] { SetDirectory(DirectoryTarget::Gamecard, ui->gamecard_path_edit); });
38 connect(ui->dump_path_button, &QToolButton::pressed, this,
39 [this] { SetDirectory(DirectoryTarget::Dump, ui->dump_path_edit); });
40 connect(ui->load_path_button, &QToolButton::pressed, this,
41 [this] { SetDirectory(DirectoryTarget::Load, ui->load_path_edit); });
42 connect(ui->cache_directory_button, &QToolButton::pressed, this,
43 [this] { SetDirectory(DirectoryTarget::Cache, ui->cache_directory_edit); });
44
45 connect(ui->reset_game_list_cache, &QPushButton::pressed, this,
46 &ConfigureFilesystem::ResetMetadata);
47
48 connect(ui->gamecard_inserted, &QCheckBox::stateChanged, this,
49 &ConfigureFilesystem::UpdateEnabledControls);
50 connect(ui->gamecard_current_game, &QCheckBox::stateChanged, this,
51 &ConfigureFilesystem::UpdateEnabledControls);
52}
53
54ConfigureFilesystem::~ConfigureFilesystem() = default;
55
56void ConfigureFilesystem::setConfiguration() {
57 ui->nand_directory_edit->setText(
58 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
59 ui->sdmc_directory_edit->setText(
60 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
61 ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path));
62 ui->dump_path_edit->setText(
63 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
64 ui->load_path_edit->setText(
65 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
66 ui->cache_directory_edit->setText(
67 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
68
69 ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted);
70 ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game);
71 ui->dump_exefs->setChecked(Settings::values.dump_exefs);
72 ui->dump_nso->setChecked(Settings::values.dump_nso);
73
74 ui->cache_game_list->setChecked(UISettings::values.cache_game_list);
75
76 SetComboBoxFromData(ui->nand_size, Settings::values.nand_total_size);
77 SetComboBoxFromData(ui->usrnand_size, Settings::values.nand_user_size);
78 SetComboBoxFromData(ui->sysnand_size, Settings::values.nand_system_size);
79 SetComboBoxFromData(ui->sdmc_size, Settings::values.sdmc_size);
80
81 UpdateEnabledControls();
82}
83
84void ConfigureFilesystem::applyConfiguration() {
85 FileUtil::GetUserPath(FileUtil::UserPath::NANDDir,
86 ui->nand_directory_edit->text().toStdString());
87 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
88 ui->sdmc_directory_edit->text().toStdString());
89 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, ui->dump_path_edit->text().toStdString());
90 FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, ui->load_path_edit->text().toStdString());
91 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir,
92 ui->cache_directory_edit->text().toStdString());
93 Settings::values.gamecard_path = ui->gamecard_path_edit->text().toStdString();
94
95 Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked();
96 Settings::values.gamecard_current_game = ui->gamecard_current_game->isChecked();
97 Settings::values.dump_exefs = ui->dump_exefs->isChecked();
98 Settings::values.dump_nso = ui->dump_nso->isChecked();
99
100 UISettings::values.cache_game_list = ui->cache_game_list->isChecked();
101
102 Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
103 ui->nand_size->itemData(ui->nand_size->currentIndex()).toULongLong());
104 Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
105 ui->nand_size->itemData(ui->sysnand_size->currentIndex()).toULongLong());
106 Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
107 ui->nand_size->itemData(ui->usrnand_size->currentIndex()).toULongLong());
108 Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
109 ui->nand_size->itemData(ui->sdmc_size->currentIndex()).toULongLong());
110}
111
112void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) {
113 QString caption;
114
115 switch (target) {
116 case DirectoryTarget::NAND:
117 caption = tr("Select Emulated NAND Directory...");
118 break;
119 case DirectoryTarget::SD:
120 caption = tr("Select Emulated SD Directory...");
121 break;
122 case DirectoryTarget::Gamecard:
123 caption = tr("Select Gamecard Path...");
124 break;
125 case DirectoryTarget::Dump:
126 caption = tr("Select Dump Directory...");
127 break;
128 case DirectoryTarget::Load:
129 caption = tr("Select Mod Load Directory...");
130 break;
131 case DirectoryTarget::Cache:
132 caption = tr("Select Cache Directory...");
133 break;
134 }
135
136 QString str;
137 if (target == DirectoryTarget::Gamecard) {
138 str = QFileDialog::getOpenFileName(this, caption, QFileInfo(edit->text()).dir().path(),
139 QStringLiteral("NX Gamecard;*.xci"));
140 } else {
141 str = QFileDialog::getExistingDirectory(this, caption, edit->text());
142 }
143
144 if (str.isEmpty())
145 return;
146
147 edit->setText(str);
148}
149
150void ConfigureFilesystem::ResetMetadata() {
151 if (!FileUtil::Exists(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
152 "game_list")) {
153 QMessageBox::information(this, tr("Reset Metadata Cache"),
154 tr("The metadata cache is already empty."));
155 } else if (FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
156 DIR_SEP + "game_list")) {
157 QMessageBox::information(this, tr("Reset Metadata Cache"),
158 tr("The operation completed successfully."));
159 UISettings::values.is_game_list_reload_pending.exchange(true);
160 } else {
161 QMessageBox::warning(
162 this, tr("Reset Metadata Cache"),
163 tr("The metadata cache couldn't be deleted. It might be in use or non-existent."));
164 }
165}
166
167void ConfigureFilesystem::UpdateEnabledControls() {
168 ui->gamecard_current_game->setEnabled(ui->gamecard_inserted->isChecked());
169 ui->gamecard_path_edit->setEnabled(ui->gamecard_inserted->isChecked() &&
170 !ui->gamecard_current_game->isChecked());
171 ui->gamecard_path_button->setEnabled(ui->gamecard_inserted->isChecked() &&
172 !ui->gamecard_current_game->isChecked());
173}
174
175void ConfigureFilesystem::retranslateUi() {
176 ui->retranslateUi(this);
177}
diff --git a/src/yuzu/configuration/configure_filesystem.h b/src/yuzu/configuration/configure_filesystem.h
new file mode 100644
index 000000000..a79303760
--- /dev/null
+++ b/src/yuzu/configuration/configure_filesystem.h
@@ -0,0 +1,43 @@
1// Copyright 2019 yuzu 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 <memory>
8#include <QWidget>
9
10class QLineEdit;
11
12namespace Ui {
13class ConfigureFilesystem;
14}
15
16class ConfigureFilesystem : public QWidget {
17 Q_OBJECT
18
19public:
20 explicit ConfigureFilesystem(QWidget* parent = nullptr);
21 ~ConfigureFilesystem() override;
22
23 void applyConfiguration();
24 void retranslateUi();
25
26private:
27 void setConfiguration();
28
29 enum class DirectoryTarget {
30 NAND,
31 SD,
32 Gamecard,
33 Dump,
34 Load,
35 Cache,
36 };
37
38 void SetDirectory(DirectoryTarget target, QLineEdit* edit);
39 void ResetMetadata();
40 void UpdateEnabledControls();
41
42 std::unique_ptr<Ui::ConfigureFilesystem> ui;
43};
diff --git a/src/yuzu/configuration/configure_filesystem.ui b/src/yuzu/configuration/configure_filesystem.ui
new file mode 100644
index 000000000..58cd07f52
--- /dev/null
+++ b/src/yuzu/configuration/configure_filesystem.ui
@@ -0,0 +1,395 @@
1<?xml version="1.0" encoding="UTF-8"?>
2<ui version="4.0">
3 <class>ConfigureFilesystem</class>
4 <widget class="QWidget" name="ConfigureFilesystem">
5 <property name="geometry">
6 <rect>
7 <x>0</x>
8 <y>0</y>
9 <width>453</width>
10 <height>561</height>
11 </rect>
12 </property>
13 <property name="windowTitle">
14 <string>Form</string>
15 </property>
16 <layout class="QVBoxLayout" name="verticalLayout">
17 <item>
18 <layout class="QVBoxLayout" name="verticalLayout_3">
19 <item>
20 <widget class="QGroupBox" name="groupBox">
21 <property name="title">
22 <string>Storage Directories</string>
23 </property>
24 <layout class="QGridLayout" name="gridLayout">
25 <item row="0" column="0">
26 <widget class="QLabel" name="label">
27 <property name="text">
28 <string>NAND</string>
29 </property>
30 </widget>
31 </item>
32 <item row="0" column="3">
33 <widget class="QToolButton" name="nand_directory_button">
34 <property name="text">
35 <string>...</string>
36 </property>
37 </widget>
38 </item>
39 <item row="0" column="2">
40 <widget class="QLineEdit" name="nand_directory_edit"/>
41 </item>
42 <item row="1" column="2">
43 <widget class="QLineEdit" name="sdmc_directory_edit"/>
44 </item>
45 <item row="1" column="0">
46 <widget class="QLabel" name="label_2">
47 <property name="text">
48 <string>SD Card</string>
49 </property>
50 </widget>
51 </item>
52 <item row="1" column="3">
53 <widget class="QToolButton" name="sdmc_directory_button">
54 <property name="text">
55 <string>...</string>
56 </property>
57 </widget>
58 </item>
59 <item row="0" column="1">
60 <spacer name="horizontalSpacer">
61 <property name="orientation">
62 <enum>Qt::Horizontal</enum>
63 </property>
64 <property name="sizeType">
65 <enum>QSizePolicy::Maximum</enum>
66 </property>
67 <property name="sizeHint" stdset="0">
68 <size>
69 <width>60</width>
70 <height>20</height>
71 </size>
72 </property>
73 </spacer>
74 </item>
75 </layout>
76 </widget>
77 </item>
78 <item>
79 <widget class="QGroupBox" name="groupBox_2">
80 <property name="title">
81 <string>Gamecard</string>
82 </property>
83 <layout class="QGridLayout" name="gridLayout_2">
84 <item row="2" column="1">
85 <widget class="QLabel" name="label_3">
86 <property name="text">
87 <string>Path</string>
88 </property>
89 </widget>
90 </item>
91 <item row="2" column="2">
92 <widget class="QLineEdit" name="gamecard_path_edit"/>
93 </item>
94 <item row="0" column="1">
95 <widget class="QCheckBox" name="gamecard_inserted">
96 <property name="text">
97 <string>Inserted</string>
98 </property>
99 </widget>
100 </item>
101 <item row="1" column="1">
102 <widget class="QCheckBox" name="gamecard_current_game">
103 <property name="text">
104 <string>Current Game</string>
105 </property>
106 </widget>
107 </item>
108 <item row="2" column="3">
109 <widget class="QToolButton" name="gamecard_path_button">
110 <property name="text">
111 <string>...</string>
112 </property>
113 </widget>
114 </item>
115 </layout>
116 </widget>
117 </item>
118 <item>
119 <widget class="QGroupBox" name="groupBox_3">
120 <property name="title">
121 <string>Storage Sizes</string>
122 </property>
123 <layout class="QGridLayout" name="gridLayout_3">
124 <item row="3" column="0">
125 <widget class="QLabel" name="label_5">
126 <property name="text">
127 <string>SD Card</string>
128 </property>
129 </widget>
130 </item>
131 <item row="1" column="0">
132 <widget class="QLabel" name="label_4">
133 <property name="text">
134 <string>System NAND</string>
135 </property>
136 </widget>
137 </item>
138 <item row="1" column="1">
139 <widget class="QComboBox" name="sysnand_size">
140 <item>
141 <property name="text">
142 <string>2.5 GB</string>
143 </property>
144 </item>
145 </widget>
146 </item>
147 <item row="3" column="1">
148 <widget class="QComboBox" name="sdmc_size">
149 <property name="currentText">
150 <string>32 GB</string>
151 </property>
152 <item>
153 <property name="text">
154 <string>1 GB</string>
155 </property>
156 </item>
157 <item>
158 <property name="text">
159 <string>2 GB</string>
160 </property>
161 </item>
162 <item>
163 <property name="text">
164 <string>4 GB</string>
165 </property>
166 </item>
167 <item>
168 <property name="text">
169 <string>8 GB</string>
170 </property>
171 </item>
172 <item>
173 <property name="text">
174 <string>16 GB</string>
175 </property>
176 </item>
177 <item>
178 <property name="text">
179 <string>32 GB</string>
180 </property>
181 </item>
182 <item>
183 <property name="text">
184 <string>64 GB</string>
185 </property>
186 </item>
187 <item>
188 <property name="text">
189 <string>128 GB</string>
190 </property>
191 </item>
192 <item>
193 <property name="text">
194 <string>256 GB</string>
195 </property>
196 </item>
197 <item>
198 <property name="text">
199 <string>1 TB</string>
200 </property>
201 </item>
202 </widget>
203 </item>
204 <item row="2" column="1">
205 <widget class="QComboBox" name="usrnand_size">
206 <item>
207 <property name="text">
208 <string>26 GB</string>
209 </property>
210 </item>
211 </widget>
212 </item>
213 <item row="2" column="0">
214 <widget class="QLabel" name="label_6">
215 <property name="text">
216 <string>User NAND</string>
217 </property>
218 </widget>
219 </item>
220 <item row="0" column="0">
221 <widget class="QLabel" name="label_7">
222 <property name="text">
223 <string>NAND</string>
224 </property>
225 </widget>
226 </item>
227 <item row="0" column="1">
228 <widget class="QComboBox" name="nand_size">
229 <item>
230 <property name="text">
231 <string>29.1 GB</string>
232 </property>
233 </item>
234 </widget>
235 </item>
236 </layout>
237 </widget>
238 </item>
239 <item>
240 <widget class="QGroupBox" name="groupBox_4">
241 <property name="title">
242 <string>Patch Manager</string>
243 </property>
244 <layout class="QGridLayout" name="gridLayout_4">
245 <item row="1" column="2">
246 <widget class="QLineEdit" name="load_path_edit"/>
247 </item>
248 <item row="0" column="2">
249 <widget class="QLineEdit" name="dump_path_edit"/>
250 </item>
251 <item row="0" column="3">
252 <widget class="QToolButton" name="dump_path_button">
253 <property name="text">
254 <string>...</string>
255 </property>
256 </widget>
257 </item>
258 <item row="1" column="3">
259 <widget class="QToolButton" name="load_path_button">
260 <property name="text">
261 <string>...</string>
262 </property>
263 </widget>
264 </item>
265 <item row="2" column="0" colspan="4">
266 <layout class="QHBoxLayout" name="horizontalLayout">
267 <item>
268 <widget class="QCheckBox" name="dump_nso">
269 <property name="text">
270 <string>Dump Decompressed NSOs</string>
271 </property>
272 </widget>
273 </item>
274 <item>
275 <widget class="QCheckBox" name="dump_exefs">
276 <property name="text">
277 <string>Dump ExeFS</string>
278 </property>
279 </widget>
280 </item>
281 </layout>
282 </item>
283 <item row="1" column="0">
284 <widget class="QLabel" name="label_9">
285 <property name="text">
286 <string>Mod Load Root</string>
287 </property>
288 </widget>
289 </item>
290 <item row="0" column="0">
291 <widget class="QLabel" name="label_8">
292 <property name="text">
293 <string>Dump Root</string>
294 </property>
295 </widget>
296 </item>
297 <item row="0" column="1">
298 <spacer name="horizontalSpacer_2">
299 <property name="orientation">
300 <enum>Qt::Horizontal</enum>
301 </property>
302 <property name="sizeType">
303 <enum>QSizePolicy::Fixed</enum>
304 </property>
305 <property name="sizeHint" stdset="0">
306 <size>
307 <width>40</width>
308 <height>20</height>
309 </size>
310 </property>
311 </spacer>
312 </item>
313 </layout>
314 </widget>
315 </item>
316 <item>
317 <widget class="QGroupBox" name="groupBox_5">
318 <property name="title">
319 <string>Caching</string>
320 </property>
321 <layout class="QGridLayout" name="gridLayout_5">
322 <item row="0" column="0">
323 <widget class="QLabel" name="label_10">
324 <property name="text">
325 <string>Cache Directory</string>
326 </property>
327 </widget>
328 </item>
329 <item row="0" column="1">
330 <spacer name="horizontalSpacer_3">
331 <property name="orientation">
332 <enum>Qt::Horizontal</enum>
333 </property>
334 <property name="sizeType">
335 <enum>QSizePolicy::Fixed</enum>
336 </property>
337 <property name="sizeHint" stdset="0">
338 <size>
339 <width>40</width>
340 <height>20</height>
341 </size>
342 </property>
343 </spacer>
344 </item>
345 <item row="0" column="2">
346 <widget class="QLineEdit" name="cache_directory_edit"/>
347 </item>
348 <item row="0" column="3">
349 <widget class="QToolButton" name="cache_directory_button">
350 <property name="text">
351 <string>...</string>
352 </property>
353 </widget>
354 </item>
355 <item row="1" column="0" colspan="4">
356 <layout class="QHBoxLayout" name="horizontalLayout_2">
357 <item>
358 <widget class="QCheckBox" name="cache_game_list">
359 <property name="text">
360 <string>Cache Game List Metadata</string>
361 </property>
362 </widget>
363 </item>
364 <item>
365 <widget class="QPushButton" name="reset_game_list_cache">
366 <property name="text">
367 <string>Reset Metadata Cache</string>
368 </property>
369 </widget>
370 </item>
371 </layout>
372 </item>
373 </layout>
374 </widget>
375 </item>
376 </layout>
377 </item>
378 <item>
379 <spacer name="verticalSpacer">
380 <property name="orientation">
381 <enum>Qt::Vertical</enum>
382 </property>
383 <property name="sizeHint" stdset="0">
384 <size>
385 <width>20</width>
386 <height>40</height>
387 </size>
388 </property>
389 </spacer>
390 </item>
391 </layout>
392 </widget>
393 <resources/>
394 <connections/>
395</ui>
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 10bcd650e..b49446be9 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.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 <QSpinBox>
5#include "core/core.h" 6#include "core/core.h"
6#include "core/settings.h" 7#include "core/settings.h"
7#include "ui_configure_general.h" 8#include "ui_configure_general.h"
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 0865385e3..f147044d9 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -221,7 +221,7 @@ GMainWindow::GMainWindow()
221 std::make_unique<FileSys::ContentProviderUnion>()); 221 std::make_unique<FileSys::ContentProviderUnion>());
222 Core::System::GetInstance().RegisterContentProvider( 222 Core::System::GetInstance().RegisterContentProvider(
223 FileSys::ContentProviderUnionSlot::FrontendManual, provider.get()); 223 FileSys::ContentProviderUnionSlot::FrontendManual, provider.get());
224 Service::FileSystem::CreateFactories(*vfs); 224 Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
225 225
226 // Gen keys if necessary 226 // Gen keys if necessary
227 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning); 227 OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
@@ -1507,15 +1507,19 @@ void GMainWindow::OnMenuInstallToNAND() {
1507 failed(); 1507 failed();
1508 return; 1508 return;
1509 } 1509 }
1510 const auto res = 1510 const auto res = Core::System::GetInstance()
1511 Service::FileSystem::GetUserNANDContents()->InstallEntry(*nsp, false, qt_raw_copy); 1511 .GetFileSystemController()
1512 .GetUserNANDContents()
1513 ->InstallEntry(*nsp, false, qt_raw_copy);
1512 if (res == FileSys::InstallResult::Success) { 1514 if (res == FileSys::InstallResult::Success) {
1513 success(); 1515 success();
1514 } else { 1516 } else {
1515 if (res == FileSys::InstallResult::ErrorAlreadyExists) { 1517 if (res == FileSys::InstallResult::ErrorAlreadyExists) {
1516 if (overwrite()) { 1518 if (overwrite()) {
1517 const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( 1519 const auto res2 = Core::System::GetInstance()
1518 *nsp, true, qt_raw_copy); 1520 .GetFileSystemController()
1521 .GetUserNANDContents()
1522 ->InstallEntry(*nsp, true, qt_raw_copy);
1519 if (res2 == FileSys::InstallResult::Success) { 1523 if (res2 == FileSys::InstallResult::Success) {
1520 success(); 1524 success();
1521 } else { 1525 } else {
@@ -1569,19 +1573,28 @@ void GMainWindow::OnMenuInstallToNAND() {
1569 1573
1570 FileSys::InstallResult res; 1574 FileSys::InstallResult res;
1571 if (index >= static_cast<size_t>(FileSys::TitleType::Application)) { 1575 if (index >= static_cast<size_t>(FileSys::TitleType::Application)) {
1572 res = Service::FileSystem::GetUserNANDContents()->InstallEntry( 1576 res = Core::System::GetInstance()
1573 *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); 1577 .GetFileSystemController()
1578 .GetUserNANDContents()
1579 ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
1580 qt_raw_copy);
1574 } else { 1581 } else {
1575 res = Service::FileSystem::GetSystemNANDContents()->InstallEntry( 1582 res = Core::System::GetInstance()
1576 *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy); 1583 .GetFileSystemController()
1584 .GetSystemNANDContents()
1585 ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
1586 qt_raw_copy);
1577 } 1587 }
1578 1588
1579 if (res == FileSys::InstallResult::Success) { 1589 if (res == FileSys::InstallResult::Success) {
1580 success(); 1590 success();
1581 } else if (res == FileSys::InstallResult::ErrorAlreadyExists) { 1591 } else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
1582 if (overwrite()) { 1592 if (overwrite()) {
1583 const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( 1593 const auto res2 = Core::System::GetInstance()
1584 *nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy); 1594 .GetFileSystemController()
1595 .GetUserNANDContents()
1596 ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index),
1597 true, qt_raw_copy);
1585 if (res2 == FileSys::InstallResult::Success) { 1598 if (res2 == FileSys::InstallResult::Success) {
1586 success(); 1599 success();
1587 } else { 1600 } else {
@@ -1611,7 +1624,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
1611 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir 1624 FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
1612 : FileUtil::UserPath::NANDDir, 1625 : FileUtil::UserPath::NANDDir,
1613 dir_path.toStdString()); 1626 dir_path.toStdString());
1614 Service::FileSystem::CreateFactories(*vfs); 1627 Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
1615 game_list->PopulateAsync(UISettings::values.game_dirs); 1628 game_list->PopulateAsync(UISettings::values.game_dirs);
1616 } 1629 }
1617} 1630}
@@ -1996,7 +2009,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
1996 2009
1997 const auto function = [this, &keys, &pdm] { 2010 const auto function = [this, &keys, &pdm] {
1998 keys.PopulateFromPartitionData(pdm); 2011 keys.PopulateFromPartitionData(pdm);
1999 Service::FileSystem::CreateFactories(*vfs); 2012 Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
2000 keys.DeriveETicket(pdm); 2013 keys.DeriveETicket(pdm);
2001 }; 2014 };
2002 2015
@@ -2041,7 +2054,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
2041 prog.close(); 2054 prog.close();
2042 } 2055 }
2043 2056
2044 Service::FileSystem::CreateFactories(*vfs); 2057 Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
2045 2058
2046 if (behavior == ReinitializeKeyBehavior::Warning) { 2059 if (behavior == ReinitializeKeyBehavior::Warning) {
2047 game_list->PopulateAsync(UISettings::values.game_dirs); 2060 game_list->PopulateAsync(UISettings::values.game_dirs);
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 5cadfd191..d82438502 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -316,6 +316,29 @@ void Config::ReadValues() {
316 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir, 316 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
317 sdl2_config->Get("Data Storage", "sdmc_directory", 317 sdl2_config->Get("Data Storage", "sdmc_directory",
318 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir))); 318 FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
319 FileUtil::GetUserPath(FileUtil::UserPath::LoadDir,
320 sdl2_config->Get("Data Storage", "load_directory",
321 FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
322 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir,
323 sdl2_config->Get("Data Storage", "dump_directory",
324 FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
325 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir,
326 sdl2_config->Get("Data Storage", "cache_directory",
327 FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
328 Settings::values.gamecard_inserted =
329 sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false);
330 Settings::values.gamecard_current_game =
331 sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false);
332 Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", "");
333 Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(sdl2_config->GetInteger(
334 "Data Storage", "nand_total_size", static_cast<long>(Settings::NANDTotalSize::S29_1GB)));
335 Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(sdl2_config->GetInteger(
336 "Data Storage", "nand_user_size", static_cast<long>(Settings::NANDUserSize::S26GB)));
337 Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
338 sdl2_config->GetInteger("Data Storage", "nand_system_size",
339 static_cast<long>(Settings::NANDSystemSize::S2_5GB)));
340 Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(sdl2_config->GetInteger(
341 "Data Storage", "sdmc_size", static_cast<long>(Settings::SDMCSize::S16GB)));
319 342
320 // System 343 // System
321 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false); 344 Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index f9f244522..a6171c3ed 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -173,6 +173,20 @@ volume =
173# 1 (default): Yes, 0: No 173# 1 (default): Yes, 0: No
174use_virtual_sd = 174use_virtual_sd =
175 175
176# Whether or not to enable gamecard emulation
177# 1: Yes, 0 (default): No
178gamecard_inserted =
179
180# Whether or not the gamecard should be emulated as the current game
181# If 'gamecard_inserted' is 0 this setting is irrelevant
182# 1: Yes, 0 (default): No
183gamecard_current_game =
184
185# Path to an XCI file to use as the gamecard
186# If 'gamecard_inserted' is 0 this setting is irrelevant
187# If 'gamecard_current_game' is 1 this setting is irrelevant
188gamecard_path =
189
176[System] 190[System]
177# Whether the system is docked 191# Whether the system is docked
178# 1: Yes, 0 (default): No 192# 1: Yes, 0 (default): No
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 129d8ca73..bac05b959 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -184,7 +184,7 @@ int main(int argc, char** argv) {
184 Core::System& system{Core::System::GetInstance()}; 184 Core::System& system{Core::System::GetInstance()};
185 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>()); 185 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
186 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 186 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
187 Service::FileSystem::CreateFactories(*system.GetFilesystem()); 187 system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
188 188
189 SCOPE_EXIT({ system.Shutdown(); }); 189 SCOPE_EXIT({ system.Shutdown(); });
190 190
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 0ee97aa54..94ad50cb3 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -22,6 +22,7 @@
22#include "common/telemetry.h" 22#include "common/telemetry.h"
23#include "core/core.h" 23#include "core/core.h"
24#include "core/crypto/key_manager.h" 24#include "core/crypto/key_manager.h"
25#include "core/file_sys/registered_cache.h"
25#include "core/file_sys/vfs_real.h" 26#include "core/file_sys/vfs_real.h"
26#include "core/hle/service/filesystem/filesystem.h" 27#include "core/hle/service/filesystem/filesystem.h"
27#include "core/loader/loader.h" 28#include "core/loader/loader.h"
@@ -216,8 +217,9 @@ int main(int argc, char** argv) {
216 }; 217 };
217 218
218 Core::System& system{Core::System::GetInstance()}; 219 Core::System& system{Core::System::GetInstance()};
220 system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
219 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>()); 221 system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
220 Service::FileSystem::CreateFactories(*system.GetFilesystem()); 222 system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
221 223
222 SCOPE_EXIT({ system.Shutdown(); }); 224 SCOPE_EXIT({ system.Shutdown(); });
223 225