From 3a1899d143b6b50da6c1ed4fcc03390ef210df75 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:07:29 -0400
Subject: [PATCH 01/33] bis_factory: Add accessors for BIS partitions

---
 src/core/file_sys/bis_factory.cpp | 41 +++++++++++++++++++++++++++++++
 src/core/file_sys/bis_factory.h   | 20 +++++++++++++++
 2 files changed, 61 insertions(+)

diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index e29f70b3a6..70a04f6a09 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -39,4 +39,45 @@ VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const {
     return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id));
 }
 
+VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
+    switch (id) {
+    case BisPartitionId::CalibrationFile:
+        return GetOrCreateDirectoryRelative(nand_root, "/prodinfof");
+    case BisPartitionId::SafeMode:
+        return GetOrCreateDirectoryRelative(nand_root, "/safe");
+    case BisPartitionId::System:
+        return GetOrCreateDirectoryRelative(nand_root, "/system");
+    case BisPartitionId::User:
+        return GetOrCreateDirectoryRelative(nand_root, "/user");
+    default:
+        return nullptr;
+    }
+}
+
+VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
+    Core::Crypto::KeyManager keys;
+    Core::Crypto::PartitionDataManager pdm{
+        Core::System::GetInstance().GetFilesystem()->OpenDirectory(
+            FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)};
+    keys.PopulateFromPartitionData(pdm);
+
+    switch (id) {
+    case BisPartitionId::CalibrationBinary:
+        return pdm.GetDecryptedProdInfo();
+    case BisPartitionId::BootConfigAndPackage2Part1:
+    case BisPartitionId::BootConfigAndPackage2Part2:
+    case BisPartitionId::BootConfigAndPackage2Part3:
+    case BisPartitionId::BootConfigAndPackage2Part4:
+    case BisPartitionId::BootConfigAndPackage2Part5:
+    case BisPartitionId::BootConfigAndPackage2Part6: {
+        const auto new_id = static_cast<u8>(id) -
+                            static_cast<u8>(BisPartitionId::BootConfigAndPackage2Part1) +
+                            static_cast<u8>(Core::Crypto::Package2Type::NormalMain);
+        return pdm.GetPackage2Raw(static_cast<Core::Crypto::Package2Type>(new_id));
+    }
+    default:
+        return nullptr;
+    }
+}
+
 } // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 453c11ad27..f8413d4ef0 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -10,6 +10,23 @@
 
 namespace FileSys {
 
+enum class BisPartitionId : u32 {
+    UserDataRoot = 20,
+    CalibrationBinary = 27,
+    CalibrationFile = 28,
+    BootConfigAndPackage2Part1 = 21,
+    BootConfigAndPackage2Part2 = 22,
+    BootConfigAndPackage2Part3 = 23,
+    BootConfigAndPackage2Part4 = 24,
+    BootConfigAndPackage2Part5 = 25,
+    BootConfigAndPackage2Part6 = 26,
+    SafeMode = 29,
+    System = 31,
+    SystemProperEncryption = 32,
+    SystemProperPartition = 33,
+    User = 30,
+};
+
 class RegisteredCache;
 
 /// File system interface to the Built-In Storage
@@ -26,6 +43,9 @@ public:
     VirtualDir GetModificationLoadRoot(u64 title_id) const;
     VirtualDir GetModificationDumpRoot(u64 title_id) const;
 
+    VirtualDir OpenPartition(BisPartitionId id) const;
+    VirtualFile OpenPartitionStorage(BisPartitionId id) const;
+
 private:
     VirtualDir nand_root;
     VirtualDir load_root;

From 4dae5a52a832cc3c4679aba80fd8b15c56ded93a Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:08:39 -0400
Subject: [PATCH 02/33] bis_factory: Add accessors for BIS content directories

---
 src/core/file_sys/bis_factory.cpp | 8 ++++++++
 src/core/file_sys/bis_factory.h   | 3 +++
 2 files changed, 11 insertions(+)

diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 70a04f6a09..be737b9adc 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -18,6 +18,14 @@ BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir
 
 BISFactory::~BISFactory() = default;
 
+VirtualDir BISFactory::GetSystemNANDContentDirectory() const {
+    return GetOrCreateDirectoryRelative(nand_root, "/system/Contents");
+}
+
+VirtualDir BISFactory::GetUserNANDContentDirectory() const {
+    return GetOrCreateDirectoryRelative(nand_root, "/user/Contents");
+}
+
 RegisteredCache* BISFactory::GetSystemNANDContents() const {
     return sysnand_cache.get();
 }
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index f8413d4ef0..bfebceeaf5 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -37,6 +37,9 @@ public:
     explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root);
     ~BISFactory();
 
+    VirtualDir GetSystemNANDContentDirectory() const;
+    VirtualDir GetUserNANDContentDirectory() const;
+
     RegisteredCache* GetSystemNANDContents() const;
     RegisteredCache* GetUserNANDContents() const;
 

From 9bee8852829bfb99025397186d9d1b2e7dec9c30 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:09:25 -0400
Subject: [PATCH 03/33] bis_factory: Add accessor for NAND Image Directory

---
 src/core/file_sys/bis_factory.cpp | 4 ++++
 src/core/file_sys/bis_factory.h   | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index be737b9adc..b8967853f6 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -88,4 +88,8 @@ VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
     }
 }
 
+VirtualDir BISFactory::GetImageDirectory() const {
+    return GetOrCreateDirectoryRelative(nand_root, "/user/Album");
+}
+
 } // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index bfebceeaf5..38dcd28dcd 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -49,6 +49,8 @@ public:
     VirtualDir OpenPartition(BisPartitionId id) const;
     VirtualFile OpenPartitionStorage(BisPartitionId id) const;
 
+    VirtualDir GetImageDirectory() const;
+
 private:
     VirtualDir nand_root;
     VirtualDir load_root;

From b71bda45ae26695665bba45e7a3f84ae5a13d2b3 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:10:38 -0400
Subject: [PATCH 04/33] bis_factory: Add accessors for BIS placeholder caches

---
 src/core/file_sys/bis_factory.cpp | 14 +++++++++++++-
 src/core/file_sys/bis_factory.h   |  7 +++++++
 2 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index b8967853f6..cbeb2f73ad 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -14,7 +14,11 @@ BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir
       sysnand_cache(std::make_unique<RegisteredCache>(
           GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
       usrnand_cache(std::make_unique<RegisteredCache>(
-          GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {}
+          GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))),
+      sysnand_placeholder(std::make_unique<PlaceholderCache>(
+          GetOrCreateDirectoryRelative(nand_root, "/system/Contents/placehld"))),
+      usrnand_placeholder(std::make_unique<PlaceholderCache>(
+          GetOrCreateDirectoryRelative(nand_root, "/user/Contents/placehld"))) {}
 
 BISFactory::~BISFactory() = default;
 
@@ -34,6 +38,14 @@ RegisteredCache* BISFactory::GetUserNANDContents() const {
     return usrnand_cache.get();
 }
 
+PlaceholderCache* BISFactory::GetSystemNANDPlaceholder() const {
+    return sysnand_placeholder.get();
+}
+
+PlaceholderCache* BISFactory::GetUserNANDPlaceholder() const {
+    return usrnand_placeholder.get();
+}
+
 VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
     // LayeredFS doesn't work on updates and title id-less homebrew
     if (title_id == 0 || (title_id & 0x800) > 0)
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 38dcd28dcd..6229cd5a9f 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -28,6 +28,7 @@ enum class BisPartitionId : u32 {
 };
 
 class RegisteredCache;
+class PlaceholderCache;
 
 /// File system interface to the Built-In Storage
 /// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND
@@ -43,6 +44,9 @@ public:
     RegisteredCache* GetSystemNANDContents() const;
     RegisteredCache* GetUserNANDContents() const;
 
+    PlaceholderCache* GetSystemNANDPlaceholder() const;
+    PlaceholderCache* GetUserNANDPlaceholder() const;
+
     VirtualDir GetModificationLoadRoot(u64 title_id) const;
     VirtualDir GetModificationDumpRoot(u64 title_id) const;
 
@@ -58,6 +62,9 @@ private:
 
     std::unique_ptr<RegisteredCache> sysnand_cache;
     std::unique_ptr<RegisteredCache> usrnand_cache;
+
+    std::unique_ptr<PlaceholderCache> sysnand_placeholder;
+    std::unique_ptr<PlaceholderCache> usrnand_placeholder;
 };
 
 } // namespace FileSys

From 256a50ad15a1666a40c8aeff9759664f9e7a85a9 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:11:50 -0400
Subject: [PATCH 05/33] bis_factory: Fix mod loader edge-case with homebrew
 title IDs

Fixes a bug where homebrew that has a title ID with the update bit set can cause issues with the PatchManager
---
 src/core/file_sys/bis_factory.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index cbeb2f73ad..6cd0a98ebe 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -48,7 +48,7 @@ PlaceholderCache* BISFactory::GetUserNANDPlaceholder() const {
 
 VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
     // LayeredFS doesn't work on updates and title id-less homebrew
-    if (title_id == 0 || (title_id & 0x800) > 0)
+    if (title_id == 0 || (title_id & 0xFFF) == 0x800)
         return nullptr;
     return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
 }

From 8500ca797f430226c86edf694db6a96ee14a8333 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:21:51 -0400
Subject: [PATCH 06/33] registered_cache: Implement PlaceholderCache to manage
 placeholder and installing content

---
 src/core/file_sys/registered_cache.cpp | 150 +++++++++++++++++++++++++
 src/core/file_sys/registered_cache.h   |  25 +++++
 2 files changed, 175 insertions(+)

diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 3725b10f7c..93a20ab949 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -127,6 +127,156 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
     return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
 }
 
+PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {}
+
+bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
+    const auto path = GetRelativePathFromNcaID(id, false, true, false);
+
+    if (dir->GetFileRelative(path) != nullptr) {
+        return false;
+    }
+
+    Core::Crypto::SHA256Hash hash{};
+    mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
+    const auto dirname = fmt::format("000000{:02X}", hash[0]);
+
+    const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
+
+    if (dir2 == nullptr)
+        return false;
+
+    const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
+
+    if (file == nullptr)
+        return false;
+
+    return file->Resize(size);
+}
+
+bool PlaceholderCache::Delete(const NcaID& id) const {
+    const auto path = GetRelativePathFromNcaID(id, false, true, false);
+
+    if (dir->GetFileRelative(path) == nullptr) {
+        return false;
+    }
+
+    Core::Crypto::SHA256Hash hash{};
+    mbedtls_sha256(id.data(), id.size(), hash.data(), 0);
+    const auto dirname = fmt::format("000000{:02X}", hash[0]);
+
+    const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
+
+    const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
+
+    return res;
+}
+
+bool PlaceholderCache::Exists(const NcaID& id) const {
+    const auto path = GetRelativePathFromNcaID(id, false, true, false);
+
+    return dir->GetFileRelative(path) != nullptr;
+}
+
+bool PlaceholderCache::Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const {
+    const auto path = GetRelativePathFromNcaID(id, false, true, false);
+    const auto file = dir->GetFileRelative(path);
+
+    if (file == nullptr)
+        return false;
+
+    return file->WriteBytes(data, offset) == data.size();
+}
+
+bool PlaceholderCache::Register(RegisteredCache* cache, const NcaID& placeholder,
+                                const NcaID& install) const {
+    const auto path = GetRelativePathFromNcaID(placeholder, false, true, false);
+    const auto file = dir->GetFileRelative(path);
+
+    if (file == nullptr)
+        return false;
+
+    const auto res = cache->RawInstallNCA(NCA{file}, &VfsRawCopy, false, install);
+
+    if (res != InstallResult::Success)
+        return false;
+
+    return Delete(placeholder);
+}
+
+bool PlaceholderCache::CleanAll() const {
+    return dir->GetParentDirectory()->CleanSubdirectoryRecursive(dir->GetName());
+}
+
+std::optional<std::array<u8, 0x10>> PlaceholderCache::GetRightsID(const NcaID& id) const {
+    const auto path = GetRelativePathFromNcaID(id, false, true, false);
+    const auto file = dir->GetFileRelative(path);
+
+    if (file == nullptr)
+        return std::nullopt;
+
+    NCA nca{file};
+
+    if (nca.GetStatus() != Loader::ResultStatus::Success &&
+        nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
+        return std::nullopt;
+    }
+
+    const auto rights_id = nca.GetRightsId();
+    if (rights_id == NcaID{})
+        return std::nullopt;
+
+    return rights_id;
+}
+
+u64 PlaceholderCache::Size(const NcaID& id) const {
+    const auto path = GetRelativePathFromNcaID(id, false, true, false);
+    const auto file = dir->GetFileRelative(path);
+
+    if (file == nullptr)
+        return 0;
+
+    return file->GetSize();
+}
+
+bool PlaceholderCache::SetSize(const NcaID& id, u64 new_size) const {
+    const auto path = GetRelativePathFromNcaID(id, false, true, false);
+    const auto file = dir->GetFileRelative(path);
+
+    if (file == nullptr)
+        return false;
+
+    return file->Resize(new_size);
+}
+
+std::vector<NcaID> PlaceholderCache::List() const {
+    std::vector<NcaID> out;
+    for (const auto& sdir : dir->GetSubdirectories()) {
+        for (const auto& file : sdir->GetFiles()) {
+            const auto name = file->GetName();
+            if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' &&
+                name[35] == 'a') {
+                out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
+            }
+        }
+    }
+    return out;
+}
+
+NcaID PlaceholderCache::Generate() {
+    std::random_device device;
+    std::mt19937 gen(device());
+    std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max());
+
+    NcaID out{};
+
+    const auto v1 = distribution(gen);
+    const auto v2 = distribution(gen);
+    std::memcpy(out.data(), &v1, sizeof(u64));
+    std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64));
+
+    return out;
+}
+
 VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
                                                        std::string_view path) const {
     const auto file = dir->GetFileRelative(path);
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 4398d63e1f..d1eec240e7 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;
 enum class TitleType : u8;
 
 struct ContentRecord;
+struct MetaRecord;
+class RegisteredCache;
 
 using NcaID = std::array<u8, 0x10>;
 using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
@@ -89,6 +91,27 @@ protected:
     Core::Crypto::KeyManager keys;
 };
 
+class PlaceholderCache {
+public:
+    explicit PlaceholderCache(VirtualDir dir);
+
+    bool Create(const NcaID& id, u64 size) const;
+    bool Delete(const NcaID& id) const;
+    bool Exists(const NcaID& id) const;
+    bool Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const;
+    bool Register(RegisteredCache* cache, const NcaID& placeholder, const NcaID& install) const;
+    bool CleanAll() const;
+    std::optional<std::array<u8, 0x10>> GetRightsID(const NcaID& id) const;
+    u64 Size(const NcaID& id) const;
+    bool SetSize(const NcaID& id, u64 new_size) const;
+    std::vector<NcaID> List() const;
+
+    static NcaID Generate();
+
+private:
+    VirtualDir dir;
+};
+
 /*
  * A class that catalogues NCAs in the registered directory structure.
  * Nintendo's registered format follows this structure:
@@ -103,6 +126,8 @@ protected:
  * when 4GB splitting can be ignored.)
  */
 class RegisteredCache : public ContentProvider {
+    friend class PlaceholderCache;
+
 public:
     // Parsing function defines the conversion from raw file to NCA. If there are other steps
     // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom

From 9d9fc8a675ffa35998830a0a5670dd1ca288112f Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:24:44 -0400
Subject: [PATCH 07/33] registered_cache: Process *.cnmt.nca files

Needed to use the RegisteredCache/PlaceholderCache on gamecards.
---
 src/core/file_sys/registered_cache.cpp | 39 +++++++++++++++-----------
 1 file changed, 23 insertions(+), 16 deletions(-)

diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 93a20ab949..afa87be9c8 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -48,19 +48,22 @@ static bool FollowsTwoDigitDirFormat(std::string_view name) {
 static bool FollowsNcaIdFormat(std::string_view name) {
     static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript |
                                                                    std::regex_constants::icase);
-    return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex);
+    static const std::regex nca_id_cnmt_regex(
+        "[0-9A-F]{32}\\.cnmt.nca", std::regex_constants::ECMAScript | std::regex_constants::icase);
+    return (name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex)) ||
+           (name.size() == 41 && std::regex_match(name.begin(), name.end(), nca_id_cnmt_regex));
 }
 
 static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper,
-                                            bool within_two_digit) {
-    if (!within_two_digit) {
-        return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper));
-    }
+                                            bool within_two_digit, bool cnmt_suffix) {
+    if (!within_two_digit)
+        return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca",
+                           Common::HexArrayToString(nca_id, second_hex_upper));
 
     Core::Crypto::SHA256Hash hash{};
     mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
-    return fmt::format("/000000{:02X}/{}.nca", hash[0],
-                       Common::HexToString(nca_id, second_hex_upper));
+    return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0],
+                       Common::HexArrayToString(nca_id, second_hex_upper));
 }
 
 static std::string GetCNMTName(TitleType type, u64 title_id) {
@@ -319,14 +322,18 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
 
 VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
     VirtualFile file;
-    // Try all four modes of file storage:
-    // (bit 1 = uppercase/lower, bit 0 = within a two-digit dir)
-    // 00: /000000**/{:032X}.nca
-    // 01: /{:032X}.nca
-    // 10: /000000**/{:032x}.nca
-    // 11: /{:032x}.nca
-    for (u8 i = 0; i < 4; ++i) {
-        const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0);
+    // Try all five relevant modes of file storage:
+    // (bit 2 = uppercase/lower, bit 1 = within a two-digit dir, bit 0 = .cnmt suffix)
+    // 000: /000000**/{:032X}.nca
+    // 010: /{:032X}.nca
+    // 100: /000000**/{:032x}.nca
+    // 110: /{:032x}.nca
+    // 111: /{:032x}.cnmt.nca
+    for (u8 i = 0; i < 8; ++i) {
+        if ((i % 2) == 1 && i != 7)
+            continue;
+        const auto path =
+            GetRelativePathFromNcaID(id, (i & 0b100) == 0, (i & 0b010) == 0, (i & 0b001) == 0b001);
         file = OpenFileOrDirectoryConcat(dir, path);
         if (file != nullptr)
             return file;
@@ -622,7 +629,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti
         memcpy(id.data(), hash.data(), 16);
     }
 
-    std::string path = GetRelativePathFromNcaID(id, false, true);
+    std::string path = GetRelativePathFromNcaID(id, false, true, false);
 
     if (GetFileAtID(id) != nullptr && !overwrite_if_exists) {
         LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping...");

From 06db4d94fde745c39326fe5564b93348610a5674 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:28:32 -0400
Subject: [PATCH 08/33] patch_manager: Add error checking to load dir to
 prevent crashes

Prevents a crash if the load dir would be nullptr, instead logs an error and returns appropriately.
---
 src/core/file_sys/patch_manager.cpp | 15 +++++++++++++++
 1 file changed, 15 insertions(+)

diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index a8f80e2c63..0a4f0ea748 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -187,6 +187,11 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
     LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
 
     const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+    if (load_dir == nullptr) {
+        LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
+        return nso;
+    }
+
     auto patch_dirs = load_dir->GetSubdirectories();
     std::sort(patch_dirs.begin(), patch_dirs.end(),
               [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -225,6 +230,11 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
     LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
 
     const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+    if (load_dir == nullptr) {
+        LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
+        return false;
+    }
+
     auto patch_dirs = load_dir->GetSubdirectories();
     std::sort(patch_dirs.begin(), patch_dirs.end(),
               [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
@@ -259,6 +269,11 @@ static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& syst
 std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
                                                      const std::array<u8, 32>& build_id_) const {
     const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+    if (load_dir == nullptr) {
+        LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
+        return {};
+    }
+
     auto patch_dirs = load_dir->GetSubdirectories();
     std::sort(patch_dirs.begin(), patch_dirs.end(),
               [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });

From 62d772eaedbdd983574d2bfd0022fff58b55ca73 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:29:15 -0400
Subject: [PATCH 09/33] patch_manager: Add short-circuit edge-case to
 GetPatchVersionNames

If title ID is 0, there are no add ons, prevents wasting time looking for them.
---
 src/core/file_sys/patch_manager.cpp | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 0a4f0ea748..e3ebaca03c 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -408,6 +408,8 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
 
 std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
     VirtualFile update_raw) const {
+    if (title_id == 0)
+        return {};
     std::map<std::string, std::string, std::less<>> out;
     const auto& installed = Core::System::GetInstance().GetContentProvider();
     const auto& disabled = Settings::values.disabled_addons[title_id];

From 49c44e3faebe5912b71532a118f3bcd71bf73b4d Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:30:49 -0400
Subject: [PATCH 10/33] savedata_factory: Implement savedata creation and don't
 create dir on open

Matches hardware behavior and eliminates some nasty behavior we were doing that wasn't hw-accurate at all.
---
 src/core/file_sys/savedata_factory.cpp | 67 +++++++++++++++-----------
 src/core/file_sys/savedata_factory.h   |  1 +
 2 files changed, 41 insertions(+), 27 deletions(-)

diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index 7974b031d8..c638153322 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -15,22 +15,8 @@ namespace FileSys {
 
 constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size";
 
-std::string SaveDataDescriptor::DebugInfo() const {
-    return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, "
-                       "rank={}, index={}]",
-                       static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
-                       static_cast<u8>(rank), index);
-}
-
-SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
-    // Delete all temporary storages
-    // On hardware, it is expected that temporary storage be empty at first use.
-    dir->DeleteSubdirectoryRecursive("temp");
-}
-
-SaveDataFactory::~SaveDataFactory() = default;
-
-ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
+namespace {
+void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) {
     if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
         if (meta.zero_1 != 0) {
             LOG_WARNING(Service_FS,
@@ -65,23 +51,50 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDat
                     "non-zero ({:016X}{:016X})",
                     meta.user_id[1], meta.user_id[0]);
     }
+}
+} // Anonymous namespace
 
-    std::string save_directory =
+std::string SaveDataDescriptor::DebugInfo() const {
+    return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, "
+                       "save_id={:016X}, "
+                       "rank={}, index={}]",
+                       static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id,
+                       static_cast<u8>(rank), index);
+}
+
+SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {
+    // Delete all temporary storages
+    // On hardware, it is expected that temporary storage be empty at first use.
+    dir->DeleteSubdirectoryRecursive("temp");
+}
+
+SaveDataFactory::~SaveDataFactory() = default;
+
+ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
+                                              const SaveDataDescriptor& meta) {
+    PrintSaveDataDescriptorWarnings(meta);
+
+    const auto save_directory =
         GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
 
-    // TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods.
-    // But, user_ids don't match so this works for now.
+    auto out = dir->CreateDirectoryRelative(save_directory);
+
+    // Return an error if the save data doesn't actually exist.
+    if (out == nullptr) {
+        // TODO(DarkLordZach): Find out correct error code.
+        return ResultCode(-1);
+    }
+
+    return MakeResult<VirtualDir>(std::move(out));
+}
+
+ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
+
+    const auto save_directory =
+        GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
 
     auto out = dir->GetDirectoryRelative(save_directory);
 
-    if (out == nullptr) {
-        // TODO(bunnei): This is a work-around to always create a save data directory if it does not
-        // already exist. This is a hack, as we do not understand yet how this works on hardware.
-        // Without a save data directory, many games will assert on boot. This should not have any
-        // bad side-effects.
-        out = dir->CreateDirectoryRelative(save_directory);
-    }
-
     // Return an error if the save data doesn't actually exist.
     if (out == nullptr) {
         // TODO(Subv): Find out correct error code.
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index b736545714..738038ee09 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -64,6 +64,7 @@ public:
     explicit SaveDataFactory(VirtualDir dir);
     ~SaveDataFactory();
 
+    ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta);
     ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta);
 
     VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;

From 02b36b0eb564f60b751398f5e7e89e75e05c1c20 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:31:41 -0400
Subject: [PATCH 11/33] sdmc_factory: Add accessor for content directory

---
 src/core/file_sys/sdmc_factory.cpp | 4 ++++
 src/core/file_sys/sdmc_factory.h   | 3 +++
 2 files changed, 7 insertions(+)

diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index bd3a570588..e5668d70e3 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -22,6 +22,10 @@ ResultVal<VirtualDir> SDMCFactory::Open() {
     return MakeResult<VirtualDir>(dir);
 }
 
+VirtualDir SDMCFactory::GetSDMCContentDirectory() const {
+    return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents");
+}
+
 RegisteredCache* SDMCFactory::GetSDMCContents() const {
     return contents.get();
 }
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index 42794ba5b4..a98e44f855 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -19,6 +19,9 @@ public:
     ~SDMCFactory();
 
     ResultVal<VirtualDir> Open();
+
+    VirtualDir GetSDMCContentDirectory() const;
+
     RegisteredCache* GetSDMCContents() const;
 
 private:

From 0084cceb203544e306d7089bc81d81e20e1ddafa Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:32:19 -0400
Subject: [PATCH 12/33] sdmc_factory: Add accessor for SDMC PlaceholderCache

---
 src/core/file_sys/sdmc_factory.cpp | 8 +++++++-
 src/core/file_sys/sdmc_factory.h   | 3 +++
 2 files changed, 10 insertions(+), 1 deletion(-)

diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index e5668d70e3..d63d960cef 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -14,7 +14,9 @@ SDMCFactory::SDMCFactory(VirtualDir dir_)
                                 GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"),
                                 [](const VirtualFile& file, const NcaID& id) {
                                     return NAX{file, id}.GetDecrypted();
-                                })) {}
+                                })),
+      placeholder(std::make_unique<PlaceholderCache>(
+          GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {}
 
 SDMCFactory::~SDMCFactory() = default;
 
@@ -30,4 +32,8 @@ RegisteredCache* SDMCFactory::GetSDMCContents() const {
     return contents.get();
 }
 
+PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const {
+    return placeholder.get();
+}
+
 } // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index a98e44f855..2ef5909a62 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -11,6 +11,7 @@
 namespace FileSys {
 
 class RegisteredCache;
+class PlaceholderCache;
 
 /// File system interface to the SDCard archive
 class SDMCFactory {
@@ -23,11 +24,13 @@ public:
     VirtualDir GetSDMCContentDirectory() const;
 
     RegisteredCache* GetSDMCContents() const;
+    PlaceholderCache* GetSDMCPlaceholder() const;
 
 private:
     VirtualDir dir;
 
     std::unique_ptr<RegisteredCache> contents;
+    std::unique_ptr<PlaceholderCache> placeholder;
 };
 
 } // namespace FileSys

From 08c0783d345550c673763328d102095345940dcc Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:32:49 -0400
Subject: [PATCH 13/33] sdmc_factory: Add accessor for SDMC Album directory

---
 src/core/file_sys/sdmc_factory.cpp | 4 ++++
 src/core/file_sys/sdmc_factory.h   | 2 ++
 2 files changed, 6 insertions(+)

diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index d63d960cef..a5f1dcc5c8 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -36,4 +36,8 @@ PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const {
     return placeholder.get();
 }
 
+VirtualDir SDMCFactory::GetImageDirectory() const {
+    return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album");
+}
+
 } // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index 2ef5909a62..abe6cc41c2 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -26,6 +26,8 @@ public:
     RegisteredCache* GetSDMCContents() const;
     PlaceholderCache* GetSDMCPlaceholder() const;
 
+    VirtualDir GetImageDirectory() const;
+
 private:
     VirtualDir dir;
 

From 0a8e54068191c911806c7b825e4050afd65a794c Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:35:42 -0400
Subject: [PATCH 14/33] submisson_package: Fix edge case with improperly sized
 filenames

Prevents a crash if the filename is less than 9 characters long.
---
 src/core/file_sys/submission_package.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 730221fd65..ef30846810 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) {
 
 void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
     for (const auto& outer_file : files) {
-        if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
+        if (outer_file->GetName().size() < 9 ||
+            outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") {
             continue;
         }
 

From 8abbc619a1b452e2324f9482226d544fa9bcd3f2 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Tue, 16 Apr 2019 09:13:34 -0400
Subject: [PATCH 15/33] filesystem: Add FileSystemController to deglobalize FS
 services

---
 .../hle/service/filesystem/filesystem.cpp     | 309 ++++++++++++++++--
 src/core/hle/service/filesystem/filesystem.h  | 104 ++++--
 2 files changed, 357 insertions(+), 56 deletions(-)

diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 8ce110dd11..adc1917aa8 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -8,6 +8,7 @@
 #include "common/file_util.h"
 #include "core/core.h"
 #include "core/file_sys/bis_factory.h"
+#include "core/file_sys/card_image.h"
 #include "core/file_sys/control_metadata.h"
 #include "core/file_sys/errors.h"
 #include "core/file_sys/mode.h"
@@ -251,44 +252,35 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
     return FileSys::ERROR_PATH_NOT_FOUND;
 }
 
-/**
- * Map of registered file systems, identified by type. Once an file system is registered here, it
- * is never removed until UnregisterFileSystems is called.
- */
-static std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
-static std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
-static std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
-static std::unique_ptr<FileSys::BISFactory> bis_factory;
-
-ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
-    ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second RomFS");
+ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
     romfs_factory = std::move(factory);
     LOG_DEBUG(Service_FS, "Registered RomFS");
     return RESULT_SUCCESS;
 }
 
-ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
-    ASSERT_MSG(romfs_factory == nullptr, "Tried to register a second save data");
+ResultCode FileSystemController::RegisterSaveData(
+    std::unique_ptr<FileSys::SaveDataFactory>&& factory) {
+    ASSERT_MSG(save_data_factory == nullptr, "Tried to register a second save data");
     save_data_factory = std::move(factory);
     LOG_DEBUG(Service_FS, "Registered save data");
     return RESULT_SUCCESS;
 }
 
-ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
+ResultCode FileSystemController::RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory) {
     ASSERT_MSG(sdmc_factory == nullptr, "Tried to register a second SDMC");
     sdmc_factory = std::move(factory);
     LOG_DEBUG(Service_FS, "Registered SDMC");
     return RESULT_SUCCESS;
 }
 
-ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
+ResultCode FileSystemController::RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory) {
     ASSERT_MSG(bis_factory == nullptr, "Tried to register a second BIS");
     bis_factory = std::move(factory);
     LOG_DEBUG(Service_FS, "Registered BIS");
     return RESULT_SUCCESS;
 }
 
-void SetPackedUpdate(FileSys::VirtualFile update_raw) {
+void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) {
     LOG_TRACE(Service_FS, "Setting packed update for romfs");
 
     if (romfs_factory == nullptr)
@@ -297,7 +289,7 @@ void SetPackedUpdate(FileSys::VirtualFile update_raw) {
     romfs_factory->SetPackedUpdate(std::move(update_raw));
 }
 
-ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
+ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() {
     LOG_TRACE(Service_FS, "Opening RomFS for current process");
 
     if (romfs_factory == nullptr) {
@@ -308,8 +300,9 @@ ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() {
     return romfs_factory->OpenCurrentProcess();
 }
 
-ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
-                                          FileSys::ContentRecordType type) {
+ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(u64 title_id,
+                                                                FileSys::StorageId storage_id,
+                                                                FileSys::ContentRecordType type) {
     LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
               title_id, static_cast<u8>(storage_id), static_cast<u8>(type));
 
@@ -321,8 +314,20 @@ ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId stora
     return romfs_factory->Open(title_id, storage_id, type);
 }
 
-ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
-                                            const FileSys::SaveDataDescriptor& descriptor) {
+ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
+    FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) {
+    LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}",
+              static_cast<u8>(space), save_struct.DebugInfo());
+
+    if (save_data_factory == nullptr) {
+        return FileSys::ERROR_ENTITY_NOT_FOUND;
+    }
+
+    return save_data_factory->Create(space, save_struct);
+}
+
+ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
+    FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) {
     LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
               static_cast<u8>(space), descriptor.DebugInfo());
 
@@ -333,7 +338,8 @@ ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
     return save_data_factory->Open(space, descriptor);
 }
 
-ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) {
+ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace(
+    FileSys::SaveDataSpaceId space) {
     LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
 
     if (save_data_factory == nullptr) {
@@ -343,7 +349,7 @@ ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space)
     return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
 }
 
-ResultVal<FileSys::VirtualDir> OpenSDMC() {
+ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() {
     LOG_TRACE(Service_FS, "Opening SDMC");
 
     if (sdmc_factory == nullptr) {
@@ -353,7 +359,91 @@ ResultVal<FileSys::VirtualDir> OpenSDMC() {
     return sdmc_factory->Open();
 }
 
-FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id) {
+ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition(FileSys::BisPartitionId id) {
+    LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", static_cast<u32>(id));
+
+    if (bis_factory == nullptr) {
+        return FileSys::ERROR_ENTITY_NOT_FOUND;
+    }
+
+    auto part = bis_factory->OpenPartition(id);
+    if (part == nullptr) {
+        return FileSys::ERROR_INVALID_ARGUMENT;
+    }
+
+    return MakeResult<FileSys::VirtualDir>(std::move(part));
+}
+
+ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
+    FileSys::BisPartitionId id) {
+    LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", static_cast<u32>(id));
+
+    if (bis_factory == nullptr) {
+        return FileSys::ERROR_ENTITY_NOT_FOUND;
+    }
+
+    auto part = bis_factory->OpenPartitionStorage(id);
+    if (part == nullptr) {
+        return FileSys::ERROR_INVALID_ARGUMENT;
+    }
+
+    return MakeResult<FileSys::VirtualFile>(std::move(part));
+}
+
+u64 FileSystemController::GetFreeSpaceSize(FileSys::StorageId id) const {
+    switch (id) {
+    case FileSys::StorageId::None:
+    case FileSys::StorageId::GameCard:
+        return 0;
+    case FileSys::StorageId::SdCard:
+        if (sdmc_factory == nullptr)
+            return 0;
+        return sdmc_factory->GetSDMCFreeSpace();
+    case FileSys::StorageId::Host:
+        if (bis_factory == nullptr)
+            return 0;
+        return bis_factory->GetSystemNANDFreeSpace() + bis_factory->GetUserNANDFreeSpace();
+    case FileSys::StorageId::NandSystem:
+        if (bis_factory == nullptr)
+            return 0;
+        return bis_factory->GetSystemNANDFreeSpace();
+    case FileSys::StorageId::NandUser:
+        if (bis_factory == nullptr)
+            return 0;
+        return bis_factory->GetUserNANDFreeSpace();
+    }
+
+    return 0;
+}
+
+u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const {
+    switch (id) {
+    case FileSys::StorageId::None:
+    case FileSys::StorageId::GameCard:
+        return 0;
+    case FileSys::StorageId::SdCard:
+        if (sdmc_factory == nullptr)
+            return 0;
+        return sdmc_factory->GetSDMCTotalSpace();
+    case FileSys::StorageId::Host:
+        if (bis_factory == nullptr)
+            return 0;
+        return bis_factory->GetFullNANDTotalSpace();
+    case FileSys::StorageId::NandSystem:
+        if (bis_factory == nullptr)
+            return 0;
+        return bis_factory->GetSystemNANDTotalSpace();
+    case FileSys::StorageId::NandUser:
+        if (bis_factory == nullptr)
+            return 0;
+        return bis_factory->GetUserNANDTotalSpace();
+    }
+
+    return 0;
+}
+
+FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type,
+                                                             u64 title_id, u128 user_id) {
     if (save_data_factory == nullptr) {
         return {0, 0};
     }
@@ -385,13 +475,32 @@ FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
     return value;
 }
 
-void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
-                       FileSys::SaveDataSize new_value) {
+void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
+                                             FileSys::SaveDataSize new_value) {
     if (save_data_factory != nullptr)
         save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
 }
 
-FileSys::RegisteredCache* GetSystemNANDContents() {
+void FileSystemController::SetGameCard(FileSys::VirtualFile file) {
+    gamecard = std::make_unique<FileSys::XCI>(file);
+    const auto dir = gamecard->ConcatenatedPseudoDirectory();
+    gamecard_registered = std::make_unique<FileSys::RegisteredCache>(dir);
+    gamecard_placeholder = std::make_unique<FileSys::PlaceholderCache>(dir);
+}
+
+FileSys::XCI* FileSystemController::GetGameCard() {
+    return gamecard.get();
+}
+
+FileSys::RegisteredCache* FileSystemController::GetGameCardContents() {
+    return gamecard_registered.get();
+}
+
+FileSys::PlaceholderCache* FileSystemController::GetGameCardPlaceholder() {
+    return gamecard_placeholder.get();
+}
+
+FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() {
     LOG_TRACE(Service_FS, "Opening System NAND Contents");
 
     if (bis_factory == nullptr)
@@ -400,7 +509,7 @@ FileSys::RegisteredCache* GetSystemNANDContents() {
     return bis_factory->GetSystemNANDContents();
 }
 
-FileSys::RegisteredCache* GetUserNANDContents() {
+FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() {
     LOG_TRACE(Service_FS, "Opening User NAND Contents");
 
     if (bis_factory == nullptr)
@@ -409,7 +518,7 @@ FileSys::RegisteredCache* GetUserNANDContents() {
     return bis_factory->GetUserNANDContents();
 }
 
-FileSys::RegisteredCache* GetSDMCContents() {
+FileSys::RegisteredCache* FileSystemController::GetSDMCContents() {
     LOG_TRACE(Service_FS, "Opening SDMC Contents");
 
     if (sdmc_factory == nullptr)
@@ -418,7 +527,143 @@ FileSys::RegisteredCache* GetSDMCContents() {
     return sdmc_factory->GetSDMCContents();
 }
 
-FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
+FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() {
+    LOG_TRACE(Service_FS, "Opening System NAND Placeholder");
+
+    if (bis_factory == nullptr)
+        return nullptr;
+
+    return bis_factory->GetSystemNANDPlaceholder();
+}
+
+FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() {
+    LOG_TRACE(Service_FS, "Opening User NAND Placeholder");
+
+    if (bis_factory == nullptr)
+        return nullptr;
+
+    return bis_factory->GetUserNANDPlaceholder();
+}
+
+FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() {
+    LOG_TRACE(Service_FS, "Opening SDMC Placeholder");
+
+    if (sdmc_factory == nullptr)
+        return nullptr;
+
+    return sdmc_factory->GetSDMCPlaceholder();
+}
+
+FileSys::RegisteredCache* FileSystemController::GetRegisteredCacheForStorage(
+    FileSys::StorageId id) {
+    switch (id) {
+    case FileSys::StorageId::None:
+    case FileSys::StorageId::Host:
+        UNIMPLEMENTED();
+        return nullptr;
+    case FileSys::StorageId::GameCard:
+        return GetGameCardContents();
+    case FileSys::StorageId::NandSystem:
+        return GetSystemNANDContents();
+    case FileSys::StorageId::NandUser:
+        return GetUserNANDContents();
+    case FileSys::StorageId::SdCard:
+        return GetSDMCContents();
+    }
+
+    return nullptr;
+}
+
+FileSys::PlaceholderCache* FileSystemController::GetPlaceholderCacheForStorage(
+    FileSys::StorageId id) {
+    switch (id) {
+    case FileSys::StorageId::None:
+    case FileSys::StorageId::Host:
+        UNIMPLEMENTED();
+        return nullptr;
+    case FileSys::StorageId::GameCard:
+        return GetGameCardPlaceholder();
+    case FileSys::StorageId::NandSystem:
+        return GetSystemNANDPlaceholder();
+    case FileSys::StorageId::NandUser:
+        return GetUserNANDPlaceholder();
+    case FileSys::StorageId::SdCard:
+        return GetSDMCPlaceholder();
+    }
+
+    return nullptr;
+}
+
+FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() {
+    LOG_TRACE(Service_FS, "Opening system NAND content directory");
+
+    if (bis_factory == nullptr)
+        return nullptr;
+
+    return bis_factory->GetSystemNANDContentDirectory();
+}
+
+FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() {
+    LOG_TRACE(Service_FS, "Opening user NAND content directory");
+
+    if (bis_factory == nullptr)
+        return nullptr;
+
+    return bis_factory->GetUserNANDContentDirectory();
+}
+
+FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() {
+    LOG_TRACE(Service_FS, "Opening SDMC content directory");
+
+    if (sdmc_factory == nullptr)
+        return nullptr;
+
+    return sdmc_factory->GetSDMCContentDirectory();
+}
+
+FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() {
+    LOG_TRACE(Service_FS, "Opening NAND image directory");
+
+    if (bis_factory == nullptr)
+        return nullptr;
+
+    return bis_factory->GetImageDirectory();
+}
+
+FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() {
+    LOG_TRACE(Service_FS, "Opening SDMC image directory");
+
+    if (sdmc_factory == nullptr)
+        return nullptr;
+
+    return sdmc_factory->GetImageDirectory();
+}
+
+FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId id) {
+    switch (id) {
+    case ContentStorageId::System:
+        return GetSystemNANDContentDirectory();
+    case ContentStorageId::User:
+        return GetUserNANDContentDirectory();
+    case ContentStorageId::SdCard:
+        return GetSDMCContentDirectory();
+    }
+
+    return nullptr;
+}
+
+FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id) {
+    switch (id) {
+    case ImageDirectoryId::NAND:
+        return GetNANDImageDirectory();
+    case ImageDirectoryId::SdCard:
+        return GetSDMCImageDirectory();
+    }
+
+    return nullptr;
+}
+
+FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id) {
     LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);
 
     if (bis_factory == nullptr)
@@ -427,7 +672,7 @@ FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) {
     return bis_factory->GetModificationLoadRoot(title_id);
 }
 
-FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
+FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) {
     LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
 
     if (bis_factory == nullptr)
@@ -436,7 +681,7 @@ FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) {
     return bis_factory->GetModificationDumpRoot(title_id);
 }
 
-void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
+void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite) {
     if (overwrite) {
         bis_factory = nullptr;
         save_data_factory = nullptr;
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 3849dd89eb..aa4e437ace 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -14,10 +14,13 @@ namespace FileSys {
 class BISFactory;
 class RegisteredCache;
 class RegisteredCacheUnion;
+class PlaceholderCache;
 class RomFSFactory;
 class SaveDataFactory;
 class SDMCFactory;
+class XCI;
 
+enum class BisPartitionId : u32;
 enum class ContentRecordType : u8;
 enum class Mode : u32;
 enum class SaveDataSpaceId : u8;
@@ -36,36 +39,89 @@ class ServiceManager;
 
 namespace FileSystem {
 
-ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
-ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
-ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
-ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
+enum class ContentStorageId : u32 {
+    System,
+    User,
+    SdCard,
+};
 
-void SetPackedUpdate(FileSys::VirtualFile update_raw);
-ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
-ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
-                                          FileSys::ContentRecordType type);
-ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
-                                            const FileSys::SaveDataDescriptor& descriptor);
-ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
-ResultVal<FileSys::VirtualDir> OpenSDMC();
+enum class ImageDirectoryId : u32 {
+    NAND,
+    SdCard,
+};
 
-FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
-void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
-                       FileSys::SaveDataSize new_value);
+class FileSystemController {
+public:
+    ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
+    ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
+    ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
+    ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
 
-FileSys::RegisteredCache* GetSystemNANDContents();
-FileSys::RegisteredCache* GetUserNANDContents();
-FileSys::RegisteredCache* GetSDMCContents();
+    void SetPackedUpdate(FileSys::VirtualFile update_raw);
+    ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
+    ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
+                                              FileSys::ContentRecordType type);
+    ResultVal<FileSys::VirtualDir> CreateSaveData(FileSys::SaveDataSpaceId space,
+                                                  const FileSys::SaveDataDescriptor& save_struct);
+    ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
+                                                const FileSys::SaveDataDescriptor& save_struct);
+    ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
+    ResultVal<FileSys::VirtualDir> OpenSDMC();
+    ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id);
+    ResultVal<FileSys::VirtualFile> OpenBISPartitionStorage(FileSys::BisPartitionId id);
 
-FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
-FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
+    u64 GetFreeSpaceSize(FileSys::StorageId id) const;
+    u64 GetTotalSpaceSize(FileSys::StorageId id) const;
 
-// Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
-// above is called.
-void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
+    FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
+    void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
+                           FileSys::SaveDataSize new_value);
 
-void InstallInterfaces(Core::System& system);
+    void SetGameCard(FileSys::VirtualFile file);
+    FileSys::XCI* GetGameCard();
+
+    FileSys::RegisteredCache* GetSystemNANDContents();
+    FileSys::RegisteredCache* GetUserNANDContents();
+    FileSys::RegisteredCache* GetSDMCContents();
+    FileSys::RegisteredCache* GetGameCardContents();
+
+    FileSys::PlaceholderCache* GetSystemNANDPlaceholder();
+    FileSys::PlaceholderCache* GetUserNANDPlaceholder();
+    FileSys::PlaceholderCache* GetSDMCPlaceholder();
+    FileSys::PlaceholderCache* GetGameCardPlaceholder();
+
+    FileSys::RegisteredCache* GetRegisteredCacheForStorage(FileSys::StorageId id);
+    FileSys::PlaceholderCache* GetPlaceholderCacheForStorage(FileSys::StorageId id);
+
+    FileSys::VirtualDir GetSystemNANDContentDirectory();
+    FileSys::VirtualDir GetUserNANDContentDirectory();
+    FileSys::VirtualDir GetSDMCContentDirectory();
+
+    FileSys::VirtualDir GetNANDImageDirectory();
+    FileSys::VirtualDir GetSDMCImageDirectory();
+
+    FileSys::VirtualDir GetContentDirectory(ContentStorageId id);
+    FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id);
+
+    FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
+    FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
+
+    // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
+    // above is called.
+    void CreateFactories(FileSys::VfsFilesystem& vfs, bool overwrite = true);
+
+private:
+    std::unique_ptr<FileSys::RomFSFactory> romfs_factory;
+    std::unique_ptr<FileSys::SaveDataFactory> save_data_factory;
+    std::unique_ptr<FileSys::SDMCFactory> sdmc_factory;
+    std::unique_ptr<FileSys::BISFactory> bis_factory;
+
+    std::unique_ptr<FileSys::XCI> gamecard;
+    std::unique_ptr<FileSys::RegisteredCache> gamecard_registered;
+    std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder;
+};
+
+void InstallInterfaces(SM::ServiceManager& service_manager, FileSystemController& controller);
 
 // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
 // pointers and booleans. This makes using a VfsDirectory with switch services much easier and

From e47b57a90fb2123a2c4d98f4f990b61976c0ea1f Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Tue, 16 Apr 2019 14:27:34 -0400
Subject: [PATCH 16/33] bis_factory: Add getters for NAND partition sizes

---
 src/core/file_sys/bis_factory.cpp | 32 +++++++++++++++++++++++++++++++
 src/core/file_sys/bis_factory.h   |  6 ++++++
 2 files changed, 38 insertions(+)

diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 6cd0a98ebe..8f758d6d9d 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -3,8 +3,12 @@
 // Refer to the license.txt file included.
 
 #include <fmt/format.h>
+#include "common/file_util.h"
+#include "core/core.h"
 #include "core/file_sys/bis_factory.h"
+#include "core/file_sys/mode.h"
 #include "core/file_sys/registered_cache.h"
+#include "core/settings.h"
 
 namespace FileSys {
 
@@ -104,4 +108,32 @@ VirtualDir BISFactory::GetImageDirectory() const {
     return GetOrCreateDirectoryRelative(nand_root, "/user/Album");
 }
 
+u64 BISFactory::GetSystemNANDFreeSpace() const {
+    const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system");
+    if (sys_dir == nullptr)
+        return 0;
+
+    return GetSystemNANDTotalSpace() - sys_dir->GetSize();
+}
+
+u64 BISFactory::GetSystemNANDTotalSpace() const {
+    return static_cast<u64>(Settings::values.nand_system_size);
+}
+
+u64 BISFactory::GetUserNANDFreeSpace() const {
+    const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user");
+    if (usr_dir == nullptr)
+        return 0;
+
+    return GetUserNANDTotalSpace() - usr_dir->GetSize();
+}
+
+u64 BISFactory::GetUserNANDTotalSpace() const {
+    return static_cast<u64>(Settings::values.nand_user_size);
+}
+
+u64 BISFactory::GetFullNANDTotalSpace() const {
+    return static_cast<u64>(Settings::values.nand_total_size);
+}
+
 } // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 6229cd5a9f..bdfe728c9f 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -55,6 +55,12 @@ public:
 
     VirtualDir GetImageDirectory() const;
 
+    u64 GetSystemNANDFreeSpace() const;
+    u64 GetSystemNANDTotalSpace() const;
+    u64 GetUserNANDFreeSpace() const;
+    u64 GetUserNANDTotalSpace() const;
+    u64 GetFullNANDTotalSpace() const;
+
 private:
     VirtualDir nand_root;
     VirtualDir load_root;

From 721a92775d2da30843fc5e1d9887e55dafa0bc3a Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Tue, 16 Apr 2019 14:28:20 -0400
Subject: [PATCH 17/33] sdmc_factory: Add SD Card size getters

---
 src/core/file_sys/sdmc_factory.cpp | 9 +++++++++
 src/core/file_sys/sdmc_factory.h   | 3 +++
 2 files changed, 12 insertions(+)

diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index a5f1dcc5c8..743e2e63ad 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -6,6 +6,7 @@
 #include "core/file_sys/registered_cache.h"
 #include "core/file_sys/sdmc_factory.h"
 #include "core/file_sys/xts_archive.h"
+#include "core/settings.h"
 
 namespace FileSys {
 
@@ -40,4 +41,12 @@ VirtualDir SDMCFactory::GetImageDirectory() const {
     return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album");
 }
 
+u64 SDMCFactory::GetSDMCFreeSpace() const {
+    return GetSDMCTotalSpace() - dir->GetSize();
+}
+
+u64 SDMCFactory::GetSDMCTotalSpace() const {
+    return static_cast<u64>(Settings::values.sdmc_size);
+}
+
 } // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index abe6cc41c2..164fd94356 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -28,6 +28,9 @@ public:
 
     VirtualDir GetImageDirectory() const;
 
+    u64 GetSDMCFreeSpace() const;
+    u64 GetSDMCTotalSpace() const;
+
 private:
     VirtualDir dir;
 

From 43af31836ebe923f0bd34d85b74788e78d04b4e2 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Tue, 16 Apr 2019 14:32:18 -0400
Subject: [PATCH 18/33] filesystem: Pass Size Getter functions to IFileSystem
 for sizes

---
 .../hle/service/filesystem/filesystem.cpp     | 12 -------
 src/core/hle/service/filesystem/filesystem.h  |  6 ----
 src/core/hle/service/filesystem/fsp_srv.cpp   | 33 +++++++++++++++++--
 3 files changed, 31 insertions(+), 20 deletions(-)

diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index adc1917aa8..d8debf6126 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -29,11 +29,6 @@
 
 namespace Service::FileSystem {
 
-// Size of emulated sd card free space, reported in bytes.
-// Just using 32GB because thats reasonable
-// TODO(DarkLordZach): Eventually make this configurable in settings.
-constexpr u64 EMULATED_SD_REPORTED_SIZE = 32000000000;
-
 // A default size for normal/journal save data size if application control metadata cannot be found.
 // This should be large enough to satisfy even the most extreme requirements (~4.2GB)
 constexpr u64 SUFFICIENT_SAVE_DATA_SIZE = 0xF0000000;
@@ -227,13 +222,6 @@ ResultVal<FileSys::VirtualDir> VfsDirectoryServiceWrapper::OpenDirectory(const s
     return MakeResult(dir);
 }
 
-u64 VfsDirectoryServiceWrapper::GetFreeSpaceSize() const {
-    if (backing->IsWritable())
-        return EMULATED_SD_REPORTED_SIZE;
-
-    return 0;
-}
-
 ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
     const std::string& path_) const {
     std::string path(FileUtil::SanitizePath(path_));
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index aa4e437ace..2eb3a641d7 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -215,12 +215,6 @@ public:
      */
     ResultVal<FileSys::VirtualDir> OpenDirectory(const std::string& path);
 
-    /**
-     * Get the free space
-     * @return The number of free bytes in the archive
-     */
-    u64 GetFreeSpaceSize() const;
-
     /**
      * Get the type of the specified path
      * @return The type of the specified path or error code
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index d3cd46a9b8..85f8e4a633 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -30,6 +30,18 @@
 
 namespace Service::FileSystem {
 
+struct SizeGetter {
+    std::function<u64()> free;
+    std::function<u64()> total;
+
+    static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) {
+        return {
+            [&fsc, id] { return fsc.GetFreeSpaceSize(id); },
+            [&fsc, id] { return fsc.GetTotalSpaceSize(id); },
+        };
+    }
+};
+
 enum class FileSystemType : u8 {
     Invalid0 = 0,
     Invalid1 = 1,
@@ -289,8 +301,8 @@ private:
 
 class IFileSystem final : public ServiceFramework<IFileSystem> {
 public:
-    explicit IFileSystem(FileSys::VirtualDir backend)
-        : ServiceFramework("IFileSystem"), backend(std::move(backend)) {
+    explicit IFileSystem(FileSys::VirtualDir backend, SizeGetter size)
+        : ServiceFramework("IFileSystem"), backend(std::move(backend)), size(std::move(size)) {
         static const FunctionInfo functions[] = {
             {0, &IFileSystem::CreateFile, "CreateFile"},
             {1, &IFileSystem::DeleteFile, "DeleteFile"},
@@ -467,8 +479,25 @@ public:
         rb.Push(RESULT_SUCCESS);
     }
 
+    void GetFreeSpaceSize(Kernel::HLERequestContext& ctx) {
+        LOG_DEBUG(Service_FS, "called");
+
+        IPC::ResponseBuilder rb{ctx, 4};
+        rb.Push(RESULT_SUCCESS);
+        rb.Push(size.free());
+    }
+
+    void GetTotalSpaceSize(Kernel::HLERequestContext& ctx) {
+        LOG_DEBUG(Service_FS, "called");
+
+        IPC::ResponseBuilder rb{ctx, 4};
+        rb.Push(RESULT_SUCCESS);
+        rb.Push(size.total());
+    }
+
 private:
     VfsDirectoryServiceWrapper backend;
+    SizeGetter size;
 };
 
 class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {

From 1b04b7265301684530dd2298b5d148a05dc010c3 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 17 Apr 2019 11:59:07 -0400
Subject: [PATCH 19/33] am: Unstub IApplicationFunctions EnsureSaveData (20)

Creates a default save data for the application given a user ID.
---
 src/core/hle/service/am/am.cpp | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index aa2c839375..eae052ce0d 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1143,13 +1143,20 @@ void IApplicationFunctions::CreateApplicationAndRequestToStartForQuest(
 
 void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
-    u128 uid = rp.PopRaw<u128>(); // What does this do?
-    LOG_WARNING(Service, "(STUBBED) called uid = {:016X}{:016X}", uid[1], uid[0]);
+    u128 user_id = rp.PopRaw<u128>();
+
+    LOG_DEBUG(Service_AM, "called, uid={:016X}{:016X}", user_id[1], user_id[0]);
+
+    FileSys::SaveDataDescriptor descriptor{};
+    descriptor.title_id = Core::CurrentProcess()->GetTitleID();
+    descriptor.user_id = user_id;
+    descriptor.type = FileSys::SaveDataType::SaveData;
+    const auto res = fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, descriptor);
 
     IPC::ResponseBuilder rb{ctx, 4};
-    rb.Push(RESULT_SUCCESS);
+    rb.Push(res.Code());
     rb.Push<u64>(0);
-} // namespace Service::AM
+}
 
 void IApplicationFunctions::SetTerminateResult(Kernel::HLERequestContext& ctx) {
     // Takes an input u32 Result, no output.
@@ -1261,8 +1268,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
               "new_journal={:016X}",
               static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
 
-    const auto title_id = system.CurrentProcess()->GetTitleID();
-    FileSystem::WriteSaveDataSize(type, title_id, user_id, {new_normal_size, new_journal_size});
+    fsc.WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id,
+                          {new_normal_size, new_journal_size});
 
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(RESULT_SUCCESS);
@@ -1281,8 +1288,7 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
               user_id[1], user_id[0]);
 
-    const auto title_id = system.CurrentProcess()->GetTitleID();
-    const auto size = FileSystem::ReadSaveDataSize(type, title_id, user_id);
+    const auto size = fsc.ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id);
 
     IPC::ResponseBuilder rb{ctx, 6};
     rb.Push(RESULT_SUCCESS);

From 4b91057688d6c388f7cbb71e23024d97233ab472 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Mon, 22 Apr 2019 17:53:58 -0400
Subject: [PATCH 20/33] services: Pass FileSystemController as reference to
 services that need it

---
 src/core/hle/service/am/am.cpp        |  5 +++--
 src/core/hle/service/am/applet_ae.cpp |  2 +-
 src/core/hle/service/am/applet_ae.h   |  4 ++++
 src/core/hle/service/am/applet_oe.cpp |  2 +-
 src/core/hle/service/am/applet_oe.h   |  4 ++++
 src/core/hle/service/ns/ns.cpp        |  4 ++--
 src/core/hle/service/ns/ns.h          | 14 +++++++++++---
 src/core/hle/service/ns/pl_u.cpp      |  5 +++--
 src/core/hle/service/ns/pl_u.h        | 14 +++++++++++---
 src/core/hle/service/service.cpp      |  5 +++--
 src/core/hle/service/service.h        |  8 ++++----
 11 files changed, 47 insertions(+), 20 deletions(-)

diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index eae052ce0d..7f8514b0de 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1268,7 +1268,7 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
               "new_journal={:016X}",
               static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
 
-    fsc.WriteSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id,
+    fsc.WriteSaveDataSize(type, system.CurrentProcess()->GetTitleID(), user_id,
                           {new_normal_size, new_journal_size});
 
     IPC::ResponseBuilder rb{ctx, 4};
@@ -1288,7 +1288,8 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
               user_id[1], user_id[0]);
 
-    const auto size = fsc.ReadSaveDataSize(type, Core::CurrentProcess()->GetTitleID(), user_id);
+    const auto size = system.FileSystemController().ReadSaveDataSize(
+        type, system.CurrentProcess()->GetTitleID(), user_id);
 
     IPC::ResponseBuilder rb{ctx, 6};
     rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index e454b77d80..d04476ae0f 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -106,7 +106,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 0, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushIpcInterface<IApplicationFunctions>(system);
+        rb.PushIpcInterface<IApplicationFunctions>(system.FileSystemController());
     }
 
     std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
diff --git a/src/core/hle/service/am/applet_ae.h b/src/core/hle/service/am/applet_ae.h
index 9e006cd9d8..0e0d108586 100644
--- a/src/core/hle/service/am/applet_ae.h
+++ b/src/core/hle/service/am/applet_ae.h
@@ -9,6 +9,10 @@
 #include "core/hle/service/service.h"
 
 namespace Service {
+namespace FileSystem {
+class FileSystemController;
+}
+
 namespace NVFlinger {
 class NVFlinger;
 }
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index a2ffaa4408..2ee21620b5 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -95,7 +95,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 0, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushIpcInterface<IApplicationFunctions>(system);
+        rb.PushIpcInterface<IApplicationFunctions>(system.FileSystemController());
     }
 
     std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
diff --git a/src/core/hle/service/am/applet_oe.h b/src/core/hle/service/am/applet_oe.h
index 22c05419d7..99a65e7b50 100644
--- a/src/core/hle/service/am/applet_oe.h
+++ b/src/core/hle/service/am/applet_oe.h
@@ -9,6 +9,10 @@
 #include "core/hle/service/service.h"
 
 namespace Service {
+namespace FileSystem {
+class FileSystemController;
+}
+
 namespace NVFlinger {
 class NVFlinger;
 }
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index ce88a29415..13121c4f1c 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -617,7 +617,7 @@ public:
     }
 };
 
-void InstallInterfaces(SM::ServiceManager& service_manager) {
+void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc) {
     std::make_shared<NS>("ns:am2")->InstallAsService(service_manager);
     std::make_shared<NS>("ns:ec")->InstallAsService(service_manager);
     std::make_shared<NS>("ns:rid")->InstallAsService(service_manager);
@@ -628,7 +628,7 @@ void InstallInterfaces(SM::ServiceManager& service_manager) {
     std::make_shared<NS_SU>()->InstallAsService(service_manager);
     std::make_shared<NS_VM>()->InstallAsService(service_manager);
 
-    std::make_shared<PL_U>()->InstallAsService(service_manager);
+    std::make_shared<PL_U>(fsc)->InstallAsService(service_manager);
 }
 
 } // namespace Service::NS
diff --git a/src/core/hle/service/ns/ns.h b/src/core/hle/service/ns/ns.h
index 0e8256cb4a..d067e7a9a3 100644
--- a/src/core/hle/service/ns/ns.h
+++ b/src/core/hle/service/ns/ns.h
@@ -6,7 +6,13 @@
 
 #include "core/hle/service/service.h"
 
-namespace Service::NS {
+namespace Service {
+
+namespace FileSystem {
+class FileSystemController;
+} // namespace FileSystem
+
+namespace NS {
 
 class IAccountProxyInterface final : public ServiceFramework<IAccountProxyInterface> {
 public:
@@ -91,6 +97,8 @@ private:
 };
 
 /// Registers all NS services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, FileSystem::FileSystemController& fsc);
 
-} // namespace Service::NS
+} // namespace NS
+
+} // namespace Service
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 2a522136d0..9d49f36e8a 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 {
     std::vector<FontRegion> shared_font_regions;
 };
 
-PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
+PL_U::PL_U(FileSystem::FileSystemController& fsc)
+    : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
     static const FunctionInfo functions[] = {
         {0, &PL_U::RequestLoad, "RequestLoad"},
         {1, &PL_U::GetLoadState, "GetLoadState"},
@@ -161,7 +162,7 @@ PL_U::PL_U() : ServiceFramework("pl:u"), impl{std::make_unique<Impl>()} {
     };
     RegisterHandlers(functions);
     // Attempt to load shared font data from disk
-    const auto* nand = FileSystem::GetSystemNANDContents();
+    const auto* nand = fsc.GetSystemNANDContents();
     std::size_t offset = 0;
     // Rebuild shared fonts from data ncas
     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 253f26a2ac..35ca424d29 100644
--- a/src/core/hle/service/ns/pl_u.h
+++ b/src/core/hle/service/ns/pl_u.h
@@ -7,11 +7,17 @@
 #include <memory>
 #include "core/hle/service/service.h"
 
-namespace Service::NS {
+namespace Service {
+
+namespace FileSystem {
+class FileSystemController;
+} // namespace FileSystem
+
+namespace NS {
 
 class PL_U final : public ServiceFramework<PL_U> {
 public:
-    PL_U();
+    PL_U(FileSystem::FileSystemController& fsc);
     ~PL_U() override;
 
 private:
@@ -26,4 +32,6 @@ private:
     std::unique_ptr<Impl> impl;
 };
 
-} // namespace Service::NS
+} // namespace NS
+
+} // namespace Service
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 3a0f8c3f68..8bf033c885 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) {
     // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
     // here and pass it into the respective InstallInterfaces functions.
     auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming());
+    fsc.CreateFactories(*system.GetFilesystem(), false);
 
     SM::ServiceManager::InstallInterfaces(sm);
 
@@ -229,13 +230,13 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
     Migration::InstallInterfaces(*sm);
     Mii::InstallInterfaces(*sm);
     MM::InstallInterfaces(*sm);
-    NCM::InstallInterfaces(*sm);
+    NCM::InstallInterfaces(*sm, fsc);
     NFC::InstallInterfaces(*sm);
     NFP::InstallInterfaces(*sm);
     NIFM::InstallInterfaces(*sm);
     NIM::InstallInterfaces(*sm);
     NPNS::InstallInterfaces(*sm);
-    NS::InstallInterfaces(*sm);
+    NS::InstallInterfaces(*sm, fsc);
     Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
     PCIe::InstallInterfaces(*sm);
     PCTL::InstallInterfaces(*sm);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index c6c4bdae5a..aef9648615 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -18,10 +18,6 @@ namespace Core {
 class System;
 }
 
-namespace FileSys {
-class VfsFilesystem;
-}
-
 namespace Kernel {
 class ClientPort;
 class ServerPort;
@@ -31,6 +27,10 @@ class HLERequestContext;
 
 namespace Service {
 
+namespace FileSystem {
+class FileSystemController;
+} // namespace FileSystem
+
 namespace SM {
 class ServiceManager;
 }

From b4164d295b052cb7ba0962b443691a224e0a6598 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 10:22:38 -0400
Subject: [PATCH 21/33] partition_data_manager: Add accessor for decrypted
 PRODINFO partition

---
 src/core/crypto/partition_data_manager.cpp | 4 ++++
 src/core/crypto/partition_data_manager.h   | 1 +
 2 files changed, 5 insertions(+)

diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index 01a969be9f..594cd82c59 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) {
     prodinfo_decrypted = std::make_shared<XTSEncryptionLayer>(prodinfo, bis_key);
 }
 
+FileSys::VirtualFile PartitionDataManager::GetDecryptedProdInfo() const {
+    return prodinfo_decrypted;
+}
+
 std::array<u8, 576> PartitionDataManager::GetETicketExtendedKek() const {
     std::array<u8, 0x240> out{};
     if (prodinfo_decrypted != nullptr)
diff --git a/src/core/crypto/partition_data_manager.h b/src/core/crypto/partition_data_manager.h
index 0ad007c727..7a7b5d0389 100644
--- a/src/core/crypto/partition_data_manager.h
+++ b/src/core/crypto/partition_data_manager.h
@@ -84,6 +84,7 @@ public:
     bool HasProdInfo() const;
     FileSys::VirtualFile GetProdInfoRaw() const;
     void DecryptProdInfo(std::array<u8, 0x20> bis_key);
+    FileSys::VirtualFile GetDecryptedProdInfo() const;
     std::array<u8, 0x240> GetETicketExtendedKek() const;
 
 private:

From e018a48460ca35403e53f319eb5f89135475d81a Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 10:23:13 -0400
Subject: [PATCH 22/33] content_archive: Add accessors for Rights ID and SDK
 Version

---
 src/core/file_sys/content_archive.cpp | 8 ++++++++
 src/core/file_sys/content_archive.h   | 2 ++
 2 files changed, 10 insertions(+)

diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index ce5c69b415..ea5c92f618 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 {
     return header.title_id;
 }
 
+std::array<u8, 16> NCA::GetRightsId() const {
+    return header.rights_id;
+}
+
+u32 NCA::GetSDKVersion() const {
+    return header.sdk_version;
+}
+
 bool NCA::IsUpdate() const {
     return is_update;
 }
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 15b9e66248..e249079b5b 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -112,6 +112,8 @@ public:
 
     NCAContentType GetType() const;
     u64 GetTitleId() const;
+    std::array<u8, 0x10> GetRightsId() const;
+    u32 GetSDKVersion() const;
     bool IsUpdate() const;
 
     VirtualFile GetRomFS() const;

From 77f9ecd32b075fb8986103de5e446fa330210526 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:12:49 -0400
Subject: [PATCH 23/33] card_image: Add functions to query gamecard update
 partition

Includes version and meta title ID, used by fsp-srv/IDeviceOperator
---
 src/core/file_sys/card_image.cpp | 20 ++++++++++++++++++++
 src/core/file_sys/card_image.h   |  4 ++++
 2 files changed, 24 insertions(+)

diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 626ed0042e..9fb6e0178f 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -175,6 +175,26 @@ VirtualDir XCI::GetParentDirectory() const {
     return file->GetContainingDirectory();
 }
 
+VirtualDir XCI::ConcatenatedPseudoDirectory() {
+    const auto out = std::make_shared<VectorVfsDirectory>();
+    for (const auto& part_id : {XCIPartition::Normal, XCIPartition::Logo, XCIPartition::Secure}) {
+        const auto& part = GetPartition(part_id);
+        if (part == nullptr)
+            continue;
+
+        for (const auto& file : part->GetFiles())
+            out->AddFile(file);
+    }
+
+    return out;
+}
+
+std::array<u8, 0x200> XCI::GetCertificate() const {
+    std::array<u8, 0x200> out;
+    file->Read(out.data(), out.size(), GAMECARD_CERTIFICATE_OFFSET);
+    return out;
+}
+
 Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
     const auto partition_index = static_cast<std::size_t>(part);
     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 a350496f78..5a83acc1ce 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -91,6 +91,8 @@ public:
     VirtualDir GetLogoPartition() const;
 
     u64 GetProgramTitleID() const;
+    u32 GetSystemUpdateVersion();
+    u64 GetSystemUpdateTitleID() const;
 
     bool HasProgramNCA() const;
     VirtualFile GetProgramNCAFile() const;
@@ -120,6 +122,8 @@ private:
     std::shared_ptr<NCA> program;
     std::vector<std::shared_ptr<NCA>> ncas;
 
+    u64 update_normal_partition_end;
+
     Core::Crypto::KeyManager keys;
 };
 } // namespace FileSys

From ccaafaccfc924c6ac910775e546537c297ce2636 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Wed, 10 Apr 2019 12:13:27 -0400
Subject: [PATCH 24/33] card_image: Add accessors for gamecard certificate

Used by fsp-srv/IDeviceOperator
---
 src/core/file_sys/card_image.cpp | 4 ++++
 src/core/file_sys/card_image.h   | 5 +++++
 2 files changed, 9 insertions(+)

diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 9fb6e0178f..db54113a06 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -12,12 +12,16 @@
 #include "core/file_sys/content_archive.h"
 #include "core/file_sys/nca_metadata.h"
 #include "core/file_sys/partition_filesystem.h"
+#include "core/file_sys/romfs.h"
 #include "core/file_sys/submission_package.h"
+#include "core/file_sys/vfs_concat.h"
 #include "core/file_sys/vfs_offset.h"
+#include "core/file_sys/vfs_vector.h"
 #include "core/loader/loader.h"
 
 namespace FileSys {
 
+constexpr u64 GAMECARD_CERTIFICATE_OFFSET = 0x7000;
 constexpr std::array partition_names{
     "update",
     "normal",
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 5a83acc1ce..3e6b92ff3c 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -108,6 +108,11 @@ public:
 
     VirtualDir GetParentDirectory() const override;
 
+    // Creates a directory that contains all the NCAs in the gamecard
+    VirtualDir ConcatenatedPseudoDirectory();
+
+    std::array<u8, 0x200> GetCertificate() const;
+
 private:
     Loader::ResultStatus AddNCAFromPartition(XCIPartition part);
 

From faf628ad8deb3ed8926b6468d291ac4deda22676 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Mon, 22 Apr 2019 17:44:37 -0400
Subject: [PATCH 25/33] settings: Update LogSettings to show NAND/SDMC paths
 from FileUtil

---
 src/core/settings.cpp | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index 0dd1632ac2..7de3fd1e5b 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -2,6 +2,7 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include "common/file_util.h"
 #include "core/core.h"
 #include "core/gdbstub/gdbstub.h"
 #include "core/hle/service/hid/hid.h"
@@ -97,8 +98,8 @@ void LogSettings() {
     LogSetting("Audio_EnableAudioStretching", Settings::values.enable_audio_stretching);
     LogSetting("Audio_OutputDevice", Settings::values.audio_device_id);
     LogSetting("DataStorage_UseVirtualSd", Settings::values.use_virtual_sd);
-    LogSetting("DataStorage_NandDir", Settings::values.nand_dir);
-    LogSetting("DataStorage_SdmcDir", Settings::values.sdmc_dir);
+    LogSetting("DataStorage_NandDir", FileUtil::GetUserPath(FileUtil::UserPath::NANDDir));
+    LogSetting("DataStorage_SdmcDir", FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir));
     LogSetting("Debugging_UseGdbstub", Settings::values.use_gdbstub);
     LogSetting("Debugging_GdbstubPort", Settings::values.gdbstub_port);
     LogSetting("Debugging_ProgramArgs", Settings::values.program_args);

From c6ff4a6f4d05eb380616be57e4088003e7aedfcb Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Mon, 22 Apr 2019 17:56:56 -0400
Subject: [PATCH 26/33] yuzu: Port old usages of Filesystem namespace to
 FilesystemController

---
 src/core/file_sys/patch_manager.cpp           | 24 ++++++----
 src/core/file_sys/registered_cache.cpp        |  1 +
 src/core/file_sys/romfs_factory.cpp           | 11 +++--
 .../hle/service/filesystem/filesystem.cpp     |  1 +
 src/core/hle/service/filesystem/fsp_srv.cpp   | 46 +++++++++++++------
 src/core/hle/service/filesystem/fsp_srv.h     |  4 +-
 src/core/hle/service/service.cpp              |  2 +-
 .../loader/deconstructed_rom_directory.cpp    |  4 +-
 src/core/loader/nca.cpp                       |  4 +-
 src/core/loader/nro.cpp                       |  4 +-
 src/core/loader/nsp.cpp                       |  4 +-
 src/core/loader/xci.cpp                       |  4 +-
 src/yuzu/main.cpp                             | 41 +++++++++++------
 src/yuzu_cmd/yuzu.cpp                         |  2 +-
 14 files changed, 106 insertions(+), 46 deletions(-)

diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index e3ebaca03c..c1dd0c6d74 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 {
 
     if (Settings::values.dump_exefs) {
         LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
-        const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
+        const auto dump_dir =
+            Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
         if (dump_dir != nullptr) {
             const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
             VfsRawCopyD(exefs, exefs_dir);
@@ -88,7 +89,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
     }
 
     // LayeredExeFS
-    const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+    const auto load_dir =
+        Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
     if (load_dir != nullptr && load_dir->GetSize() > 0) {
         auto patch_dirs = load_dir->GetSubdirectories();
         std::sort(
@@ -174,7 +176,8 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
     if (Settings::values.dump_nso) {
         LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
                  title_id);
-        const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id);
+        const auto dump_dir =
+            Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
         if (dump_dir != nullptr) {
             const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
             const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
@@ -186,7 +189,8 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
 
     LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
 
-    const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+    const auto load_dir =
+        Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
     if (load_dir == nullptr) {
         LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
         return nso;
@@ -229,7 +233,8 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
 
     LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
 
-    const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+    const auto load_dir =
+        Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
     if (load_dir == nullptr) {
         LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
         return false;
@@ -268,7 +273,8 @@ static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& syst
 
 std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
                                                      const std::array<u8, 32>& build_id_) const {
-    const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+    const auto load_dir =
+        Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
     if (load_dir == nullptr) {
         LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
         return {};
@@ -299,7 +305,8 @@ std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system,
 }
 
 static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
-    const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+    const auto load_dir =
+        Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
     if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
         load_dir == nullptr || load_dir->GetSize() <= 0) {
         return;
@@ -440,7 +447,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
     }
 
     // General Mods (LayeredFS and IPS)
-    const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+    const auto mod_dir =
+        Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
     if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
         for (const auto& mod : mod_dir->GetSubdirectories()) {
             std::string types;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index afa87be9c8..d1ef1e72d2 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -3,6 +3,7 @@
 // Refer to the license.txt file included.
 
 #include <algorithm>
+#include <random>
 #include <regex>
 #include <mbedtls/sha256.h>
 #include "common/assert.h"
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index b2ccb29267..c3ee4a1582 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -7,6 +7,7 @@
 #include "common/common_types.h"
 #include "common/logging/log.h"
 #include "core/core.h"
+#include "core/file_sys/card_image.h"
 #include "core/file_sys/content_archive.h"
 #include "core/file_sys/nca_metadata.h"
 #include "core/file_sys/patch_manager.h"
@@ -51,13 +52,17 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte
         res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
         break;
     case StorageId::NandSystem:
-        res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type);
+        res =
+            Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry(
+                title_id, type);
         break;
     case StorageId::NandUser:
-        res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type);
+        res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry(
+            title_id, type);
         break;
     case StorageId::SdCard:
-        res = Service::FileSystem::GetSDMCContents()->GetEntry(title_id, type);
+        res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry(
+            title_id, type);
         break;
     default:
         UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index d8debf6126..76a860c6db 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -26,6 +26,7 @@
 #include "core/hle/service/filesystem/fsp_pr.h"
 #include "core/hle/service/filesystem/fsp_srv.h"
 #include "core/loader/loader.h"
+#include "core/settings.h"
 
 namespace Service::FileSystem {
 
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 85f8e4a633..525fda19f8 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -19,6 +19,7 @@
 #include "core/file_sys/mode.h"
 #include "core/file_sys/nca_metadata.h"
 #include "core/file_sys/patch_manager.h"
+#include "core/file_sys/romfs_factory.h"
 #include "core/file_sys/savedata_factory.h"
 #include "core/file_sys/system_archive/system_archive.h"
 #include "core/file_sys/vfs.h"
@@ -502,8 +503,8 @@ private:
 
 class ISaveDataInfoReader final : public ServiceFramework<ISaveDataInfoReader> {
 public:
-    explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space)
-        : ServiceFramework("ISaveDataInfoReader") {
+    explicit ISaveDataInfoReader(FileSys::SaveDataSpaceId space, FileSystemController& fsc)
+        : ServiceFramework("ISaveDataInfoReader"), fsc(fsc) {
         static const FunctionInfo functions[] = {
             {0, &ISaveDataInfoReader::ReadSaveDataInfo, "ReadSaveDataInfo"},
         };
@@ -549,8 +550,13 @@ private:
     }
 
     void FindAllSaves(FileSys::SaveDataSpaceId space) {
-        const auto save_root = OpenSaveDataSpace(space);
-        ASSERT(save_root.Succeeded());
+        const auto save_root = fsc.OpenSaveDataSpace(space);
+
+        if (save_root.Failed() || *save_root == nullptr) {
+            LOG_ERROR(Service_FS, "The save root for the space_id={:02X} was invalid!",
+                      static_cast<u8>(space));
+            return;
+        }
 
         for (const auto& type : (*save_root)->GetSubdirectories()) {
             if (type->GetName() == "save") {
@@ -639,11 +645,12 @@ private:
     };
     static_assert(sizeof(SaveDataInfo) == 0x60, "SaveDataInfo has incorrect size.");
 
+    FileSystemController& fsc;
     std::vector<SaveDataInfo> info;
     u64 next_entry_index = 0;
 };
 
-FSP_SRV::FSP_SRV(const Core::Reporter& reporter) : ServiceFramework("fsp-srv"), reporter(reporter) {
+FSP_SRV::FSP_SRV(FileSystemController& fsc) : ServiceFramework("fsp-srv"), fsc(fsc) {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, nullptr, "OpenFileSystem"},
@@ -783,7 +790,8 @@ void FSP_SRV::OpenFileSystemWithPatch(Kernel::HLERequestContext& ctx) {
 void FSP_SRV::OpenSdCardFileSystem(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_FS, "called");
 
-    IFileSystem filesystem(OpenSDMC().Unwrap());
+    IFileSystem filesystem(fsc.OpenSDMC().Unwrap(),
+                           SizeGetter::FromStorageId(fsc, FileSys::StorageId::SdCard));
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(RESULT_SUCCESS);
@@ -797,8 +805,10 @@ void FSP_SRV::CreateSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
     auto save_create_struct = rp.PopRaw<std::array<u8, 0x40>>();
     u128 uid = rp.PopRaw<u128>();
 
-    LOG_WARNING(Service_FS, "(STUBBED) called save_struct = {}, uid = {:016X}{:016X}",
-                save_struct.DebugInfo(), uid[1], uid[0]);
+    LOG_DEBUG(Service_FS, "called save_struct = {}, uid = {:016X}{:016X}", save_struct.DebugInfo(),
+              uid[1], uid[0]);
+
+    fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, save_struct);
 
     IPC::ResponseBuilder rb{ctx, 2};
     rb.Push(RESULT_SUCCESS);
@@ -815,14 +825,24 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
     IPC::RequestParser rp{ctx};
     const auto parameters = rp.PopRaw<Parameters>();
 
-    auto dir = OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
+    auto dir = fsc.OpenSaveData(parameters.save_data_space_id, parameters.descriptor);
     if (dir.Failed()) {
         IPC::ResponseBuilder rb{ctx, 2, 0, 0};
         rb.Push(FileSys::ERROR_ENTITY_NOT_FOUND);
         return;
     }
 
-    IFileSystem filesystem(std::move(dir.Unwrap()));
+    FileSys::StorageId id;
+    if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::NandUser) {
+        id = FileSys::StorageId::NandUser;
+    } else if (parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardSystem ||
+               parameters.save_data_space_id == FileSys::SaveDataSpaceId::SdCardUser) {
+        id = FileSys::StorageId::SdCard;
+    } else {
+        id = FileSys::StorageId::NandSystem;
+    }
+
+    IFileSystem filesystem(std::move(dir.Unwrap()), SizeGetter::FromStorageId(fsc, id));
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(RESULT_SUCCESS);
@@ -841,7 +861,7 @@ void FSP_SRV::OpenSaveDataInfoReaderBySaveDataSpaceId(Kernel::HLERequestContext&
 
     IPC::ResponseBuilder rb{ctx, 2, 0, 1};
     rb.Push(RESULT_SUCCESS);
-    rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space));
+    rb.PushIpcInterface<ISaveDataInfoReader>(std::make_shared<ISaveDataInfoReader>(space, fsc));
 }
 
 void FSP_SRV::SetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
@@ -865,7 +885,7 @@ void FSP_SRV::GetGlobalAccessLogMode(Kernel::HLERequestContext& ctx) {
 void FSP_SRV::OpenDataStorageByCurrentProcess(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_FS, "called");
 
-    auto romfs = OpenRomFSCurrentProcess();
+    auto romfs = fsc.OpenRomFSCurrentProcess();
     if (romfs.Failed()) {
         // TODO (bunnei): Find the right error code to use here
         LOG_CRITICAL(Service_FS, "no file system interface available!");
@@ -890,7 +910,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_FS, "called with storage_id={:02X}, unknown={:08X}, title_id={:016X}",
               static_cast<u8>(storage_id), unknown, title_id);
 
-    auto data = OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
+    auto data = fsc.OpenRomFS(title_id, storage_id, FileSys::ContentRecordType::Data);
 
     if (data.Failed()) {
         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 b5486a1932..494348598a 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 {
 
 class FSP_SRV final : public ServiceFramework<FSP_SRV> {
 public:
-    explicit FSP_SRV(const Core::Reporter& reporter);
+    explicit FSP_SRV(FileSystemController& fsc);
     ~FSP_SRV() override;
 
 private:
@@ -51,6 +51,8 @@ private:
     void OutputAccessLogToSdCard(Kernel::HLERequestContext& ctx);
     void GetAccessLogVersionInfo(Kernel::HLERequestContext& ctx);
 
+    FileSystemController& fsc;
+
     FileSys::VirtualFile romfs;
     u64 current_process_id = 0;
     u32 access_log_program_index = 0;
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 8bf033c885..3d6a5990fd 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -230,7 +230,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
     Migration::InstallInterfaces(*sm);
     Mii::InstallInterfaces(*sm);
     MM::InstallInterfaces(*sm);
-    NCM::InstallInterfaces(*sm, fsc);
+    NCM::InstallInterfaces(*sm);
     NFC::InstallInterfaces(*sm);
     NFP::InstallInterfaces(*sm);
     NIFM::InstallInterfaces(*sm);
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index f9e88be2bc..d19c3623cc 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -7,6 +7,7 @@
 #include "common/common_funcs.h"
 #include "common/file_util.h"
 #include "common/logging/log.h"
+#include "core/core.h"
 #include "core/file_sys/content_archive.h"
 #include "core/file_sys/control_metadata.h"
 #include "core/file_sys/patch_manager.h"
@@ -176,7 +177,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
     // Register the RomFS if a ".romfs" file was found
     if (romfs_iter != files.end() && *romfs_iter != nullptr) {
         romfs = *romfs_iter;
-        Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
+        Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
+            std::make_unique<FileSys::RomFSFactory>(*this));
     }
 
     is_loaded = true;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 0f65fb6377..5a04699780 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -6,6 +6,7 @@
 
 #include "common/file_util.h"
 #include "common/logging/log.h"
+#include "core/core.h"
 #include "core/file_sys/content_archive.h"
 #include "core/file_sys/romfs_factory.h"
 #include "core/hle/kernel/process.h"
@@ -57,7 +58,8 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
     }
 
     if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
-        Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
+        Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
+            std::make_unique<FileSys::RomFSFactory>(*this));
     }
 
     is_loaded = true;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 3a5361fdde..175898b916 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -10,6 +10,7 @@
 #include "common/file_util.h"
 #include "common/logging/log.h"
 #include "common/swap.h"
+#include "core/core.h"
 #include "core/file_sys/control_metadata.h"
 #include "core/file_sys/romfs_factory.h"
 #include "core/file_sys/vfs_offset.h"
@@ -214,7 +215,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
     }
 
     if (romfs != nullptr) {
-        Service::FileSystem::RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(*this));
+        Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
+            std::make_unique<FileSys::RomFSFactory>(*this));
     }
 
     is_loaded = true;
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 35c82c99d0..13950fc082 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -5,6 +5,7 @@
 #include <vector>
 
 #include "common/common_types.h"
+#include "core/core.h"
 #include "core/file_sys/card_image.h"
 #include "core/file_sys/content_archive.h"
 #include "core/file_sys/control_metadata.h"
@@ -105,7 +106,8 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
 
     FileSys::VirtualFile update_raw;
     if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
-        Service::FileSystem::SetPackedUpdate(std::move(update_raw));
+        Core::System::GetInstance().GetFileSystemController().SetPackedUpdate(
+            std::move(update_raw));
     }
 
     is_loaded = true;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 5e8553db9d..7186ad1ff1 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -5,6 +5,7 @@
 #include <vector>
 
 #include "common/common_types.h"
+#include "core/core.h"
 #include "core/file_sys/card_image.h"
 #include "core/file_sys/content_archive.h"
 #include "core/file_sys/control_metadata.h"
@@ -72,7 +73,8 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
 
     FileSys::VirtualFile update_raw;
     if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
-        Service::FileSystem::SetPackedUpdate(std::move(update_raw));
+        Core::System::GetInstance().GetFileSystemController().SetPackedUpdate(
+            std::move(update_raw));
     }
 
     is_loaded = true;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index 1dcfac2580..e84811a64a 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -221,7 +221,7 @@ GMainWindow::GMainWindow()
         std::make_unique<FileSys::ContentProviderUnion>());
     Core::System::GetInstance().RegisterContentProvider(
         FileSys::ContentProviderUnionSlot::FrontendManual, provider.get());
-    Service::FileSystem::CreateFactories(*vfs);
+    Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
 
     // Gen keys if necessary
     OnReinitializeKeys(ReinitializeKeyBehavior::NoWarning);
@@ -1507,15 +1507,19 @@ void GMainWindow::OnMenuInstallToNAND() {
             failed();
             return;
         }
-        const auto res =
-            Service::FileSystem::GetUserNANDContents()->InstallEntry(*nsp, false, qt_raw_copy);
+        const auto res = Core::System::GetInstance()
+                             .GetFileSystemController()
+                             .GetUserNANDContents()
+                             ->InstallEntry(*nsp, false, qt_raw_copy);
         if (res == FileSys::InstallResult::Success) {
             success();
         } else {
             if (res == FileSys::InstallResult::ErrorAlreadyExists) {
                 if (overwrite()) {
-                    const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
-                        *nsp, true, qt_raw_copy);
+                    const auto res2 = Core::System::GetInstance()
+                                          .GetFileSystemController()
+                                          .GetUserNANDContents()
+                                          ->InstallEntry(*nsp, true, qt_raw_copy);
                     if (res2 == FileSys::InstallResult::Success) {
                         success();
                     } else {
@@ -1569,19 +1573,28 @@ void GMainWindow::OnMenuInstallToNAND() {
 
         FileSys::InstallResult res;
         if (index >= static_cast<size_t>(FileSys::TitleType::Application)) {
-            res = Service::FileSystem::GetUserNANDContents()->InstallEntry(
-                *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
+            res = Core::System::GetInstance()
+                      .GetFileSystemController()
+                      .GetUserNANDContents()
+                      ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
+                                     qt_raw_copy);
         } else {
-            res = Service::FileSystem::GetSystemNANDContents()->InstallEntry(
-                *nca, static_cast<FileSys::TitleType>(index), false, qt_raw_copy);
+            res = Core::System::GetInstance()
+                      .GetFileSystemController()
+                      .GetSystemNANDContents()
+                      ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index), false,
+                                     qt_raw_copy);
         }
 
         if (res == FileSys::InstallResult::Success) {
             success();
         } else if (res == FileSys::InstallResult::ErrorAlreadyExists) {
             if (overwrite()) {
-                const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry(
-                    *nca, static_cast<FileSys::TitleType>(index), true, qt_raw_copy);
+                const auto res2 = Core::System::GetInstance()
+                                      .GetFileSystemController()
+                                      .GetUserNANDContents()
+                                      ->InstallEntry(*nca, static_cast<FileSys::TitleType>(index),
+                                                     true, qt_raw_copy);
                 if (res2 == FileSys::InstallResult::Success) {
                     success();
                 } else {
@@ -1611,7 +1624,7 @@ void GMainWindow::OnMenuSelectEmulatedDirectory(EmulatedDirectoryTarget target)
         FileUtil::GetUserPath(target == EmulatedDirectoryTarget::SDMC ? FileUtil::UserPath::SDMCDir
                                                                       : FileUtil::UserPath::NANDDir,
                               dir_path.toStdString());
-        Service::FileSystem::CreateFactories(*vfs);
+        Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
         game_list->PopulateAsync(UISettings::values.game_dirs);
     }
 }
@@ -1996,7 +2009,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
 
         const auto function = [this, &keys, &pdm] {
             keys.PopulateFromPartitionData(pdm);
-            Service::FileSystem::CreateFactories(*vfs);
+            Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
             keys.DeriveETicket(pdm);
         };
 
@@ -2041,7 +2054,7 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
         prog.close();
     }
 
-    Service::FileSystem::CreateFactories(*vfs);
+    Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
 
     if (behavior == ReinitializeKeyBehavior::Warning) {
         game_list->PopulateAsync(UISettings::values.game_dirs);
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 129d8ca73d..bac05b9592 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -184,7 +184,7 @@ int main(int argc, char** argv) {
     Core::System& system{Core::System::GetInstance()};
     system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
     system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
-    Service::FileSystem::CreateFactories(*system.GetFilesystem());
+    system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
 
     SCOPE_EXIT({ system.Shutdown(); });
 

From 918119ae1b67ac96d91945660d71b530475204bc Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Tue, 23 Apr 2019 08:34:19 -0400
Subject: [PATCH 27/33] settings: Add options for setting storage sizes

---
 src/core/settings.h               | 29 +++++++++++++++++++++++++++++
 src/yuzu/configuration/config.cpp | 20 +++++++++++++++++++-
 src/yuzu_cmd/config.cpp           |  9 +++++++++
 3 files changed, 57 insertions(+), 1 deletion(-)

diff --git a/src/core/settings.h b/src/core/settings.h
index d4b70ec4cf..51363fc516 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -346,6 +346,31 @@ struct TouchscreenInput {
     u32 rotation_angle;
 };
 
+enum class NANDTotalSize : u64 {
+    S29_1GB = 0x747C00000ULL,
+};
+
+enum class NANDUserSize : u64 {
+    S26GB = 0x680000000ULL,
+};
+
+enum class NANDSystemSize : u64 {
+    S2_5GB = 0xA0000000,
+};
+
+enum class SDMCSize : u64 {
+    S1GB = 0x40000000,
+    S2GB = 0x80000000,
+    S4GB = 0x100000000ULL,
+    S8GB = 0x200000000ULL,
+    S16GB = 0x400000000ULL,
+    S32GB = 0x800000000ULL,
+    S64GB = 0x1000000000ULL,
+    S128GB = 0x2000000000ULL,
+    S256GB = 0x4000000000ULL,
+    S1TB = 0x10000000000ULL,
+};
+
 struct Values {
     // System
     bool use_docked_mode;
@@ -384,6 +409,10 @@ struct Values {
     bool use_virtual_sd;
     std::string nand_dir;
     std::string sdmc_dir;
+    NANDTotalSize nand_total_size;
+    NANDSystemSize nand_system_size;
+    NANDUserSize nand_user_size;
+    SDMCSize sdmc_size;
 
     // Renderer
     float resolution_factor;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 3f54f54fb8..bcd0c0b1bb 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -459,6 +459,17 @@ void Config::ReadDataStorageValues() {
                     QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)))
             .toString()
             .toStdString());
+    Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
+        ReadSetting(QStringLiteral("nand_total_size"), static_cast<u64>(Settings::NANDTotalSize::S29_1GB))
+            .toULongLong());
+    Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
+        ReadSetting(QStringLiteral("nand_user_size"), static_cast<u64>(Settings::NANDUserSize::S26GB))
+            .toULongLong());
+    Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
+        ReadSetting(QStringLiteral("nand_system_size"), static_cast<u64>(Settings::NANDSystemSize::S2_5GB))
+            .toULongLong());
+    Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
+        ReadSetting(QStringLiteral("sdmc_size"), static_cast<u64>(Settings::SDMCSize::S16GB)).toULongLong());
 
     qt_config->endGroup();
 }
@@ -875,7 +886,14 @@ void Config::SaveDataStorageValues() {
     WriteSetting(QStringLiteral("sdmc_directory"),
                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
-
+    WriteSetting(QStringLiteral("nand_total_size"), static_cast<u64>(Settings::values.nand_total_size),
+                 static_cast<u64>(Settings::NANDTotalSize::S29_1GB));
+    WriteSetting(QStringLiteral("nand_user_size"), static_cast<u64>(Settings::values.nand_user_size),
+                 static_cast<u64>(Settings::NANDUserSize::S26GB));
+    WriteSetting(QStringLiteral("nand_system_size"), static_cast<u64>(Settings::values.nand_system_size),
+                 static_cast<u64>(Settings::NANDSystemSize::S2_5GB));
+    WriteSetting(QStringLiteral("sdmc_size"), static_cast<u64>(Settings::values.sdmc_size),
+                 static_cast<u64>(Settings::SDMCSize::S16GB));
     qt_config->endGroup();
 }
 
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 5cadfd1910..60e2b2d1b1 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -316,6 +316,15 @@ void Config::ReadValues() {
     FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
                           sdl2_config->Get("Data Storage", "sdmc_directory",
                                            FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
+    Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(sdl2_config->GetInteger(
+        "Data Storage", "nand_total_size", static_cast<long>(Settings::NANDTotalSize::S29_1GB)));
+    Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(sdl2_config->GetInteger(
+        "Data Storage", "nand_user_size", static_cast<long>(Settings::NANDUserSize::S26GB)));
+    Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
+        sdl2_config->GetInteger("Data Storage", "nand_system_size",
+                                static_cast<long>(Settings::NANDSystemSize::S2_5GB)));
+    Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(sdl2_config->GetInteger(
+        "Data Storage", "sdmc_size", static_cast<long>(Settings::SDMCSize::S16GB)));
 
     // System
     Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);

From 2590b5a9ea51f6868cac4322b2e4984d65656541 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Tue, 23 Apr 2019 08:35:12 -0400
Subject: [PATCH 28/33] settings: Add options for managing gamecard emulation

---
 src/core/settings.h               |  5 +++--
 src/yuzu/configuration/config.cpp | 36 +++++++++++++++++++++++++++++++
 src/yuzu_cmd/config.cpp           | 14 ++++++++++++
 src/yuzu_cmd/default_ini.h        | 14 ++++++++++++
 4 files changed, 67 insertions(+), 2 deletions(-)

diff --git a/src/core/settings.h b/src/core/settings.h
index 51363fc516..47bddfb30d 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -407,8 +407,9 @@ struct Values {
 
     // Data Storage
     bool use_virtual_sd;
-    std::string nand_dir;
-    std::string sdmc_dir;
+    bool gamecard_inserted;
+    bool gamecard_current_game;
+    std::string gamecard_path;
     NANDTotalSize nand_total_size;
     NANDSystemSize nand_system_size;
     NANDUserSize nand_user_size;
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index bcd0c0b1bb..8113060d05 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -459,6 +459,30 @@ void Config::ReadDataStorageValues() {
                     QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)))
             .toString()
             .toStdString());
+    FileUtil::GetUserPath(
+        FileUtil::UserPath::LoadDir,
+        qt_config
+            ->value("load_directory",
+                    QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)))
+            .toString()
+            .toStdString());
+    FileUtil::GetUserPath(
+        FileUtil::UserPath::DumpDir,
+        qt_config
+            ->value("dump_directory",
+                    QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)))
+            .toString()
+            .toStdString());
+    FileUtil::GetUserPath(
+        FileUtil::UserPath::CacheDir,
+        qt_config
+            ->value("cache_directory",
+                    QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)))
+            .toString()
+            .toStdString());
+    Settings::values.gamecard_inserted = ReadSetting("gamecard_inserted", false).toBool();
+    Settings::values.gamecard_current_game = ReadSetting("gamecard_current_game", false).toBool();
+    Settings::values.gamecard_path = ReadSetting("gamecard_path", "").toString().toStdString();
     Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
         ReadSetting(QStringLiteral("nand_total_size"), static_cast<u64>(Settings::NANDTotalSize::S29_1GB))
             .toULongLong());
@@ -886,6 +910,18 @@ void Config::SaveDataStorageValues() {
     WriteSetting(QStringLiteral("sdmc_directory"),
                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)),
                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
+    WriteSetting(QStringLiteral("load_directory"),
+                 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)),
+                 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
+    WriteSetting(QStringLiteral("dump_directory"),
+                 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)),
+                 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
+    WriteSetting(QStringLiteral("cache_directory"),
+                 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)),
+                 QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
+    WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false);
+    WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, false);
+    WriteSetting(QStringLiteral("gamecard_path"), QString::fromStdString(Settings::values.gamecard_path), "");
     WriteSetting(QStringLiteral("nand_total_size"), static_cast<u64>(Settings::values.nand_total_size),
                  static_cast<u64>(Settings::NANDTotalSize::S29_1GB));
     WriteSetting(QStringLiteral("nand_user_size"), static_cast<u64>(Settings::values.nand_user_size),
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 60e2b2d1b1..d82438502d 100644
--- a/src/yuzu_cmd/config.cpp
+++ b/src/yuzu_cmd/config.cpp
@@ -316,6 +316,20 @@ void Config::ReadValues() {
     FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
                           sdl2_config->Get("Data Storage", "sdmc_directory",
                                            FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
+    FileUtil::GetUserPath(FileUtil::UserPath::LoadDir,
+                          sdl2_config->Get("Data Storage", "load_directory",
+                                           FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
+    FileUtil::GetUserPath(FileUtil::UserPath::DumpDir,
+                          sdl2_config->Get("Data Storage", "dump_directory",
+                                           FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
+    FileUtil::GetUserPath(FileUtil::UserPath::CacheDir,
+                          sdl2_config->Get("Data Storage", "cache_directory",
+                                           FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
+    Settings::values.gamecard_inserted =
+        sdl2_config->GetBoolean("Data Storage", "gamecard_inserted", false);
+    Settings::values.gamecard_current_game =
+        sdl2_config->GetBoolean("Data Storage", "gamecard_current_game", false);
+    Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", "");
     Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(sdl2_config->GetInteger(
         "Data Storage", "nand_total_size", static_cast<long>(Settings::NANDTotalSize::S29_1GB)));
     Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(sdl2_config->GetInteger(
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index f9f2445226..e66d2f89b4 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -173,6 +173,20 @@ volume =
 # 1 (default): Yes, 0: No
 use_virtual_sd =
 
+# Whether or not to enable gamecard emulation
+# 1: Yes, 0 (default): No
+gamecard_inserted = 
+
+# Whether or not the gamecard should be emulated as the current game
+# If 'gamecard_inserted' is 0 this setting is irrelevant
+# 1: Yes, 0 (default): No
+gamecard_current_game = 
+
+# Path to an XCI file to use as the gamecard
+# If 'gamecard_inserted' is 0 this setting is irrelevant
+# If 'gamecard_current_game' is 1 this setting is irrelevant
+gamecard_path = 
+
 [System]
 # Whether the system is docked
 # 1: Yes, 0 (default): No

From c182688ad6e6c36be4a683e85bdcba05fdc92ba3 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Tue, 23 Apr 2019 08:35:33 -0400
Subject: [PATCH 29/33] core: Store FileSystemController in core

---
 src/core/core.cpp | 24 ++++++++++++++++++++++++
 src/core/core.h   |  8 ++++++++
 2 files changed, 32 insertions(+)

diff --git a/src/core/core.cpp b/src/core/core.cpp
index 9ab174de26..f22244cf7c 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -14,8 +14,13 @@
 #include "core/core_cpu.h"
 #include "core/core_timing.h"
 #include "core/cpu_core_manager.h"
+#include "core/file_sys/bis_factory.h"
+#include "core/file_sys/card_image.h"
 #include "core/file_sys/mode.h"
 #include "core/file_sys/registered_cache.h"
+#include "core/file_sys/romfs_factory.h"
+#include "core/file_sys/savedata_factory.h"
+#include "core/file_sys/sdmc_factory.h"
 #include "core/file_sys/vfs_concat.h"
 #include "core/file_sys/vfs_real.h"
 #include "core/gdbstub/gdbstub.h"
@@ -27,6 +32,7 @@
 #include "core/hle/kernel/thread.h"
 #include "core/hle/service/am/applets/applets.h"
 #include "core/hle/service/apm/controller.h"
+#include "core/hle/service/filesystem/filesystem.h"
 #include "core/hle/service/glue/manager.h"
 #include "core/hle/service/service.h"
 #include "core/hle/service/sm/sm.h"
@@ -202,6 +208,15 @@ struct System::Impl {
         main_process->Run(load_parameters->main_thread_priority,
                           load_parameters->main_thread_stack_size);
 
+        if (Settings::values.gamecard_inserted) {
+            if (Settings::values.gamecard_current_game) {
+                fs_controller.SetGameCard(GetGameFileFromPath(virtual_filesystem, filepath));
+            } else if (!Settings::values.gamecard_path.empty()) {
+                fs_controller.SetGameCard(
+                    GetGameFileFromPath(virtual_filesystem, Settings::values.gamecard_path));
+            }
+        }
+
         u64 title_id{0};
         if (app_loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
             LOG_ERROR(Core, "Failed to find title id for ROM (Error {})",
@@ -304,6 +319,7 @@ struct System::Impl {
     FileSys::VirtualFilesystem virtual_filesystem;
     /// ContentProviderUnion instance
     std::unique_ptr<FileSys::ContentProviderUnion> content_provider;
+    Service::FileSystem::FileSystemController fs_controller;
     /// AppLoader used to load the current executing application
     std::unique_ptr<Loader::AppLoader> app_loader;
     std::unique_ptr<VideoCore::RendererBase> renderer;
@@ -571,6 +587,14 @@ const FileSys::ContentProvider& System::GetContentProvider() const {
     return *impl->content_provider;
 }
 
+Service::FileSystem::FileSystemController& System::GetFileSystemController() {
+    return impl->fs_controller;
+}
+
+const Service::FileSystem::FileSystemController& System::GetFileSystemController() const {
+    return impl->fs_controller;
+}
+
 void System::RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
                                      FileSys::ContentProvider* provider) {
     impl->content_provider->SetSlot(slot, provider);
diff --git a/src/core/core.h b/src/core/core.h
index 0138d93b07..bb2962fdd7 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -47,6 +47,10 @@ namespace APM {
 class Controller;
 }
 
+namespace FileSystem {
+class FileSystemController;
+} // namespace FileSystem
+
 namespace Glue {
 class ARPManager;
 }
@@ -299,6 +303,10 @@ public:
 
     const FileSys::ContentProvider& GetContentProvider() const;
 
+    Service::FileSystem::FileSystemController& GetFileSystemController();
+
+    const Service::FileSystem::FileSystemController& GetFileSystemController() const;
+
     void RegisterContentProvider(FileSys::ContentProviderUnionSlot slot,
                                  FileSys::ContentProvider* provider);
 

From d4d38dd44d61a64a56a5bc5e7c0644dfbc17e128 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Tue, 23 Apr 2019 08:36:35 -0400
Subject: [PATCH 30/33] yuzu: Add UI to manage filesystem paths and sizes

---
 src/yuzu/CMakeLists.txt                       |   2 +
 src/yuzu/configuration/configure.ui           |  11 +
 src/yuzu/configuration/configure_dialog.cpp   |   4 +-
 .../configuration/configure_filesystem.cpp    | 173 ++++++++
 src/yuzu/configuration/configure_filesystem.h |  43 ++
 .../configuration/configure_filesystem.ui     | 395 ++++++++++++++++++
 6 files changed, 627 insertions(+), 1 deletion(-)
 create mode 100644 src/yuzu/configuration/configure_filesystem.cpp
 create mode 100644 src/yuzu/configuration/configure_filesystem.h
 create mode 100644 src/yuzu/configuration/configure_filesystem.ui

diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index f051e17b46..c50ca317d5 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -33,6 +33,8 @@ add_executable(yuzu
     configuration/configure_debug.ui
     configuration/configure_dialog.cpp
     configuration/configure_dialog.h
+    configuration/configure_filesystem.cpp
+    configuration/configure_filesystem.h
     configuration/configure_gamelist.cpp
     configuration/configure_gamelist.h
     configuration/configure_gamelist.ui
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 267717bc97..49fadd0ef7 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -63,6 +63,11 @@
          <string>Profiles</string>
         </attribute>
        </widget>
+       <widget class="ConfigureFilesystem" name="filesystemTab">
+        <attribute name="title">
+         <string>Filesystem</string>
+        </attribute>
+       </widget>
        <widget class="ConfigureInputSimple" name="inputTab">
         <attribute name="title">
          <string>Input</string>
@@ -125,6 +130,12 @@
    <header>configuration/configure_profile_manager.h</header>
    <container>1</container>
   </customwidget>
+  <customwidget>
+   <class>ConfigureFilesystem</class>
+   <extends>QWidget</extends>
+   <header>configuration/configure_filesystem.h</header>
+   <container>1</container>
+  </customwidget>
   <customwidget>
    <class>ConfigureAudio</class>
    <extends>QWidget</extends>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 775e3f2eac..7c875ae870 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -37,6 +37,7 @@ void ConfigureDialog::ApplyConfiguration() {
     ui->gameListTab->ApplyConfiguration();
     ui->systemTab->ApplyConfiguration();
     ui->profileManagerTab->ApplyConfiguration();
+    ui->filesystemTab->applyConfiguration();
     ui->inputTab->ApplyConfiguration();
     ui->hotkeysTab->ApplyConfiguration(registry);
     ui->graphicsTab->ApplyConfiguration();
@@ -73,7 +74,7 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
 void ConfigureDialog::PopulateSelectionList() {
     const std::array<std::pair<QString, QList<QWidget*>>, 4> items{
         {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->gameListTab}},
-         {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->audioTab}},
+         {tr("System"), {ui->systemTab, ui->profileManagerTab, ui->filesystemTab, ui->audioTab}},
          {tr("Graphics"), {ui->graphicsTab}},
          {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}},
     };
@@ -106,6 +107,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
         {ui->debugTab, tr("Debug")},
         {ui->webTab, tr("Web")},
         {ui->gameListTab, tr("Game List")},
+        {ui->filesystemTab, tr("Filesystem")},
     };
 
     [[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 0000000000..fabe86e0a3
--- /dev/null
+++ b/src/yuzu/configuration/configure_filesystem.cpp
@@ -0,0 +1,173 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QFileDialog>
+#include <QMessageBox>
+#include "common/common_paths.h"
+#include "common/file_util.h"
+#include "core/settings.h"
+#include "ui_configure_filesystem.h"
+#include "yuzu/configuration/configure_filesystem.h"
+#include "yuzu/ui_settings.h"
+
+namespace {
+
+template <typename T>
+void SetComboBoxFromData(QComboBox* combo_box, T data) {
+    const auto index = combo_box->findData(QVariant::fromValue(static_cast<u64>(data)));
+    if (index >= combo_box->count() || index < 0)
+        return;
+
+    combo_box->setCurrentIndex(index);
+}
+
+} // Anonymous namespace
+
+ConfigureFilesystem::ConfigureFilesystem(QWidget* parent)
+    : QWidget(parent), ui(std::make_unique<Ui::ConfigureFilesystem>()) {
+    ui->setupUi(this);
+    this->setConfiguration();
+
+    connect(ui->nand_directory_button, &QToolButton::pressed, this,
+            [this] { SetDirectory(DirectoryTarget::NAND, ui->nand_directory_edit); });
+    connect(ui->sdmc_directory_button, &QToolButton::pressed, this,
+            [this] { SetDirectory(DirectoryTarget::SD, ui->sdmc_directory_edit); });
+    connect(ui->gamecard_path_button, &QToolButton::pressed, this,
+            [this] { SetDirectory(DirectoryTarget::Gamecard, ui->gamecard_path_edit); });
+    connect(ui->dump_path_button, &QToolButton::pressed, this,
+            [this] { SetDirectory(DirectoryTarget::Dump, ui->dump_path_edit); });
+    connect(ui->load_path_button, &QToolButton::pressed, this,
+            [this] { SetDirectory(DirectoryTarget::Load, ui->load_path_edit); });
+    connect(ui->cache_directory_button, &QToolButton::pressed, this,
+            [this] { SetDirectory(DirectoryTarget::Cache, ui->cache_directory_edit); });
+
+    connect(ui->reset_game_list_cache, &QPushButton::pressed, this,
+            &ConfigureFilesystem::ResetMetadata);
+
+    connect(ui->gamecard_inserted, &QCheckBox::stateChanged, this,
+            &ConfigureFilesystem::UpdateEnabledControls);
+    connect(ui->gamecard_current_game, &QCheckBox::stateChanged, this,
+            &ConfigureFilesystem::UpdateEnabledControls);
+}
+
+ConfigureFilesystem::~ConfigureFilesystem() = default;
+
+void ConfigureFilesystem::setConfiguration() {
+    ui->nand_directory_edit->setText(
+        QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::NANDDir)));
+    ui->sdmc_directory_edit->setText(
+        QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir)));
+    ui->gamecard_path_edit->setText(QString::fromStdString(Settings::values.gamecard_path));
+    ui->dump_path_edit->setText(
+        QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)));
+    ui->load_path_edit->setText(
+        QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)));
+    ui->cache_directory_edit->setText(
+        QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
+
+    ui->gamecard_inserted->setChecked(Settings::values.gamecard_inserted);
+    ui->gamecard_current_game->setChecked(Settings::values.gamecard_current_game);
+    ui->dump_exefs->setChecked(Settings::values.dump_exefs);
+    ui->dump_nso->setChecked(Settings::values.dump_nso);
+
+    ui->cache_game_list->setChecked(UISettings::values.cache_game_list);
+
+    SetComboBoxFromData(ui->nand_size, Settings::values.nand_total_size);
+    SetComboBoxFromData(ui->usrnand_size, Settings::values.nand_user_size);
+    SetComboBoxFromData(ui->sysnand_size, Settings::values.nand_system_size);
+    SetComboBoxFromData(ui->sdmc_size, Settings::values.sdmc_size);
+
+    UpdateEnabledControls();
+}
+
+void ConfigureFilesystem::applyConfiguration() {
+    FileUtil::GetUserPath(FileUtil::UserPath::NANDDir,
+                          ui->nand_directory_edit->text().toStdString());
+    FileUtil::GetUserPath(FileUtil::UserPath::SDMCDir,
+                          ui->sdmc_directory_edit->text().toStdString());
+    FileUtil::GetUserPath(FileUtil::UserPath::DumpDir, ui->dump_path_edit->text().toStdString());
+    FileUtil::GetUserPath(FileUtil::UserPath::LoadDir, ui->load_path_edit->text().toStdString());
+    FileUtil::GetUserPath(FileUtil::UserPath::CacheDir,
+                          ui->cache_directory_edit->text().toStdString());
+    Settings::values.gamecard_path = ui->gamecard_path_edit->text().toStdString();
+
+    Settings::values.gamecard_inserted = ui->gamecard_inserted->isChecked();
+    Settings::values.gamecard_current_game = ui->gamecard_current_game->isChecked();
+    Settings::values.dump_exefs = ui->dump_exefs->isChecked();
+    Settings::values.dump_nso = ui->dump_nso->isChecked();
+
+    UISettings::values.cache_game_list = ui->cache_game_list->isChecked();
+
+    Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
+        ui->nand_size->itemData(ui->nand_size->currentIndex()).toULongLong());
+    Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
+        ui->nand_size->itemData(ui->sysnand_size->currentIndex()).toULongLong());
+    Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
+        ui->nand_size->itemData(ui->usrnand_size->currentIndex()).toULongLong());
+    Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
+        ui->nand_size->itemData(ui->sdmc_size->currentIndex()).toULongLong());
+}
+
+void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit) {
+    QString caption;
+
+    switch (target) {
+    case DirectoryTarget::NAND:
+        caption = tr("Select Emulated NAND Directory...");
+        break;
+    case DirectoryTarget::SD:
+        caption = tr("Select Emulated SD Directory...");
+        break;
+    case DirectoryTarget::Gamecard:
+        caption = tr("Select Gamecard Path...");
+        break;
+    case DirectoryTarget::Dump:
+        caption = tr("Select Dump Directory...");
+        break;
+    case DirectoryTarget::Load:
+        caption = tr("Select Mod Load Directory...");
+        break;
+    case DirectoryTarget::Cache:
+        caption = tr("Select Cache Directory...");
+        break;
+    }
+
+    QString str;
+    if (target == DirectoryTarget::Gamecard) {
+        str = QFileDialog::getOpenFileName(this, caption, QFileInfo(edit->text()).dir().path(),
+                                           "NX Gamecard;*.xci");
+    } else {
+        str = QFileDialog::getExistingDirectory(this, caption, edit->text());
+    }
+
+    if (str.isEmpty())
+        return;
+
+    edit->setText(str);
+}
+
+void ConfigureFilesystem::ResetMetadata() {
+    if (FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
+                                       DIR_SEP + "game_list")) {
+        QMessageBox::information(this, tr("Reset Metadata Cache"),
+                                 tr("The operation completed successfully."));
+        UISettings::values.is_game_list_reload_pending.exchange(true);
+    } else {
+        QMessageBox::warning(
+            this, tr("Reset Metadata Cache"),
+            tr("The metadata cache couldn't be deleted. It might be in use or non-existent."));
+    }
+}
+
+void ConfigureFilesystem::UpdateEnabledControls() {
+    ui->gamecard_current_game->setEnabled(ui->gamecard_inserted->isChecked());
+    ui->gamecard_path_edit->setEnabled(ui->gamecard_inserted->isChecked() &&
+                                       !ui->gamecard_current_game->isChecked());
+    ui->gamecard_path_button->setEnabled(ui->gamecard_inserted->isChecked() &&
+                                         !ui->gamecard_current_game->isChecked());
+}
+
+void ConfigureFilesystem::retranslateUi() {
+    ui->retranslateUi(this);
+}
diff --git a/src/yuzu/configuration/configure_filesystem.h b/src/yuzu/configuration/configure_filesystem.h
new file mode 100644
index 0000000000..a793037604
--- /dev/null
+++ b/src/yuzu/configuration/configure_filesystem.h
@@ -0,0 +1,43 @@
+// Copyright 2019 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QWidget>
+
+class QLineEdit;
+
+namespace Ui {
+class ConfigureFilesystem;
+}
+
+class ConfigureFilesystem : public QWidget {
+    Q_OBJECT
+
+public:
+    explicit ConfigureFilesystem(QWidget* parent = nullptr);
+    ~ConfigureFilesystem() override;
+
+    void applyConfiguration();
+    void retranslateUi();
+
+private:
+    void setConfiguration();
+
+    enum class DirectoryTarget {
+        NAND,
+        SD,
+        Gamecard,
+        Dump,
+        Load,
+        Cache,
+    };
+
+    void SetDirectory(DirectoryTarget target, QLineEdit* edit);
+    void ResetMetadata();
+    void UpdateEnabledControls();
+
+    std::unique_ptr<Ui::ConfigureFilesystem> ui;
+};
diff --git a/src/yuzu/configuration/configure_filesystem.ui b/src/yuzu/configuration/configure_filesystem.ui
new file mode 100644
index 0000000000..58cd07f527
--- /dev/null
+++ b/src/yuzu/configuration/configure_filesystem.ui
@@ -0,0 +1,395 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureFilesystem</class>
+ <widget class="QWidget" name="ConfigureFilesystem">
+  <property name="geometry">
+   <rect>
+    <x>0</x>
+    <y>0</y>
+    <width>453</width>
+    <height>561</height>
+   </rect>
+  </property>
+  <property name="windowTitle">
+   <string>Form</string>
+  </property>
+  <layout class="QVBoxLayout" name="verticalLayout">
+   <item>
+    <layout class="QVBoxLayout" name="verticalLayout_3">
+     <item>
+      <widget class="QGroupBox" name="groupBox">
+       <property name="title">
+        <string>Storage Directories</string>
+       </property>
+       <layout class="QGridLayout" name="gridLayout">
+        <item row="0" column="0">
+         <widget class="QLabel" name="label">
+          <property name="text">
+           <string>NAND</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="3">
+         <widget class="QToolButton" name="nand_directory_button">
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="2">
+         <widget class="QLineEdit" name="nand_directory_edit"/>
+        </item>
+        <item row="1" column="2">
+         <widget class="QLineEdit" name="sdmc_directory_edit"/>
+        </item>
+        <item row="1" column="0">
+         <widget class="QLabel" name="label_2">
+          <property name="text">
+           <string>SD Card</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="3">
+         <widget class="QToolButton" name="sdmc_directory_button">
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="1">
+         <spacer name="horizontalSpacer">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeType">
+           <enum>QSizePolicy::Maximum</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>60</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <widget class="QGroupBox" name="groupBox_2">
+       <property name="title">
+        <string>Gamecard</string>
+       </property>
+       <layout class="QGridLayout" name="gridLayout_2">
+        <item row="2" column="1">
+         <widget class="QLabel" name="label_3">
+          <property name="text">
+           <string>Path</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="2">
+         <widget class="QLineEdit" name="gamecard_path_edit"/>
+        </item>
+        <item row="0" column="1">
+         <widget class="QCheckBox" name="gamecard_inserted">
+          <property name="text">
+           <string>Inserted</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="1">
+         <widget class="QCheckBox" name="gamecard_current_game">
+          <property name="text">
+           <string>Current Game</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="3">
+         <widget class="QToolButton" name="gamecard_path_button">
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <widget class="QGroupBox" name="groupBox_3">
+       <property name="title">
+        <string>Storage Sizes</string>
+       </property>
+       <layout class="QGridLayout" name="gridLayout_3">
+        <item row="3" column="0">
+         <widget class="QLabel" name="label_5">
+          <property name="text">
+           <string>SD Card</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="0">
+         <widget class="QLabel" name="label_4">
+          <property name="text">
+           <string>System NAND</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="1">
+         <widget class="QComboBox" name="sysnand_size">
+          <item>
+           <property name="text">
+            <string>2.5 GB</string>
+           </property>
+          </item>
+         </widget>
+        </item>
+        <item row="3" column="1">
+         <widget class="QComboBox" name="sdmc_size">
+          <property name="currentText">
+           <string>32 GB</string>
+          </property>
+          <item>
+           <property name="text">
+            <string>1 GB</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>2 GB</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>4 GB</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>8 GB</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>16 GB</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>32 GB</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>64 GB</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>128 GB</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>256 GB</string>
+           </property>
+          </item>
+          <item>
+           <property name="text">
+            <string>1 TB</string>
+           </property>
+          </item>
+         </widget>
+        </item>
+        <item row="2" column="1">
+         <widget class="QComboBox" name="usrnand_size">
+          <item>
+           <property name="text">
+            <string>26 GB</string>
+           </property>
+          </item>
+         </widget>
+        </item>
+        <item row="2" column="0">
+         <widget class="QLabel" name="label_6">
+          <property name="text">
+           <string>User NAND</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="0">
+         <widget class="QLabel" name="label_7">
+          <property name="text">
+           <string>NAND</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="1">
+         <widget class="QComboBox" name="nand_size">
+          <item>
+           <property name="text">
+            <string>29.1 GB</string>
+           </property>
+          </item>
+         </widget>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <widget class="QGroupBox" name="groupBox_4">
+       <property name="title">
+        <string>Patch Manager</string>
+       </property>
+       <layout class="QGridLayout" name="gridLayout_4">
+        <item row="1" column="2">
+         <widget class="QLineEdit" name="load_path_edit"/>
+        </item>
+        <item row="0" column="2">
+         <widget class="QLineEdit" name="dump_path_edit"/>
+        </item>
+        <item row="0" column="3">
+         <widget class="QToolButton" name="dump_path_button">
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="3">
+         <widget class="QToolButton" name="load_path_button">
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+        <item row="2" column="0" colspan="4">
+         <layout class="QHBoxLayout" name="horizontalLayout">
+          <item>
+           <widget class="QCheckBox" name="dump_nso">
+            <property name="text">
+             <string>Dump Decompressed NSOs</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QCheckBox" name="dump_exefs">
+            <property name="text">
+             <string>Dump ExeFS</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+        <item row="1" column="0">
+         <widget class="QLabel" name="label_9">
+          <property name="text">
+           <string>Mod Load Root</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="0">
+         <widget class="QLabel" name="label_8">
+          <property name="text">
+           <string>Dump Root</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="1">
+         <spacer name="horizontalSpacer_2">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeType">
+           <enum>QSizePolicy::Fixed</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+       </layout>
+      </widget>
+     </item>
+     <item>
+      <widget class="QGroupBox" name="groupBox_5">
+       <property name="title">
+        <string>Caching</string>
+       </property>
+       <layout class="QGridLayout" name="gridLayout_5">
+        <item row="0" column="0">
+         <widget class="QLabel" name="label_10">
+          <property name="text">
+           <string>Cache Directory</string>
+          </property>
+         </widget>
+        </item>
+        <item row="0" column="1">
+         <spacer name="horizontalSpacer_3">
+          <property name="orientation">
+           <enum>Qt::Horizontal</enum>
+          </property>
+          <property name="sizeType">
+           <enum>QSizePolicy::Fixed</enum>
+          </property>
+          <property name="sizeHint" stdset="0">
+           <size>
+            <width>40</width>
+            <height>20</height>
+           </size>
+          </property>
+         </spacer>
+        </item>
+        <item row="0" column="2">
+         <widget class="QLineEdit" name="cache_directory_edit"/>
+        </item>
+        <item row="0" column="3">
+         <widget class="QToolButton" name="cache_directory_button">
+          <property name="text">
+           <string>...</string>
+          </property>
+         </widget>
+        </item>
+        <item row="1" column="0" colspan="4">
+         <layout class="QHBoxLayout" name="horizontalLayout_2">
+          <item>
+           <widget class="QCheckBox" name="cache_game_list">
+            <property name="text">
+             <string>Cache Game List Metadata</string>
+            </property>
+           </widget>
+          </item>
+          <item>
+           <widget class="QPushButton" name="reset_game_list_cache">
+            <property name="text">
+             <string>Reset Metadata Cache</string>
+            </property>
+           </widget>
+          </item>
+         </layout>
+        </item>
+       </layout>
+      </widget>
+     </item>
+    </layout>
+   </item>
+   <item>
+    <spacer name="verticalSpacer">
+     <property name="orientation">
+      <enum>Qt::Vertical</enum>
+     </property>
+     <property name="sizeHint" stdset="0">
+      <size>
+       <width>20</width>
+       <height>40</height>
+      </size>
+     </property>
+    </spacer>
+   </item>
+  </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>

From a49169e81906d230fd6bfc7546acc6f763f4c321 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Tue, 23 Apr 2019 14:38:18 -0400
Subject: [PATCH 31/33] filesystem: Add const qualification to various
 accessors

---
 src/core/file_sys/romfs_factory.cpp           |  5 +-
 src/core/file_sys/romfs_factory.h             |  4 +-
 src/core/file_sys/savedata_factory.cpp        |  7 +-
 src/core/file_sys/savedata_factory.h          |  7 +-
 src/core/file_sys/sdmc_factory.cpp            |  2 +-
 src/core/file_sys/sdmc_factory.h              |  2 +-
 .../hle/service/filesystem/filesystem.cpp     | 68 ++++++++++---------
 src/core/hle/service/filesystem/filesystem.h  | 68 ++++++++++---------
 src/core/hle/service/filesystem/fsp_srv.cpp   |  8 +--
 src/yuzu_cmd/default_ini.h                    |  6 +-
 10 files changed, 94 insertions(+), 83 deletions(-)

diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index c3ee4a1582..84cd4684c5 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -35,7 +35,7 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
     this->update_raw = std::move(update_raw);
 }
 
-ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
+ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const {
     if (!updatable)
         return MakeResult<VirtualFile>(file);
 
@@ -44,7 +44,8 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
         patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
 }
 
-ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) {
+ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
+                                          ContentRecordType type) const {
     std::shared_ptr<NCA> res;
 
     switch (storage) {
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index 7724c0b234..da63a313ad 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -33,8 +33,8 @@ public:
     ~RomFSFactory();
 
     void SetPackedUpdate(VirtualFile update_raw);
-    ResultVal<VirtualFile> OpenCurrentProcess();
-    ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
+    ResultVal<VirtualFile> OpenCurrentProcess() const;
+    ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const;
 
 private:
     VirtualFile file;
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index c638153322..f77cc02acb 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -71,7 +71,7 @@ SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save
 SaveDataFactory::~SaveDataFactory() = default;
 
 ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
-                                              const SaveDataDescriptor& meta) {
+                                              const SaveDataDescriptor& meta) const {
     PrintSaveDataDescriptorWarnings(meta);
 
     const auto save_directory =
@@ -88,7 +88,8 @@ ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space,
     return MakeResult<VirtualDir>(std::move(out));
 }
 
-ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) {
+ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space,
+                                            const SaveDataDescriptor& meta) const {
 
     const auto save_directory =
         GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id);
@@ -165,7 +166,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id,
 }
 
 void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
-                                        SaveDataSize new_value) {
+                                        SaveDataSize new_value) const {
     const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0);
     const auto dir = GetOrCreateDirectoryRelative(this->dir, path);
 
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index 738038ee09..991e57aa10 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -64,8 +64,8 @@ public:
     explicit SaveDataFactory(VirtualDir dir);
     ~SaveDataFactory();
 
-    ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta);
-    ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta);
+    ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
+    ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const;
 
     VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const;
 
@@ -74,7 +74,8 @@ public:
                                    u128 user_id, u64 save_id);
 
     SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const;
-    void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value);
+    void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id,
+                           SaveDataSize new_value) const;
 
 private:
     VirtualDir dir;
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index 743e2e63ad..5113a1ca64 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -21,7 +21,7 @@ SDMCFactory::SDMCFactory(VirtualDir dir_)
 
 SDMCFactory::~SDMCFactory() = default;
 
-ResultVal<VirtualDir> SDMCFactory::Open() {
+ResultVal<VirtualDir> SDMCFactory::Open() const {
     return MakeResult<VirtualDir>(dir);
 }
 
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index 164fd94356..42dc4e08a9 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -19,7 +19,7 @@ public:
     explicit SDMCFactory(VirtualDir dir);
     ~SDMCFactory();
 
-    ResultVal<VirtualDir> Open();
+    ResultVal<VirtualDir> Open() const;
 
     VirtualDir GetSDMCContentDirectory() const;
 
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 76a860c6db..31d5fd79b3 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -241,6 +241,10 @@ ResultVal<FileSys::EntryType> VfsDirectoryServiceWrapper::GetEntryType(
     return FileSys::ERROR_PATH_NOT_FOUND;
 }
 
+FileSystemController::FileSystemController() = default;
+
+FileSystemController::~FileSystemController() = default;
+
 ResultCode FileSystemController::RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory) {
     romfs_factory = std::move(factory);
     LOG_DEBUG(Service_FS, "Registered RomFS");
@@ -278,7 +282,7 @@ void FileSystemController::SetPackedUpdate(FileSys::VirtualFile update_raw) {
     romfs_factory->SetPackedUpdate(std::move(update_raw));
 }
 
-ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() {
+ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess() const {
     LOG_TRACE(Service_FS, "Opening RomFS for current process");
 
     if (romfs_factory == nullptr) {
@@ -289,9 +293,8 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFSCurrentProcess()
     return romfs_factory->OpenCurrentProcess();
 }
 
-ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(u64 title_id,
-                                                                FileSys::StorageId storage_id,
-                                                                FileSys::ContentRecordType type) {
+ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(
+    u64 title_id, FileSys::StorageId storage_id, FileSys::ContentRecordType type) const {
     LOG_TRACE(Service_FS, "Opening RomFS for title_id={:016X}, storage_id={:02X}, type={:02X}",
               title_id, static_cast<u8>(storage_id), static_cast<u8>(type));
 
@@ -304,7 +307,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenRomFS(u64 title_id,
 }
 
 ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
-    FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) {
+    FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const {
     LOG_TRACE(Service_FS, "Creating Save Data for space_id={:01X}, save_struct={}",
               static_cast<u8>(space), save_struct.DebugInfo());
 
@@ -316,7 +319,7 @@ ResultVal<FileSys::VirtualDir> FileSystemController::CreateSaveData(
 }
 
 ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
-    FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) {
+    FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& descriptor) const {
     LOG_TRACE(Service_FS, "Opening Save Data for space_id={:01X}, save_struct={}",
               static_cast<u8>(space), descriptor.DebugInfo());
 
@@ -328,7 +331,7 @@ ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveData(
 }
 
 ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace(
-    FileSys::SaveDataSpaceId space) {
+    FileSys::SaveDataSpaceId space) const {
     LOG_TRACE(Service_FS, "Opening Save Data Space for space_id={:01X}", static_cast<u8>(space));
 
     if (save_data_factory == nullptr) {
@@ -338,7 +341,7 @@ ResultVal<FileSys::VirtualDir> FileSystemController::OpenSaveDataSpace(
     return MakeResult(save_data_factory->GetSaveDataSpaceDirectory(space));
 }
 
-ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() {
+ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() const {
     LOG_TRACE(Service_FS, "Opening SDMC");
 
     if (sdmc_factory == nullptr) {
@@ -348,7 +351,8 @@ ResultVal<FileSys::VirtualDir> FileSystemController::OpenSDMC() {
     return sdmc_factory->Open();
 }
 
-ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition(FileSys::BisPartitionId id) {
+ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition(
+    FileSys::BisPartitionId id) const {
     LOG_TRACE(Service_FS, "Opening BIS Partition with id={:08X}", static_cast<u32>(id));
 
     if (bis_factory == nullptr) {
@@ -364,7 +368,7 @@ ResultVal<FileSys::VirtualDir> FileSystemController::OpenBISPartition(FileSys::B
 }
 
 ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
-    FileSys::BisPartitionId id) {
+    FileSys::BisPartitionId id) const {
     LOG_TRACE(Service_FS, "Opening BIS Partition Storage with id={:08X}", static_cast<u32>(id));
 
     if (bis_factory == nullptr) {
@@ -432,7 +436,7 @@ u64 FileSystemController::GetTotalSpaceSize(FileSys::StorageId id) const {
 }
 
 FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataType type,
-                                                             u64 title_id, u128 user_id) {
+                                                             u64 title_id, u128 user_id) const {
     if (save_data_factory == nullptr) {
         return {0, 0};
     }
@@ -465,7 +469,7 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy
 }
 
 void FileSystemController::WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
-                                             FileSys::SaveDataSize new_value) {
+                                             FileSys::SaveDataSize new_value) const {
     if (save_data_factory != nullptr)
         save_data_factory->WriteSaveDataSize(type, title_id, user_id, new_value);
 }
@@ -477,19 +481,19 @@ void FileSystemController::SetGameCard(FileSys::VirtualFile file) {
     gamecard_placeholder = std::make_unique<FileSys::PlaceholderCache>(dir);
 }
 
-FileSys::XCI* FileSystemController::GetGameCard() {
+FileSys::XCI* FileSystemController::GetGameCard() const {
     return gamecard.get();
 }
 
-FileSys::RegisteredCache* FileSystemController::GetGameCardContents() {
+FileSys::RegisteredCache* FileSystemController::GetGameCardContents() const {
     return gamecard_registered.get();
 }
 
-FileSys::PlaceholderCache* FileSystemController::GetGameCardPlaceholder() {
+FileSys::PlaceholderCache* FileSystemController::GetGameCardPlaceholder() const {
     return gamecard_placeholder.get();
 }
 
-FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() {
+FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() const {
     LOG_TRACE(Service_FS, "Opening System NAND Contents");
 
     if (bis_factory == nullptr)
@@ -498,7 +502,7 @@ FileSys::RegisteredCache* FileSystemController::GetSystemNANDContents() {
     return bis_factory->GetSystemNANDContents();
 }
 
-FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() {
+FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() const {
     LOG_TRACE(Service_FS, "Opening User NAND Contents");
 
     if (bis_factory == nullptr)
@@ -507,7 +511,7 @@ FileSys::RegisteredCache* FileSystemController::GetUserNANDContents() {
     return bis_factory->GetUserNANDContents();
 }
 
-FileSys::RegisteredCache* FileSystemController::GetSDMCContents() {
+FileSys::RegisteredCache* FileSystemController::GetSDMCContents() const {
     LOG_TRACE(Service_FS, "Opening SDMC Contents");
 
     if (sdmc_factory == nullptr)
@@ -516,7 +520,7 @@ FileSys::RegisteredCache* FileSystemController::GetSDMCContents() {
     return sdmc_factory->GetSDMCContents();
 }
 
-FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() {
+FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() const {
     LOG_TRACE(Service_FS, "Opening System NAND Placeholder");
 
     if (bis_factory == nullptr)
@@ -525,7 +529,7 @@ FileSys::PlaceholderCache* FileSystemController::GetSystemNANDPlaceholder() {
     return bis_factory->GetSystemNANDPlaceholder();
 }
 
-FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() {
+FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() const {
     LOG_TRACE(Service_FS, "Opening User NAND Placeholder");
 
     if (bis_factory == nullptr)
@@ -534,7 +538,7 @@ FileSys::PlaceholderCache* FileSystemController::GetUserNANDPlaceholder() {
     return bis_factory->GetUserNANDPlaceholder();
 }
 
-FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() {
+FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() const {
     LOG_TRACE(Service_FS, "Opening SDMC Placeholder");
 
     if (sdmc_factory == nullptr)
@@ -544,7 +548,7 @@ FileSys::PlaceholderCache* FileSystemController::GetSDMCPlaceholder() {
 }
 
 FileSys::RegisteredCache* FileSystemController::GetRegisteredCacheForStorage(
-    FileSys::StorageId id) {
+    FileSys::StorageId id) const {
     switch (id) {
     case FileSys::StorageId::None:
     case FileSys::StorageId::Host:
@@ -564,7 +568,7 @@ FileSys::RegisteredCache* FileSystemController::GetRegisteredCacheForStorage(
 }
 
 FileSys::PlaceholderCache* FileSystemController::GetPlaceholderCacheForStorage(
-    FileSys::StorageId id) {
+    FileSys::StorageId id) const {
     switch (id) {
     case FileSys::StorageId::None:
     case FileSys::StorageId::Host:
@@ -583,7 +587,7 @@ FileSys::PlaceholderCache* FileSystemController::GetPlaceholderCacheForStorage(
     return nullptr;
 }
 
-FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() {
+FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() const {
     LOG_TRACE(Service_FS, "Opening system NAND content directory");
 
     if (bis_factory == nullptr)
@@ -592,7 +596,7 @@ FileSys::VirtualDir FileSystemController::GetSystemNANDContentDirectory() {
     return bis_factory->GetSystemNANDContentDirectory();
 }
 
-FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() {
+FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() const {
     LOG_TRACE(Service_FS, "Opening user NAND content directory");
 
     if (bis_factory == nullptr)
@@ -601,7 +605,7 @@ FileSys::VirtualDir FileSystemController::GetUserNANDContentDirectory() {
     return bis_factory->GetUserNANDContentDirectory();
 }
 
-FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() {
+FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() const {
     LOG_TRACE(Service_FS, "Opening SDMC content directory");
 
     if (sdmc_factory == nullptr)
@@ -610,7 +614,7 @@ FileSys::VirtualDir FileSystemController::GetSDMCContentDirectory() {
     return sdmc_factory->GetSDMCContentDirectory();
 }
 
-FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() {
+FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() const {
     LOG_TRACE(Service_FS, "Opening NAND image directory");
 
     if (bis_factory == nullptr)
@@ -619,7 +623,7 @@ FileSys::VirtualDir FileSystemController::GetNANDImageDirectory() {
     return bis_factory->GetImageDirectory();
 }
 
-FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() {
+FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() const {
     LOG_TRACE(Service_FS, "Opening SDMC image directory");
 
     if (sdmc_factory == nullptr)
@@ -628,7 +632,7 @@ FileSys::VirtualDir FileSystemController::GetSDMCImageDirectory() {
     return sdmc_factory->GetImageDirectory();
 }
 
-FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId id) {
+FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId id) const {
     switch (id) {
     case ContentStorageId::System:
         return GetSystemNANDContentDirectory();
@@ -641,7 +645,7 @@ FileSys::VirtualDir FileSystemController::GetContentDirectory(ContentStorageId i
     return nullptr;
 }
 
-FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id) {
+FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id) const {
     switch (id) {
     case ImageDirectoryId::NAND:
         return GetNANDImageDirectory();
@@ -652,7 +656,7 @@ FileSys::VirtualDir FileSystemController::GetImageDirectory(ImageDirectoryId id)
     return nullptr;
 }
 
-FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id) {
+FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id) const {
     LOG_TRACE(Service_FS, "Opening mod load root for tid={:016X}", title_id);
 
     if (bis_factory == nullptr)
@@ -661,7 +665,7 @@ FileSys::VirtualDir FileSystemController::GetModificationLoadRoot(u64 title_id)
     return bis_factory->GetModificationLoadRoot(title_id);
 }
 
-FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) {
+FileSys::VirtualDir FileSystemController::GetModificationDumpRoot(u64 title_id) const {
     LOG_TRACE(Service_FS, "Opening mod dump root for tid={:016X}", title_id);
 
     if (bis_factory == nullptr)
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 2eb3a641d7..4d7da3b6f3 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -52,59 +52,63 @@ enum class ImageDirectoryId : u32 {
 
 class FileSystemController {
 public:
+    FileSystemController();
+    ~FileSystemController();
+
     ResultCode RegisterRomFS(std::unique_ptr<FileSys::RomFSFactory>&& factory);
     ResultCode RegisterSaveData(std::unique_ptr<FileSys::SaveDataFactory>&& factory);
     ResultCode RegisterSDMC(std::unique_ptr<FileSys::SDMCFactory>&& factory);
     ResultCode RegisterBIS(std::unique_ptr<FileSys::BISFactory>&& factory);
 
     void SetPackedUpdate(FileSys::VirtualFile update_raw);
-    ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess();
+    ResultVal<FileSys::VirtualFile> OpenRomFSCurrentProcess() const;
     ResultVal<FileSys::VirtualFile> OpenRomFS(u64 title_id, FileSys::StorageId storage_id,
-                                              FileSys::ContentRecordType type);
-    ResultVal<FileSys::VirtualDir> CreateSaveData(FileSys::SaveDataSpaceId space,
-                                                  const FileSys::SaveDataDescriptor& save_struct);
-    ResultVal<FileSys::VirtualDir> OpenSaveData(FileSys::SaveDataSpaceId space,
-                                                const FileSys::SaveDataDescriptor& save_struct);
-    ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space);
-    ResultVal<FileSys::VirtualDir> OpenSDMC();
-    ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id);
-    ResultVal<FileSys::VirtualFile> OpenBISPartitionStorage(FileSys::BisPartitionId id);
+                                              FileSys::ContentRecordType type) const;
+    ResultVal<FileSys::VirtualDir> CreateSaveData(
+        FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
+    ResultVal<FileSys::VirtualDir> OpenSaveData(
+        FileSys::SaveDataSpaceId space, const FileSys::SaveDataDescriptor& save_struct) const;
+    ResultVal<FileSys::VirtualDir> OpenSaveDataSpace(FileSys::SaveDataSpaceId space) const;
+    ResultVal<FileSys::VirtualDir> OpenSDMC() const;
+    ResultVal<FileSys::VirtualDir> OpenBISPartition(FileSys::BisPartitionId id) const;
+    ResultVal<FileSys::VirtualFile> OpenBISPartitionStorage(FileSys::BisPartitionId id) const;
 
     u64 GetFreeSpaceSize(FileSys::StorageId id) const;
     u64 GetTotalSpaceSize(FileSys::StorageId id) const;
 
-    FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id);
+    FileSys::SaveDataSize ReadSaveDataSize(FileSys::SaveDataType type, u64 title_id,
+                                           u128 user_id) const;
     void WriteSaveDataSize(FileSys::SaveDataType type, u64 title_id, u128 user_id,
-                           FileSys::SaveDataSize new_value);
+                           FileSys::SaveDataSize new_value) const;
 
     void SetGameCard(FileSys::VirtualFile file);
-    FileSys::XCI* GetGameCard();
+    FileSys::XCI* GetGameCard() const;
 
-    FileSys::RegisteredCache* GetSystemNANDContents();
-    FileSys::RegisteredCache* GetUserNANDContents();
-    FileSys::RegisteredCache* GetSDMCContents();
-    FileSys::RegisteredCache* GetGameCardContents();
+    FileSys::RegisteredCache* GetSystemNANDContents() const;
+    FileSys::RegisteredCache* GetUserNANDContents() const;
+    FileSys::RegisteredCache* GetSDMCContents() const;
+    FileSys::RegisteredCache* GetGameCardContents() const;
 
-    FileSys::PlaceholderCache* GetSystemNANDPlaceholder();
-    FileSys::PlaceholderCache* GetUserNANDPlaceholder();
-    FileSys::PlaceholderCache* GetSDMCPlaceholder();
-    FileSys::PlaceholderCache* GetGameCardPlaceholder();
+    FileSys::PlaceholderCache* GetSystemNANDPlaceholder() const;
+    FileSys::PlaceholderCache* GetUserNANDPlaceholder() const;
+    FileSys::PlaceholderCache* GetSDMCPlaceholder() const;
+    FileSys::PlaceholderCache* GetGameCardPlaceholder() const;
 
-    FileSys::RegisteredCache* GetRegisteredCacheForStorage(FileSys::StorageId id);
-    FileSys::PlaceholderCache* GetPlaceholderCacheForStorage(FileSys::StorageId id);
+    FileSys::RegisteredCache* GetRegisteredCacheForStorage(FileSys::StorageId id) const;
+    FileSys::PlaceholderCache* GetPlaceholderCacheForStorage(FileSys::StorageId id) const;
 
-    FileSys::VirtualDir GetSystemNANDContentDirectory();
-    FileSys::VirtualDir GetUserNANDContentDirectory();
-    FileSys::VirtualDir GetSDMCContentDirectory();
+    FileSys::VirtualDir GetSystemNANDContentDirectory() const;
+    FileSys::VirtualDir GetUserNANDContentDirectory() const;
+    FileSys::VirtualDir GetSDMCContentDirectory() const;
 
-    FileSys::VirtualDir GetNANDImageDirectory();
-    FileSys::VirtualDir GetSDMCImageDirectory();
+    FileSys::VirtualDir GetNANDImageDirectory() const;
+    FileSys::VirtualDir GetSDMCImageDirectory() const;
 
-    FileSys::VirtualDir GetContentDirectory(ContentStorageId id);
-    FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id);
+    FileSys::VirtualDir GetContentDirectory(ContentStorageId id) const;
+    FileSys::VirtualDir GetImageDirectory(ImageDirectoryId id) const;
 
-    FileSys::VirtualDir GetModificationLoadRoot(u64 title_id);
-    FileSys::VirtualDir GetModificationDumpRoot(u64 title_id);
+    FileSys::VirtualDir GetModificationLoadRoot(u64 title_id) const;
+    FileSys::VirtualDir GetModificationDumpRoot(u64 title_id) const;
 
     // Creates the SaveData, SDMC, and BIS Factories. Should be called once and before any function
     // above is called.
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 525fda19f8..305d9e1762 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -32,8 +32,8 @@
 namespace Service::FileSystem {
 
 struct SizeGetter {
-    std::function<u64()> free;
-    std::function<u64()> total;
+    std::function<u64()> get_free_size;
+    std::function<u64()> get_total_size;
 
     static SizeGetter FromStorageId(const FileSystemController& fsc, FileSys::StorageId id) {
         return {
@@ -485,7 +485,7 @@ public:
 
         IPC::ResponseBuilder rb{ctx, 4};
         rb.Push(RESULT_SUCCESS);
-        rb.Push(size.free());
+        rb.Push(size.get_free_size());
     }
 
     void GetTotalSpaceSize(Kernel::HLERequestContext& ctx) {
@@ -493,7 +493,7 @@ public:
 
         IPC::ResponseBuilder rb{ctx, 4};
         rb.Push(RESULT_SUCCESS);
-        rb.Push(size.total());
+        rb.Push(size.get_total_size());
     }
 
 private:
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index e66d2f89b4..a6171c3edc 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -175,17 +175,17 @@ use_virtual_sd =
 
 # Whether or not to enable gamecard emulation
 # 1: Yes, 0 (default): No
-gamecard_inserted = 
+gamecard_inserted =
 
 # Whether or not the gamecard should be emulated as the current game
 # If 'gamecard_inserted' is 0 this setting is irrelevant
 # 1: Yes, 0 (default): No
-gamecard_current_game = 
+gamecard_current_game =
 
 # Path to an XCI file to use as the gamecard
 # If 'gamecard_inserted' is 0 this setting is irrelevant
 # If 'gamecard_current_game' is 1 this setting is irrelevant
-gamecard_path = 
+gamecard_path =
 
 [System]
 # Whether the system is docked

From ccd70819c241a84c2ec7f8ad4706b6c0e9a24347 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Sun, 26 May 2019 14:36:35 -0400
Subject: [PATCH 32/33] config: Remove Dump options from configure_debug

---
 src/yuzu/configuration/config.cpp             | 42 ++++++++++++-------
 src/yuzu/configuration/configure_debug.cpp    |  4 --
 src/yuzu/configuration/configure_debug.ui     | 30 +++----------
 .../configuration/configure_filesystem.cpp    | 10 +++--
 4 files changed, 39 insertions(+), 47 deletions(-)

diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 8113060d05..7c286af62f 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -462,38 +462,45 @@ void Config::ReadDataStorageValues() {
     FileUtil::GetUserPath(
         FileUtil::UserPath::LoadDir,
         qt_config
-            ->value("load_directory",
+            ->value(QStringLiteral("load_directory"),
                     QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::LoadDir)))
             .toString()
             .toStdString());
     FileUtil::GetUserPath(
         FileUtil::UserPath::DumpDir,
         qt_config
-            ->value("dump_directory",
+            ->value(QStringLiteral("dump_directory"),
                     QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::DumpDir)))
             .toString()
             .toStdString());
     FileUtil::GetUserPath(
         FileUtil::UserPath::CacheDir,
         qt_config
-            ->value("cache_directory",
+            ->value(QStringLiteral("cache_directory"),
                     QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)))
             .toString()
             .toStdString());
-    Settings::values.gamecard_inserted = ReadSetting("gamecard_inserted", false).toBool();
-    Settings::values.gamecard_current_game = ReadSetting("gamecard_current_game", false).toBool();
-    Settings::values.gamecard_path = ReadSetting("gamecard_path", "").toString().toStdString();
+    Settings::values.gamecard_inserted =
+        ReadSetting(QStringLiteral("gamecard_inserted"), false).toBool();
+    Settings::values.gamecard_current_game =
+        ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
+    Settings::values.gamecard_path =
+        ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString();
     Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
-        ReadSetting(QStringLiteral("nand_total_size"), static_cast<u64>(Settings::NANDTotalSize::S29_1GB))
+        ReadSetting(QStringLiteral("nand_total_size"),
+                    static_cast<u64>(Settings::NANDTotalSize::S29_1GB))
             .toULongLong());
     Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
-        ReadSetting(QStringLiteral("nand_user_size"), static_cast<u64>(Settings::NANDUserSize::S26GB))
+        ReadSetting(QStringLiteral("nand_user_size"),
+                    static_cast<u64>(Settings::NANDUserSize::S26GB))
             .toULongLong());
     Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
-        ReadSetting(QStringLiteral("nand_system_size"), static_cast<u64>(Settings::NANDSystemSize::S2_5GB))
+        ReadSetting(QStringLiteral("nand_system_size"),
+                    static_cast<u64>(Settings::NANDSystemSize::S2_5GB))
             .toULongLong());
     Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
-        ReadSetting(QStringLiteral("sdmc_size"), static_cast<u64>(Settings::SDMCSize::S16GB)).toULongLong());
+        ReadSetting(QStringLiteral("sdmc_size"), static_cast<u64>(Settings::SDMCSize::S16GB))
+            .toULongLong());
 
     qt_config->endGroup();
 }
@@ -920,13 +927,18 @@ void Config::SaveDataStorageValues() {
                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)),
                  QString::fromStdString(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir)));
     WriteSetting(QStringLiteral("gamecard_inserted"), Settings::values.gamecard_inserted, false);
-    WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game, false);
-    WriteSetting(QStringLiteral("gamecard_path"), QString::fromStdString(Settings::values.gamecard_path), "");
-    WriteSetting(QStringLiteral("nand_total_size"), static_cast<u64>(Settings::values.nand_total_size),
+    WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
+                 false);
+    WriteSetting(QStringLiteral("gamecard_path"),
+                 QString::fromStdString(Settings::values.gamecard_path), QStringLiteral(""));
+    WriteSetting(QStringLiteral("nand_total_size"),
+                 static_cast<u64>(Settings::values.nand_total_size),
                  static_cast<u64>(Settings::NANDTotalSize::S29_1GB));
-    WriteSetting(QStringLiteral("nand_user_size"), static_cast<u64>(Settings::values.nand_user_size),
+    WriteSetting(QStringLiteral("nand_user_size"),
+                 static_cast<u64>(Settings::values.nand_user_size),
                  static_cast<u64>(Settings::NANDUserSize::S26GB));
-    WriteSetting(QStringLiteral("nand_system_size"), static_cast<u64>(Settings::values.nand_system_size),
+    WriteSetting(QStringLiteral("nand_system_size"),
+                 static_cast<u64>(Settings::values.nand_system_size),
                  static_cast<u64>(Settings::NANDSystemSize::S2_5GB));
     WriteSetting(QStringLiteral("sdmc_size"), static_cast<u64>(Settings::values.sdmc_size),
                  static_cast<u64>(Settings::SDMCSize::S16GB));
diff --git a/src/yuzu/configuration/configure_debug.cpp b/src/yuzu/configuration/configure_debug.cpp
index 5b7e030569..90c1f9459d 100644
--- a/src/yuzu/configuration/configure_debug.cpp
+++ b/src/yuzu/configuration/configure_debug.cpp
@@ -34,8 +34,6 @@ void ConfigureDebug::SetConfiguration() {
     ui->toggle_console->setChecked(UISettings::values.show_console);
     ui->log_filter_edit->setText(QString::fromStdString(Settings::values.log_filter));
     ui->homebrew_args_edit->setText(QString::fromStdString(Settings::values.program_args));
-    ui->dump_exefs->setChecked(Settings::values.dump_exefs);
-    ui->dump_decompressed_nso->setChecked(Settings::values.dump_nso);
     ui->reporting_services->setChecked(Settings::values.reporting_services);
     ui->quest_flag->setChecked(Settings::values.quest_flag);
 }
@@ -46,8 +44,6 @@ void ConfigureDebug::ApplyConfiguration() {
     UISettings::values.show_console = ui->toggle_console->isChecked();
     Settings::values.log_filter = ui->log_filter_edit->text().toStdString();
     Settings::values.program_args = ui->homebrew_args_edit->text().toStdString();
-    Settings::values.dump_exefs = ui->dump_exefs->isChecked();
-    Settings::values.dump_nso = ui->dump_decompressed_nso->isChecked();
     Settings::values.reporting_services = ui->reporting_services->isChecked();
     Settings::values.quest_flag = ui->quest_flag->isChecked();
     Debugger::ToggleConsole();
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index 7e109cef0c..db254f560d 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -130,31 +130,6 @@
     </widget>
    </item>
    <item>
-    <widget class="QGroupBox" name="groupBox_4">
-     <property name="title">
-      <string>Dump</string>
-     </property>
-     <layout class="QVBoxLayout" name="verticalLayout_6">
-      <item>
-       <widget class="QCheckBox" name="dump_decompressed_nso">
-        <property name="whatsThis">
-         <string>When checked, any NSO yuzu tries to load or patch will be copied decompressed to the yuzu/dump directory.</string>
-        </property>
-        <property name="text">
-         <string>Dump Decompressed NSOs</string>
-        </property>
-       </widget>
-      </item>
-      <item>
-       <widget class="QCheckBox" name="dump_exefs">
-        <property name="whatsThis">
-         <string>When checked, any game that yuzu loads will have its ExeFS dumped to the yuzu/dump directory.</string>
-        </property>
-        <property name="text">
-         <string>Dump ExeFS</string>
-        </property>
-       </widget>
-      </item>
       <item>
        <widget class="QCheckBox" name="reporting_services">
         <property name="text">
@@ -197,6 +172,11 @@
     </widget>
    </item>
    <item>
+    <widget class="QGroupBox" name="groupBox_4">
+     <property name="title">
+      <string>Dump</string>
+     </property>
+     <layout class="QVBoxLayout" name="verticalLayout_6">
     <spacer name="verticalSpacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp
index fabe86e0a3..b8b9daf2aa 100644
--- a/src/yuzu/configuration/configure_filesystem.cpp
+++ b/src/yuzu/configuration/configure_filesystem.cpp
@@ -136,7 +136,7 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit)
     QString str;
     if (target == DirectoryTarget::Gamecard) {
         str = QFileDialog::getOpenFileName(this, caption, QFileInfo(edit->text()).dir().path(),
-                                           "NX Gamecard;*.xci");
+                                           QStringLiteral("NX Gamecard;*.xci"));
     } else {
         str = QFileDialog::getExistingDirectory(this, caption, edit->text());
     }
@@ -148,8 +148,12 @@ void ConfigureFilesystem::SetDirectory(DirectoryTarget target, QLineEdit* edit)
 }
 
 void ConfigureFilesystem::ResetMetadata() {
-    if (FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
-                                       DIR_SEP + "game_list")) {
+    if (!FileUtil::Exists(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) + DIR_SEP +
+                          "game_list")) {
+        QMessageBox::information(this, tr("Reset Metadata Cache"),
+                                 tr("The metadata cache is already empty."));
+    } else if (FileUtil::DeleteDirRecursively(FileUtil::GetUserPath(FileUtil::UserPath::CacheDir) +
+                                              DIR_SEP + "game_list")) {
         QMessageBox::information(this, tr("Reset Metadata Cache"),
                                  tr("The operation completed successfully."));
         UISettings::values.is_game_list_reload_pending.exchange(true);

From 038bcec11153cefd713ddb06eddcc42b0a936df2 Mon Sep 17 00:00:00 2001
From: Zach Hilman <zachhilman@gmail.com>
Date: Sat, 21 Sep 2019 18:43:11 -0400
Subject: [PATCH 33/33] configure_debug: Move reporting option to logging

---
 src/core/file_sys/registered_cache.cpp        |  8 +--
 src/core/hle/service/am/am.cpp                |  9 +--
 src/core/hle/service/am/applet_ae.cpp         |  2 +-
 src/core/hle/service/am/applet_oe.cpp         |  2 +-
 .../hle/service/filesystem/filesystem.cpp     |  5 +-
 src/core/hle/service/filesystem/filesystem.h  |  2 +-
 src/core/hle/service/filesystem/fsp_srv.cpp   |  3 +-
 src/core/hle/service/filesystem/fsp_srv.h     |  2 +-
 src/core/hle/service/service.cpp              |  4 +-
 src/yuzu/CMakeLists.txt                       |  1 +
 src/yuzu/configuration/config.cpp             | 26 +++++----
 src/yuzu/configuration/configure_debug.ui     | 56 +++++++++----------
 .../configuration/configure_filesystem.cpp    |  2 +-
 src/yuzu/configuration/configure_general.cpp  |  1 +
 src/yuzu_tester/yuzu.cpp                      |  4 +-
 15 files changed, 64 insertions(+), 63 deletions(-)

diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index d1ef1e72d2..ac3fbd849e 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -59,12 +59,12 @@ static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bo
                                             bool within_two_digit, bool cnmt_suffix) {
     if (!within_two_digit)
         return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca",
-                           Common::HexArrayToString(nca_id, second_hex_upper));
+                           Common::HexToString(nca_id, second_hex_upper));
 
     Core::Crypto::SHA256Hash hash{};
     mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
     return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0],
-                       Common::HexArrayToString(nca_id, second_hex_upper));
+                       Common::HexToString(nca_id, second_hex_upper));
 }
 
 static std::string GetCNMTName(TitleType type, u64 title_id) {
@@ -149,7 +149,7 @@ bool PlaceholderCache::Create(const NcaID& id, u64 size) const {
     if (dir2 == nullptr)
         return false;
 
-    const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
+    const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexToString(id, false)));
 
     if (file == nullptr)
         return false;
@@ -170,7 +170,7 @@ bool PlaceholderCache::Delete(const NcaID& id) const {
 
     const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname);
 
-    const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexArrayToString(id, false)));
+    const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false)));
 
     return res;
 }
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 7f8514b0de..6c594dcafd 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -1151,7 +1151,8 @@ void IApplicationFunctions::EnsureSaveData(Kernel::HLERequestContext& ctx) {
     descriptor.title_id = Core::CurrentProcess()->GetTitleID();
     descriptor.user_id = user_id;
     descriptor.type = FileSys::SaveDataType::SaveData;
-    const auto res = fsc.CreateSaveData(FileSys::SaveDataSpaceId::NandUser, descriptor);
+    const auto res = system.GetFileSystemController().CreateSaveData(
+        FileSys::SaveDataSpaceId::NandUser, descriptor);
 
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(res.Code());
@@ -1268,8 +1269,8 @@ void IApplicationFunctions::ExtendSaveData(Kernel::HLERequestContext& ctx) {
               "new_journal={:016X}",
               static_cast<u8>(type), user_id[1], user_id[0], new_normal_size, new_journal_size);
 
-    fsc.WriteSaveDataSize(type, system.CurrentProcess()->GetTitleID(), user_id,
-                          {new_normal_size, new_journal_size});
+    system.GetFileSystemController().WriteSaveDataSize(
+        type, system.CurrentProcess()->GetTitleID(), user_id, {new_normal_size, new_journal_size});
 
     IPC::ResponseBuilder rb{ctx, 4};
     rb.Push(RESULT_SUCCESS);
@@ -1288,7 +1289,7 @@ void IApplicationFunctions::GetSaveDataSize(Kernel::HLERequestContext& ctx) {
     LOG_DEBUG(Service_AM, "called with type={:02X}, user_id={:016X}{:016X}", static_cast<u8>(type),
               user_id[1], user_id[0]);
 
-    const auto size = system.FileSystemController().ReadSaveDataSize(
+    const auto size = system.GetFileSystemController().ReadSaveDataSize(
         type, system.CurrentProcess()->GetTitleID(), user_id);
 
     IPC::ResponseBuilder rb{ctx, 6};
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index d04476ae0f..e454b77d80 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -106,7 +106,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 0, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushIpcInterface<IApplicationFunctions>(system.FileSystemController());
+        rb.PushIpcInterface<IApplicationFunctions>(system);
     }
 
     std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
diff --git a/src/core/hle/service/am/applet_oe.cpp b/src/core/hle/service/am/applet_oe.cpp
index 2ee21620b5..a2ffaa4408 100644
--- a/src/core/hle/service/am/applet_oe.cpp
+++ b/src/core/hle/service/am/applet_oe.cpp
@@ -95,7 +95,7 @@ private:
 
         IPC::ResponseBuilder rb{ctx, 2, 0, 1};
         rb.Push(RESULT_SUCCESS);
-        rb.PushIpcInterface<IApplicationFunctions>(system.FileSystemController());
+        rb.PushIpcInterface<IApplicationFunctions>(system);
     }
 
     std::shared_ptr<NVFlinger::NVFlinger> nvflinger;
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 31d5fd79b3..14cd0e3229 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -711,11 +711,10 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
 }
 
 void InstallInterfaces(Core::System& system) {
-    romfs_factory = nullptr;
-    CreateFactories(*system.GetFilesystem(), false);
     std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager());
     std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager());
-    std::make_shared<FSP_SRV>(system.GetReporter())->InstallAsService(system.ServiceManager());
+    std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter())
+        ->InstallAsService(system.ServiceManager());
 }
 
 } // namespace Service::FileSystem
diff --git a/src/core/hle/service/filesystem/filesystem.h b/src/core/hle/service/filesystem/filesystem.h
index 4d7da3b6f3..3e0c03ec0f 100644
--- a/src/core/hle/service/filesystem/filesystem.h
+++ b/src/core/hle/service/filesystem/filesystem.h
@@ -125,7 +125,7 @@ private:
     std::unique_ptr<FileSys::PlaceholderCache> gamecard_placeholder;
 };
 
-void InstallInterfaces(SM::ServiceManager& service_manager, FileSystemController& controller);
+void InstallInterfaces(Core::System& system);
 
 // A class that wraps a VfsDirectory with methods that return ResultVal and ResultCode instead of
 // pointers and booleans. This makes using a VfsDirectory with switch services much easier and
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 305d9e1762..eb982ad49f 100644
--- a/src/core/hle/service/filesystem/fsp_srv.cpp
+++ b/src/core/hle/service/filesystem/fsp_srv.cpp
@@ -650,7 +650,8 @@ private:
     u64 next_entry_index = 0;
 };
 
-FSP_SRV::FSP_SRV(FileSystemController& fsc) : ServiceFramework("fsp-srv"), fsc(fsc) {
+FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
+    : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) {
     // clang-format off
     static const FunctionInfo functions[] = {
         {0, nullptr, "OpenFileSystem"},
diff --git a/src/core/hle/service/filesystem/fsp_srv.h b/src/core/hle/service/filesystem/fsp_srv.h
index 494348598a..d52b55999a 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 {
 
 class FSP_SRV final : public ServiceFramework<FSP_SRV> {
 public:
-    explicit FSP_SRV(FileSystemController& fsc);
+    explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter);
     ~FSP_SRV() override;
 
 private:
diff --git a/src/core/hle/service/service.cpp b/src/core/hle/service/service.cpp
index 3d6a5990fd..4543874672 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -199,7 +199,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
     // NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
     // here and pass it into the respective InstallInterfaces functions.
     auto nv_flinger = std::make_shared<NVFlinger::NVFlinger>(system.CoreTiming());
-    fsc.CreateFactories(*system.GetFilesystem(), false);
+    system.GetFileSystemController().CreateFactories(*system.GetFilesystem(), false);
 
     SM::ServiceManager::InstallInterfaces(sm);
 
@@ -236,7 +236,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
     NIFM::InstallInterfaces(*sm);
     NIM::InstallInterfaces(*sm);
     NPNS::InstallInterfaces(*sm);
-    NS::InstallInterfaces(*sm, fsc);
+    NS::InstallInterfaces(*sm, system.GetFileSystemController());
     Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
     PCIe::InstallInterfaces(*sm);
     PCTL::InstallInterfaces(*sm);
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index c50ca317d5..dc6fa07fc2 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -35,6 +35,7 @@ add_executable(yuzu
     configuration/configure_dialog.h
     configuration/configure_filesystem.cpp
     configuration/configure_filesystem.h
+    configuration/configure_filesystem.ui
     configuration/configure_gamelist.cpp
     configuration/configure_gamelist.h
     configuration/configure_gamelist.ui
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 7c286af62f..92d9fb1610 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -488,18 +488,19 @@ void Config::ReadDataStorageValues() {
         ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString();
     Settings::values.nand_total_size = static_cast<Settings::NANDTotalSize>(
         ReadSetting(QStringLiteral("nand_total_size"),
-                    static_cast<u64>(Settings::NANDTotalSize::S29_1GB))
+                    QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)))
             .toULongLong());
     Settings::values.nand_user_size = static_cast<Settings::NANDUserSize>(
         ReadSetting(QStringLiteral("nand_user_size"),
-                    static_cast<u64>(Settings::NANDUserSize::S26GB))
+                    QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)))
             .toULongLong());
     Settings::values.nand_system_size = static_cast<Settings::NANDSystemSize>(
         ReadSetting(QStringLiteral("nand_system_size"),
-                    static_cast<u64>(Settings::NANDSystemSize::S2_5GB))
+                    QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)))
             .toULongLong());
     Settings::values.sdmc_size = static_cast<Settings::SDMCSize>(
-        ReadSetting(QStringLiteral("sdmc_size"), static_cast<u64>(Settings::SDMCSize::S16GB))
+        ReadSetting(QStringLiteral("sdmc_size"),
+                    QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)))
             .toULongLong());
 
     qt_config->endGroup();
@@ -932,16 +933,17 @@ void Config::SaveDataStorageValues() {
     WriteSetting(QStringLiteral("gamecard_path"),
                  QString::fromStdString(Settings::values.gamecard_path), QStringLiteral(""));
     WriteSetting(QStringLiteral("nand_total_size"),
-                 static_cast<u64>(Settings::values.nand_total_size),
-                 static_cast<u64>(Settings::NANDTotalSize::S29_1GB));
+                 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_total_size)),
+                 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDTotalSize::S29_1GB)));
     WriteSetting(QStringLiteral("nand_user_size"),
-                 static_cast<u64>(Settings::values.nand_user_size),
-                 static_cast<u64>(Settings::NANDUserSize::S26GB));
+                 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_user_size)),
+                 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDUserSize::S26GB)));
     WriteSetting(QStringLiteral("nand_system_size"),
-                 static_cast<u64>(Settings::values.nand_system_size),
-                 static_cast<u64>(Settings::NANDSystemSize::S2_5GB));
-    WriteSetting(QStringLiteral("sdmc_size"), static_cast<u64>(Settings::values.sdmc_size),
-                 static_cast<u64>(Settings::SDMCSize::S16GB));
+                 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.nand_system_size)),
+                 QVariant::fromValue<u64>(static_cast<u64>(Settings::NANDSystemSize::S2_5GB)));
+    WriteSetting(QStringLiteral("sdmc_size"),
+                 QVariant::fromValue<u64>(static_cast<u64>(Settings::values.sdmc_size)),
+                 QVariant::fromValue<u64>(static_cast<u64>(Settings::SDMCSize::S16GB)));
     qt_config->endGroup();
 }
 
diff --git a/src/yuzu/configuration/configure_debug.ui b/src/yuzu/configuration/configure_debug.ui
index db254f560d..ce49569bb1 100644
--- a/src/yuzu/configuration/configure_debug.ui
+++ b/src/yuzu/configuration/configure_debug.ui
@@ -103,33 +103,6 @@
         </item>
        </layout>
       </item>
-     </layout>
-    </widget>
-   </item>
-   <item>
-    <widget class="QGroupBox" name="groupBox_3">
-     <property name="title">
-      <string>Homebrew</string>
-     </property>
-     <layout class="QVBoxLayout" name="verticalLayout_5">
-      <item>
-       <layout class="QHBoxLayout" name="horizontalLayout_4">
-        <item>
-         <widget class="QLabel" name="label_3">
-          <property name="text">
-           <string>Arguments String</string>
-          </property>
-         </widget>
-        </item>
-        <item>
-         <widget class="QLineEdit" name="homebrew_args_edit"/>
-        </item>
-       </layout>
-      </item>
-     </layout>
-    </widget>
-   </item>
-   <item>
       <item>
        <widget class="QCheckBox" name="reporting_services">
         <property name="text">
@@ -138,7 +111,7 @@
        </widget>
       </item>
       <item>
-       <widget class="QLabel" name="label_3">
+       <widget class="QLabel" name="label">
         <property name="font">
          <font>
           <italic>true</italic>
@@ -172,15 +145,36 @@
     </widget>
    </item>
    <item>
-    <widget class="QGroupBox" name="groupBox_4">
+    <widget class="QGroupBox" name="groupBox_3">
      <property name="title">
-      <string>Dump</string>
+      <string>Homebrew</string>
      </property>
-     <layout class="QVBoxLayout" name="verticalLayout_6">
+     <layout class="QVBoxLayout" name="verticalLayout_5">
+      <item>
+       <layout class="QHBoxLayout" name="horizontalLayout_4">
+        <item>
+         <widget class="QLabel" name="label_3">
+          <property name="text">
+           <string>Arguments String</string>
+          </property>
+         </widget>
+        </item>
+        <item>
+         <widget class="QLineEdit" name="homebrew_args_edit"/>
+        </item>
+       </layout>
+      </item>
+     </layout>
+    </widget>
+   </item>
+   <item>
     <spacer name="verticalSpacer">
      <property name="orientation">
       <enum>Qt::Vertical</enum>
      </property>
+     <property name="sizeType">
+      <enum>QSizePolicy::Expanding</enum>
+     </property>
      <property name="sizeHint" stdset="0">
       <size>
        <width>20</width>
diff --git a/src/yuzu/configuration/configure_filesystem.cpp b/src/yuzu/configuration/configure_filesystem.cpp
index b8b9daf2aa..29f540eb7c 100644
--- a/src/yuzu/configuration/configure_filesystem.cpp
+++ b/src/yuzu/configuration/configure_filesystem.cpp
@@ -9,7 +9,7 @@
 #include "core/settings.h"
 #include "ui_configure_filesystem.h"
 #include "yuzu/configuration/configure_filesystem.h"
-#include "yuzu/ui_settings.h"
+#include "yuzu/uisettings.h"
 
 namespace {
 
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 10bcd650ea..b49446be96 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -2,6 +2,7 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include <QSpinBox>
 #include "core/core.h"
 #include "core/settings.h"
 #include "ui_configure_general.h"
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 0ee97aa54a..94ad50cb33 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -22,6 +22,7 @@
 #include "common/telemetry.h"
 #include "core/core.h"
 #include "core/crypto/key_manager.h"
+#include "core/file_sys/registered_cache.h"
 #include "core/file_sys/vfs_real.h"
 #include "core/hle/service/filesystem/filesystem.h"
 #include "core/loader/loader.h"
@@ -216,8 +217,9 @@ int main(int argc, char** argv) {
     };
 
     Core::System& system{Core::System::GetInstance()};
+    system.SetContentProvider(std::make_unique<FileSys::ContentProviderUnion>());
     system.SetFilesystem(std::make_shared<FileSys::RealVfsFilesystem>());
-    Service::FileSystem::CreateFactories(*system.GetFilesystem());
+    system.GetFileSystemController().CreateFactories(*system.GetFilesystem());
 
     SCOPE_EXIT({ system.Shutdown(); });