diff options
Diffstat (limited to 'src/core/file_sys')
| -rw-r--r-- | src/core/file_sys/bis_factory.cpp | 101 | ||||
| -rw-r--r-- | src/core/file_sys/bis_factory.h | 38 | ||||
| -rw-r--r-- | src/core/file_sys/card_image.cpp | 24 | ||||
| -rw-r--r-- | src/core/file_sys/card_image.h | 9 | ||||
| -rw-r--r-- | src/core/file_sys/content_archive.cpp | 8 | ||||
| -rw-r--r-- | src/core/file_sys/content_archive.h | 2 | ||||
| -rw-r--r-- | src/core/file_sys/patch_manager.cpp | 41 | ||||
| -rw-r--r-- | src/core/file_sys/registered_cache.cpp | 188 | ||||
| -rw-r--r-- | src/core/file_sys/registered_cache.h | 25 | ||||
| -rw-r--r-- | src/core/file_sys/romfs_factory.cpp | 16 | ||||
| -rw-r--r-- | src/core/file_sys/romfs_factory.h | 4 | ||||
| -rw-r--r-- | src/core/file_sys/savedata_factory.cpp | 68 | ||||
| -rw-r--r-- | src/core/file_sys/savedata_factory.h | 6 | ||||
| -rw-r--r-- | src/core/file_sys/sdmc_factory.cpp | 27 | ||||
| -rw-r--r-- | src/core/file_sys/sdmc_factory.h | 13 | ||||
| -rw-r--r-- | src/core/file_sys/submission_package.cpp | 3 |
16 files changed, 508 insertions, 65 deletions
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp index e29f70b3a..8f758d6d9 100644 --- a/src/core/file_sys/bis_factory.cpp +++ b/src/core/file_sys/bis_factory.cpp | |||
| @@ -3,8 +3,12 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <fmt/format.h> | 5 | #include <fmt/format.h> |
| 6 | #include "common/file_util.h" | ||
| 7 | #include "core/core.h" | ||
| 6 | #include "core/file_sys/bis_factory.h" | 8 | #include "core/file_sys/bis_factory.h" |
| 9 | #include "core/file_sys/mode.h" | ||
| 7 | #include "core/file_sys/registered_cache.h" | 10 | #include "core/file_sys/registered_cache.h" |
| 11 | #include "core/settings.h" | ||
| 8 | 12 | ||
| 9 | namespace FileSys { | 13 | namespace FileSys { |
| 10 | 14 | ||
| @@ -14,10 +18,22 @@ BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_, VirtualDir | |||
| 14 | sysnand_cache(std::make_unique<RegisteredCache>( | 18 | sysnand_cache(std::make_unique<RegisteredCache>( |
| 15 | GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), | 19 | GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))), |
| 16 | usrnand_cache(std::make_unique<RegisteredCache>( | 20 | usrnand_cache(std::make_unique<RegisteredCache>( |
| 17 | GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))) {} | 21 | GetOrCreateDirectoryRelative(nand_root, "/user/Contents/registered"))), |
| 22 | sysnand_placeholder(std::make_unique<PlaceholderCache>( | ||
| 23 | GetOrCreateDirectoryRelative(nand_root, "/system/Contents/placehld"))), | ||
| 24 | usrnand_placeholder(std::make_unique<PlaceholderCache>( | ||
| 25 | GetOrCreateDirectoryRelative(nand_root, "/user/Contents/placehld"))) {} | ||
| 18 | 26 | ||
| 19 | BISFactory::~BISFactory() = default; | 27 | BISFactory::~BISFactory() = default; |
| 20 | 28 | ||
| 29 | VirtualDir BISFactory::GetSystemNANDContentDirectory() const { | ||
| 30 | return GetOrCreateDirectoryRelative(nand_root, "/system/Contents"); | ||
| 31 | } | ||
| 32 | |||
| 33 | VirtualDir BISFactory::GetUserNANDContentDirectory() const { | ||
| 34 | return GetOrCreateDirectoryRelative(nand_root, "/user/Contents"); | ||
| 35 | } | ||
| 36 | |||
| 21 | RegisteredCache* BISFactory::GetSystemNANDContents() const { | 37 | RegisteredCache* BISFactory::GetSystemNANDContents() const { |
| 22 | return sysnand_cache.get(); | 38 | return sysnand_cache.get(); |
| 23 | } | 39 | } |
| @@ -26,9 +42,17 @@ RegisteredCache* BISFactory::GetUserNANDContents() const { | |||
| 26 | return usrnand_cache.get(); | 42 | return usrnand_cache.get(); |
| 27 | } | 43 | } |
| 28 | 44 | ||
| 45 | PlaceholderCache* BISFactory::GetSystemNANDPlaceholder() const { | ||
| 46 | return sysnand_placeholder.get(); | ||
| 47 | } | ||
| 48 | |||
| 49 | PlaceholderCache* BISFactory::GetUserNANDPlaceholder() const { | ||
| 50 | return usrnand_placeholder.get(); | ||
| 51 | } | ||
| 52 | |||
| 29 | VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { | 53 | VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const { |
| 30 | // LayeredFS doesn't work on updates and title id-less homebrew | 54 | // LayeredFS doesn't work on updates and title id-less homebrew |
| 31 | if (title_id == 0 || (title_id & 0x800) > 0) | 55 | if (title_id == 0 || (title_id & 0xFFF) == 0x800) |
| 32 | return nullptr; | 56 | return nullptr; |
| 33 | return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); | 57 | return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id)); |
| 34 | } | 58 | } |
| @@ -39,4 +63,77 @@ VirtualDir BISFactory::GetModificationDumpRoot(u64 title_id) const { | |||
| 39 | return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id)); | 63 | return GetOrCreateDirectoryRelative(dump_root, fmt::format("/{:016X}", title_id)); |
| 40 | } | 64 | } |
| 41 | 65 | ||
| 66 | VirtualDir BISFactory::OpenPartition(BisPartitionId id) const { | ||
| 67 | switch (id) { | ||
| 68 | case BisPartitionId::CalibrationFile: | ||
| 69 | return GetOrCreateDirectoryRelative(nand_root, "/prodinfof"); | ||
| 70 | case BisPartitionId::SafeMode: | ||
| 71 | return GetOrCreateDirectoryRelative(nand_root, "/safe"); | ||
| 72 | case BisPartitionId::System: | ||
| 73 | return GetOrCreateDirectoryRelative(nand_root, "/system"); | ||
| 74 | case BisPartitionId::User: | ||
| 75 | return GetOrCreateDirectoryRelative(nand_root, "/user"); | ||
| 76 | default: | ||
| 77 | return nullptr; | ||
| 78 | } | ||
| 79 | } | ||
| 80 | |||
| 81 | VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const { | ||
| 82 | Core::Crypto::KeyManager keys; | ||
| 83 | Core::Crypto::PartitionDataManager pdm{ | ||
| 84 | Core::System::GetInstance().GetFilesystem()->OpenDirectory( | ||
| 85 | FileUtil::GetUserPath(FileUtil::UserPath::SysDataDir), Mode::Read)}; | ||
| 86 | keys.PopulateFromPartitionData(pdm); | ||
| 87 | |||
| 88 | switch (id) { | ||
| 89 | case BisPartitionId::CalibrationBinary: | ||
| 90 | return pdm.GetDecryptedProdInfo(); | ||
| 91 | case BisPartitionId::BootConfigAndPackage2Part1: | ||
| 92 | case BisPartitionId::BootConfigAndPackage2Part2: | ||
| 93 | case BisPartitionId::BootConfigAndPackage2Part3: | ||
| 94 | case BisPartitionId::BootConfigAndPackage2Part4: | ||
| 95 | case BisPartitionId::BootConfigAndPackage2Part5: | ||
| 96 | case BisPartitionId::BootConfigAndPackage2Part6: { | ||
| 97 | const auto new_id = static_cast<u8>(id) - | ||
| 98 | static_cast<u8>(BisPartitionId::BootConfigAndPackage2Part1) + | ||
| 99 | static_cast<u8>(Core::Crypto::Package2Type::NormalMain); | ||
| 100 | return pdm.GetPackage2Raw(static_cast<Core::Crypto::Package2Type>(new_id)); | ||
| 101 | } | ||
| 102 | default: | ||
| 103 | return nullptr; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | VirtualDir BISFactory::GetImageDirectory() const { | ||
| 108 | return GetOrCreateDirectoryRelative(nand_root, "/user/Album"); | ||
| 109 | } | ||
| 110 | |||
| 111 | u64 BISFactory::GetSystemNANDFreeSpace() const { | ||
| 112 | const auto sys_dir = GetOrCreateDirectoryRelative(nand_root, "/system"); | ||
| 113 | if (sys_dir == nullptr) | ||
| 114 | return 0; | ||
| 115 | |||
| 116 | return GetSystemNANDTotalSpace() - sys_dir->GetSize(); | ||
| 117 | } | ||
| 118 | |||
| 119 | u64 BISFactory::GetSystemNANDTotalSpace() const { | ||
| 120 | return static_cast<u64>(Settings::values.nand_system_size); | ||
| 121 | } | ||
| 122 | |||
| 123 | u64 BISFactory::GetUserNANDFreeSpace() const { | ||
| 124 | const auto usr_dir = GetOrCreateDirectoryRelative(nand_root, "/user"); | ||
| 125 | if (usr_dir == nullptr) | ||
| 126 | return 0; | ||
| 127 | |||
| 128 | return GetUserNANDTotalSpace() - usr_dir->GetSize(); | ||
| 129 | } | ||
| 130 | |||
| 131 | u64 BISFactory::GetUserNANDTotalSpace() const { | ||
| 132 | return static_cast<u64>(Settings::values.nand_user_size); | ||
| 133 | } | ||
| 134 | |||
| 135 | u64 BISFactory::GetFullNANDTotalSpace() const { | ||
| 136 | return static_cast<u64>(Settings::values.nand_total_size); | ||
| 137 | } | ||
| 138 | |||
| 42 | } // namespace FileSys | 139 | } // namespace FileSys |
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h index 453c11ad2..bdfe728c9 100644 --- a/src/core/file_sys/bis_factory.h +++ b/src/core/file_sys/bis_factory.h | |||
| @@ -10,7 +10,25 @@ | |||
| 10 | 10 | ||
| 11 | namespace FileSys { | 11 | namespace FileSys { |
| 12 | 12 | ||
| 13 | enum class BisPartitionId : u32 { | ||
| 14 | UserDataRoot = 20, | ||
| 15 | CalibrationBinary = 27, | ||
| 16 | CalibrationFile = 28, | ||
| 17 | BootConfigAndPackage2Part1 = 21, | ||
| 18 | BootConfigAndPackage2Part2 = 22, | ||
| 19 | BootConfigAndPackage2Part3 = 23, | ||
| 20 | BootConfigAndPackage2Part4 = 24, | ||
| 21 | BootConfigAndPackage2Part5 = 25, | ||
| 22 | BootConfigAndPackage2Part6 = 26, | ||
| 23 | SafeMode = 29, | ||
| 24 | System = 31, | ||
| 25 | SystemProperEncryption = 32, | ||
| 26 | SystemProperPartition = 33, | ||
| 27 | User = 30, | ||
| 28 | }; | ||
| 29 | |||
| 13 | class RegisteredCache; | 30 | class RegisteredCache; |
| 31 | class PlaceholderCache; | ||
| 14 | 32 | ||
| 15 | /// File system interface to the Built-In Storage | 33 | /// File system interface to the Built-In Storage |
| 16 | /// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND | 34 | /// This is currently missing accessors to BIS partitions, but seemed like a good place for the NAND |
| @@ -20,12 +38,29 @@ public: | |||
| 20 | explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root); | 38 | explicit BISFactory(VirtualDir nand_root, VirtualDir load_root, VirtualDir dump_root); |
| 21 | ~BISFactory(); | 39 | ~BISFactory(); |
| 22 | 40 | ||
| 41 | VirtualDir GetSystemNANDContentDirectory() const; | ||
| 42 | VirtualDir GetUserNANDContentDirectory() const; | ||
| 43 | |||
| 23 | RegisteredCache* GetSystemNANDContents() const; | 44 | RegisteredCache* GetSystemNANDContents() const; |
| 24 | RegisteredCache* GetUserNANDContents() const; | 45 | RegisteredCache* GetUserNANDContents() const; |
| 25 | 46 | ||
| 47 | PlaceholderCache* GetSystemNANDPlaceholder() const; | ||
| 48 | PlaceholderCache* GetUserNANDPlaceholder() const; | ||
| 49 | |||
| 26 | VirtualDir GetModificationLoadRoot(u64 title_id) const; | 50 | VirtualDir GetModificationLoadRoot(u64 title_id) const; |
| 27 | VirtualDir GetModificationDumpRoot(u64 title_id) const; | 51 | VirtualDir GetModificationDumpRoot(u64 title_id) const; |
| 28 | 52 | ||
| 53 | VirtualDir OpenPartition(BisPartitionId id) const; | ||
| 54 | VirtualFile OpenPartitionStorage(BisPartitionId id) const; | ||
| 55 | |||
| 56 | VirtualDir GetImageDirectory() const; | ||
| 57 | |||
| 58 | u64 GetSystemNANDFreeSpace() const; | ||
| 59 | u64 GetSystemNANDTotalSpace() const; | ||
| 60 | u64 GetUserNANDFreeSpace() const; | ||
| 61 | u64 GetUserNANDTotalSpace() const; | ||
| 62 | u64 GetFullNANDTotalSpace() const; | ||
| 63 | |||
| 29 | private: | 64 | private: |
| 30 | VirtualDir nand_root; | 65 | VirtualDir nand_root; |
| 31 | VirtualDir load_root; | 66 | VirtualDir load_root; |
| @@ -33,6 +68,9 @@ private: | |||
| 33 | 68 | ||
| 34 | std::unique_ptr<RegisteredCache> sysnand_cache; | 69 | std::unique_ptr<RegisteredCache> sysnand_cache; |
| 35 | std::unique_ptr<RegisteredCache> usrnand_cache; | 70 | std::unique_ptr<RegisteredCache> usrnand_cache; |
| 71 | |||
| 72 | std::unique_ptr<PlaceholderCache> sysnand_placeholder; | ||
| 73 | std::unique_ptr<PlaceholderCache> usrnand_placeholder; | ||
| 36 | }; | 74 | }; |
| 37 | 75 | ||
| 38 | } // namespace FileSys | 76 | } // namespace FileSys |
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index 626ed0042..db54113a0 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp | |||
| @@ -12,12 +12,16 @@ | |||
| 12 | #include "core/file_sys/content_archive.h" | 12 | #include "core/file_sys/content_archive.h" |
| 13 | #include "core/file_sys/nca_metadata.h" | 13 | #include "core/file_sys/nca_metadata.h" |
| 14 | #include "core/file_sys/partition_filesystem.h" | 14 | #include "core/file_sys/partition_filesystem.h" |
| 15 | #include "core/file_sys/romfs.h" | ||
| 15 | #include "core/file_sys/submission_package.h" | 16 | #include "core/file_sys/submission_package.h" |
| 17 | #include "core/file_sys/vfs_concat.h" | ||
| 16 | #include "core/file_sys/vfs_offset.h" | 18 | #include "core/file_sys/vfs_offset.h" |
| 19 | #include "core/file_sys/vfs_vector.h" | ||
| 17 | #include "core/loader/loader.h" | 20 | #include "core/loader/loader.h" |
| 18 | 21 | ||
| 19 | namespace FileSys { | 22 | namespace FileSys { |
| 20 | 23 | ||
| 24 | constexpr u64 GAMECARD_CERTIFICATE_OFFSET = 0x7000; | ||
| 21 | constexpr std::array partition_names{ | 25 | constexpr std::array partition_names{ |
| 22 | "update", | 26 | "update", |
| 23 | "normal", | 27 | "normal", |
| @@ -175,6 +179,26 @@ VirtualDir XCI::GetParentDirectory() const { | |||
| 175 | return file->GetContainingDirectory(); | 179 | return file->GetContainingDirectory(); |
| 176 | } | 180 | } |
| 177 | 181 | ||
| 182 | VirtualDir XCI::ConcatenatedPseudoDirectory() { | ||
| 183 | const auto out = std::make_shared<VectorVfsDirectory>(); | ||
| 184 | for (const auto& part_id : {XCIPartition::Normal, XCIPartition::Logo, XCIPartition::Secure}) { | ||
| 185 | const auto& part = GetPartition(part_id); | ||
| 186 | if (part == nullptr) | ||
| 187 | continue; | ||
| 188 | |||
| 189 | for (const auto& file : part->GetFiles()) | ||
| 190 | out->AddFile(file); | ||
| 191 | } | ||
| 192 | |||
| 193 | return out; | ||
| 194 | } | ||
| 195 | |||
| 196 | std::array<u8, 0x200> XCI::GetCertificate() const { | ||
| 197 | std::array<u8, 0x200> out; | ||
| 198 | file->Read(out.data(), out.size(), GAMECARD_CERTIFICATE_OFFSET); | ||
| 199 | return out; | ||
| 200 | } | ||
| 201 | |||
| 178 | Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { | 202 | Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) { |
| 179 | const auto partition_index = static_cast<std::size_t>(part); | 203 | const auto partition_index = static_cast<std::size_t>(part); |
| 180 | const auto& partition = partitions[partition_index]; | 204 | const auto& partition = partitions[partition_index]; |
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index a350496f7..3e6b92ff3 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h | |||
| @@ -91,6 +91,8 @@ public: | |||
| 91 | VirtualDir GetLogoPartition() const; | 91 | VirtualDir GetLogoPartition() const; |
| 92 | 92 | ||
| 93 | u64 GetProgramTitleID() const; | 93 | u64 GetProgramTitleID() const; |
| 94 | u32 GetSystemUpdateVersion(); | ||
| 95 | u64 GetSystemUpdateTitleID() const; | ||
| 94 | 96 | ||
| 95 | bool HasProgramNCA() const; | 97 | bool HasProgramNCA() const; |
| 96 | VirtualFile GetProgramNCAFile() const; | 98 | VirtualFile GetProgramNCAFile() const; |
| @@ -106,6 +108,11 @@ public: | |||
| 106 | 108 | ||
| 107 | VirtualDir GetParentDirectory() const override; | 109 | VirtualDir GetParentDirectory() const override; |
| 108 | 110 | ||
| 111 | // Creates a directory that contains all the NCAs in the gamecard | ||
| 112 | VirtualDir ConcatenatedPseudoDirectory(); | ||
| 113 | |||
| 114 | std::array<u8, 0x200> GetCertificate() const; | ||
| 115 | |||
| 109 | private: | 116 | private: |
| 110 | Loader::ResultStatus AddNCAFromPartition(XCIPartition part); | 117 | Loader::ResultStatus AddNCAFromPartition(XCIPartition part); |
| 111 | 118 | ||
| @@ -120,6 +127,8 @@ private: | |||
| 120 | std::shared_ptr<NCA> program; | 127 | std::shared_ptr<NCA> program; |
| 121 | std::vector<std::shared_ptr<NCA>> ncas; | 128 | std::vector<std::shared_ptr<NCA>> ncas; |
| 122 | 129 | ||
| 130 | u64 update_normal_partition_end; | ||
| 131 | |||
| 123 | Core::Crypto::KeyManager keys; | 132 | Core::Crypto::KeyManager keys; |
| 124 | }; | 133 | }; |
| 125 | } // namespace FileSys | 134 | } // namespace FileSys |
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp index ce5c69b41..ea5c92f61 100644 --- a/src/core/file_sys/content_archive.cpp +++ b/src/core/file_sys/content_archive.cpp | |||
| @@ -528,6 +528,14 @@ u64 NCA::GetTitleId() const { | |||
| 528 | return header.title_id; | 528 | return header.title_id; |
| 529 | } | 529 | } |
| 530 | 530 | ||
| 531 | std::array<u8, 16> NCA::GetRightsId() const { | ||
| 532 | return header.rights_id; | ||
| 533 | } | ||
| 534 | |||
| 535 | u32 NCA::GetSDKVersion() const { | ||
| 536 | return header.sdk_version; | ||
| 537 | } | ||
| 538 | |||
| 531 | bool NCA::IsUpdate() const { | 539 | bool NCA::IsUpdate() const { |
| 532 | return is_update; | 540 | return is_update; |
| 533 | } | 541 | } |
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h index 15b9e6624..e249079b5 100644 --- a/src/core/file_sys/content_archive.h +++ b/src/core/file_sys/content_archive.h | |||
| @@ -112,6 +112,8 @@ public: | |||
| 112 | 112 | ||
| 113 | NCAContentType GetType() const; | 113 | NCAContentType GetType() const; |
| 114 | u64 GetTitleId() const; | 114 | u64 GetTitleId() const; |
| 115 | std::array<u8, 0x10> GetRightsId() const; | ||
| 116 | u32 GetSDKVersion() const; | ||
| 115 | bool IsUpdate() const; | 117 | bool IsUpdate() const; |
| 116 | 118 | ||
| 117 | VirtualFile GetRomFS() const; | 119 | VirtualFile GetRomFS() const; |
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp index a8f80e2c6..c1dd0c6d7 100644 --- a/src/core/file_sys/patch_manager.cpp +++ b/src/core/file_sys/patch_manager.cpp | |||
| @@ -63,7 +63,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 63 | 63 | ||
| 64 | if (Settings::values.dump_exefs) { | 64 | if (Settings::values.dump_exefs) { |
| 65 | LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); | 65 | LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id); |
| 66 | const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); | 66 | const auto dump_dir = |
| 67 | Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id); | ||
| 67 | if (dump_dir != nullptr) { | 68 | if (dump_dir != nullptr) { |
| 68 | const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); | 69 | const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs"); |
| 69 | VfsRawCopyD(exefs, exefs_dir); | 70 | VfsRawCopyD(exefs, exefs_dir); |
| @@ -88,7 +89,8 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const { | |||
| 88 | } | 89 | } |
| 89 | 90 | ||
| 90 | // LayeredExeFS | 91 | // LayeredExeFS |
| 91 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 92 | const auto load_dir = |
| 93 | Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||
| 92 | if (load_dir != nullptr && load_dir->GetSize() > 0) { | 94 | if (load_dir != nullptr && load_dir->GetSize() > 0) { |
| 93 | auto patch_dirs = load_dir->GetSubdirectories(); | 95 | auto patch_dirs = load_dir->GetSubdirectories(); |
| 94 | std::sort( | 96 | std::sort( |
| @@ -174,7 +176,8 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st | |||
| 174 | if (Settings::values.dump_nso) { | 176 | if (Settings::values.dump_nso) { |
| 175 | LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id, | 177 | LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id, |
| 176 | title_id); | 178 | title_id); |
| 177 | const auto dump_dir = Service::FileSystem::GetModificationDumpRoot(title_id); | 179 | const auto dump_dir = |
| 180 | Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id); | ||
| 178 | if (dump_dir != nullptr) { | 181 | if (dump_dir != nullptr) { |
| 179 | const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); | 182 | const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso"); |
| 180 | const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id)); | 183 | const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id)); |
| @@ -186,7 +189,13 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st | |||
| 186 | 189 | ||
| 187 | LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); | 190 | LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id); |
| 188 | 191 | ||
| 189 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 192 | const auto load_dir = |
| 193 | Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||
| 194 | if (load_dir == nullptr) { | ||
| 195 | LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); | ||
| 196 | return nso; | ||
| 197 | } | ||
| 198 | |||
| 190 | auto patch_dirs = load_dir->GetSubdirectories(); | 199 | auto patch_dirs = load_dir->GetSubdirectories(); |
| 191 | std::sort(patch_dirs.begin(), patch_dirs.end(), | 200 | std::sort(patch_dirs.begin(), patch_dirs.end(), |
| 192 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 201 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); |
| @@ -224,7 +233,13 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const { | |||
| 224 | 233 | ||
| 225 | LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); | 234 | LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id); |
| 226 | 235 | ||
| 227 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 236 | const auto load_dir = |
| 237 | Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||
| 238 | if (load_dir == nullptr) { | ||
| 239 | LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); | ||
| 240 | return false; | ||
| 241 | } | ||
| 242 | |||
| 228 | auto patch_dirs = load_dir->GetSubdirectories(); | 243 | auto patch_dirs = load_dir->GetSubdirectories(); |
| 229 | std::sort(patch_dirs.begin(), patch_dirs.end(), | 244 | std::sort(patch_dirs.begin(), patch_dirs.end(), |
| 230 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 245 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); |
| @@ -258,7 +273,13 @@ static std::optional<CheatList> ReadCheatFileFromFolder(const Core::System& syst | |||
| 258 | 273 | ||
| 259 | std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system, | 274 | std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system, |
| 260 | const std::array<u8, 32>& build_id_) const { | 275 | const std::array<u8, 32>& build_id_) const { |
| 261 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 276 | const auto load_dir = |
| 277 | Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||
| 278 | if (load_dir == nullptr) { | ||
| 279 | LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id); | ||
| 280 | return {}; | ||
| 281 | } | ||
| 282 | |||
| 262 | auto patch_dirs = load_dir->GetSubdirectories(); | 283 | auto patch_dirs = load_dir->GetSubdirectories(); |
| 263 | std::sort(patch_dirs.begin(), patch_dirs.end(), | 284 | std::sort(patch_dirs.begin(), patch_dirs.end(), |
| 264 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); | 285 | [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); }); |
| @@ -284,7 +305,8 @@ std::vector<CheatList> PatchManager::CreateCheatList(const Core::System& system, | |||
| 284 | } | 305 | } |
| 285 | 306 | ||
| 286 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { | 307 | static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) { |
| 287 | const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 308 | const auto load_dir = |
| 309 | Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||
| 288 | if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || | 310 | if ((type != ContentRecordType::Program && type != ContentRecordType::Data) || |
| 289 | load_dir == nullptr || load_dir->GetSize() <= 0) { | 311 | load_dir == nullptr || load_dir->GetSize() <= 0) { |
| 290 | return; | 312 | return; |
| @@ -393,6 +415,8 @@ static bool IsDirValidAndNonEmpty(const VirtualDir& dir) { | |||
| 393 | 415 | ||
| 394 | std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( | 416 | std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames( |
| 395 | VirtualFile update_raw) const { | 417 | VirtualFile update_raw) const { |
| 418 | if (title_id == 0) | ||
| 419 | return {}; | ||
| 396 | std::map<std::string, std::string, std::less<>> out; | 420 | std::map<std::string, std::string, std::less<>> out; |
| 397 | const auto& installed = Core::System::GetInstance().GetContentProvider(); | 421 | const auto& installed = Core::System::GetInstance().GetContentProvider(); |
| 398 | const auto& disabled = Settings::values.disabled_addons[title_id]; | 422 | const auto& disabled = Settings::values.disabled_addons[title_id]; |
| @@ -423,7 +447,8 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam | |||
| 423 | } | 447 | } |
| 424 | 448 | ||
| 425 | // General Mods (LayeredFS and IPS) | 449 | // General Mods (LayeredFS and IPS) |
| 426 | const auto mod_dir = Service::FileSystem::GetModificationLoadRoot(title_id); | 450 | const auto mod_dir = |
| 451 | Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id); | ||
| 427 | if (mod_dir != nullptr && mod_dir->GetSize() > 0) { | 452 | if (mod_dir != nullptr && mod_dir->GetSize() > 0) { |
| 428 | for (const auto& mod : mod_dir->GetSubdirectories()) { | 453 | for (const auto& mod : mod_dir->GetSubdirectories()) { |
| 429 | std::string types; | 454 | std::string types; |
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 3725b10f7..ac3fbd849 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp | |||
| @@ -3,6 +3,7 @@ | |||
| 3 | // Refer to the license.txt file included. | 3 | // Refer to the license.txt file included. |
| 4 | 4 | ||
| 5 | #include <algorithm> | 5 | #include <algorithm> |
| 6 | #include <random> | ||
| 6 | #include <regex> | 7 | #include <regex> |
| 7 | #include <mbedtls/sha256.h> | 8 | #include <mbedtls/sha256.h> |
| 8 | #include "common/assert.h" | 9 | #include "common/assert.h" |
| @@ -48,18 +49,21 @@ static bool FollowsTwoDigitDirFormat(std::string_view name) { | |||
| 48 | static bool FollowsNcaIdFormat(std::string_view name) { | 49 | static bool FollowsNcaIdFormat(std::string_view name) { |
| 49 | static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript | | 50 | static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript | |
| 50 | std::regex_constants::icase); | 51 | std::regex_constants::icase); |
| 51 | return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex); | 52 | static const std::regex nca_id_cnmt_regex( |
| 53 | "[0-9A-F]{32}\\.cnmt.nca", std::regex_constants::ECMAScript | std::regex_constants::icase); | ||
| 54 | return (name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex)) || | ||
| 55 | (name.size() == 41 && std::regex_match(name.begin(), name.end(), nca_id_cnmt_regex)); | ||
| 52 | } | 56 | } |
| 53 | 57 | ||
| 54 | static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper, | 58 | static std::string GetRelativePathFromNcaID(const std::array<u8, 16>& nca_id, bool second_hex_upper, |
| 55 | bool within_two_digit) { | 59 | bool within_two_digit, bool cnmt_suffix) { |
| 56 | if (!within_two_digit) { | 60 | if (!within_two_digit) |
| 57 | return fmt::format("/{}.nca", Common::HexToString(nca_id, second_hex_upper)); | 61 | return fmt::format(cnmt_suffix ? "{}.cnmt.nca" : "/{}.nca", |
| 58 | } | 62 | Common::HexToString(nca_id, second_hex_upper)); |
| 59 | 63 | ||
| 60 | Core::Crypto::SHA256Hash hash{}; | 64 | Core::Crypto::SHA256Hash hash{}; |
| 61 | mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); | 65 | mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0); |
| 62 | return fmt::format("/000000{:02X}/{}.nca", hash[0], | 66 | return fmt::format(cnmt_suffix ? "/000000{:02X}/{}.cnmt.nca" : "/000000{:02X}/{}.nca", hash[0], |
| 63 | Common::HexToString(nca_id, second_hex_upper)); | 67 | Common::HexToString(nca_id, second_hex_upper)); |
| 64 | } | 68 | } |
| 65 | 69 | ||
| @@ -127,6 +131,156 @@ std::vector<ContentProviderEntry> ContentProvider::ListEntries() const { | |||
| 127 | return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt); | 131 | return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt); |
| 128 | } | 132 | } |
| 129 | 133 | ||
| 134 | PlaceholderCache::PlaceholderCache(VirtualDir dir_) : dir(std::move(dir_)) {} | ||
| 135 | |||
| 136 | bool PlaceholderCache::Create(const NcaID& id, u64 size) const { | ||
| 137 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 138 | |||
| 139 | if (dir->GetFileRelative(path) != nullptr) { | ||
| 140 | return false; | ||
| 141 | } | ||
| 142 | |||
| 143 | Core::Crypto::SHA256Hash hash{}; | ||
| 144 | mbedtls_sha256(id.data(), id.size(), hash.data(), 0); | ||
| 145 | const auto dirname = fmt::format("000000{:02X}", hash[0]); | ||
| 146 | |||
| 147 | const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); | ||
| 148 | |||
| 149 | if (dir2 == nullptr) | ||
| 150 | return false; | ||
| 151 | |||
| 152 | const auto file = dir2->CreateFile(fmt::format("{}.nca", Common::HexToString(id, false))); | ||
| 153 | |||
| 154 | if (file == nullptr) | ||
| 155 | return false; | ||
| 156 | |||
| 157 | return file->Resize(size); | ||
| 158 | } | ||
| 159 | |||
| 160 | bool PlaceholderCache::Delete(const NcaID& id) const { | ||
| 161 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 162 | |||
| 163 | if (dir->GetFileRelative(path) == nullptr) { | ||
| 164 | return false; | ||
| 165 | } | ||
| 166 | |||
| 167 | Core::Crypto::SHA256Hash hash{}; | ||
| 168 | mbedtls_sha256(id.data(), id.size(), hash.data(), 0); | ||
| 169 | const auto dirname = fmt::format("000000{:02X}", hash[0]); | ||
| 170 | |||
| 171 | const auto dir2 = GetOrCreateDirectoryRelative(dir, dirname); | ||
| 172 | |||
| 173 | const auto res = dir2->DeleteFile(fmt::format("{}.nca", Common::HexToString(id, false))); | ||
| 174 | |||
| 175 | return res; | ||
| 176 | } | ||
| 177 | |||
| 178 | bool PlaceholderCache::Exists(const NcaID& id) const { | ||
| 179 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 180 | |||
| 181 | return dir->GetFileRelative(path) != nullptr; | ||
| 182 | } | ||
| 183 | |||
| 184 | bool PlaceholderCache::Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const { | ||
| 185 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 186 | const auto file = dir->GetFileRelative(path); | ||
| 187 | |||
| 188 | if (file == nullptr) | ||
| 189 | return false; | ||
| 190 | |||
| 191 | return file->WriteBytes(data, offset) == data.size(); | ||
| 192 | } | ||
| 193 | |||
| 194 | bool PlaceholderCache::Register(RegisteredCache* cache, const NcaID& placeholder, | ||
| 195 | const NcaID& install) const { | ||
| 196 | const auto path = GetRelativePathFromNcaID(placeholder, false, true, false); | ||
| 197 | const auto file = dir->GetFileRelative(path); | ||
| 198 | |||
| 199 | if (file == nullptr) | ||
| 200 | return false; | ||
| 201 | |||
| 202 | const auto res = cache->RawInstallNCA(NCA{file}, &VfsRawCopy, false, install); | ||
| 203 | |||
| 204 | if (res != InstallResult::Success) | ||
| 205 | return false; | ||
| 206 | |||
| 207 | return Delete(placeholder); | ||
| 208 | } | ||
| 209 | |||
| 210 | bool PlaceholderCache::CleanAll() const { | ||
| 211 | return dir->GetParentDirectory()->CleanSubdirectoryRecursive(dir->GetName()); | ||
| 212 | } | ||
| 213 | |||
| 214 | std::optional<std::array<u8, 0x10>> PlaceholderCache::GetRightsID(const NcaID& id) const { | ||
| 215 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 216 | const auto file = dir->GetFileRelative(path); | ||
| 217 | |||
| 218 | if (file == nullptr) | ||
| 219 | return std::nullopt; | ||
| 220 | |||
| 221 | NCA nca{file}; | ||
| 222 | |||
| 223 | if (nca.GetStatus() != Loader::ResultStatus::Success && | ||
| 224 | nca.GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | ||
| 225 | return std::nullopt; | ||
| 226 | } | ||
| 227 | |||
| 228 | const auto rights_id = nca.GetRightsId(); | ||
| 229 | if (rights_id == NcaID{}) | ||
| 230 | return std::nullopt; | ||
| 231 | |||
| 232 | return rights_id; | ||
| 233 | } | ||
| 234 | |||
| 235 | u64 PlaceholderCache::Size(const NcaID& id) const { | ||
| 236 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 237 | const auto file = dir->GetFileRelative(path); | ||
| 238 | |||
| 239 | if (file == nullptr) | ||
| 240 | return 0; | ||
| 241 | |||
| 242 | return file->GetSize(); | ||
| 243 | } | ||
| 244 | |||
| 245 | bool PlaceholderCache::SetSize(const NcaID& id, u64 new_size) const { | ||
| 246 | const auto path = GetRelativePathFromNcaID(id, false, true, false); | ||
| 247 | const auto file = dir->GetFileRelative(path); | ||
| 248 | |||
| 249 | if (file == nullptr) | ||
| 250 | return false; | ||
| 251 | |||
| 252 | return file->Resize(new_size); | ||
| 253 | } | ||
| 254 | |||
| 255 | std::vector<NcaID> PlaceholderCache::List() const { | ||
| 256 | std::vector<NcaID> out; | ||
| 257 | for (const auto& sdir : dir->GetSubdirectories()) { | ||
| 258 | for (const auto& file : sdir->GetFiles()) { | ||
| 259 | const auto name = file->GetName(); | ||
| 260 | if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' && | ||
| 261 | name[35] == 'a') { | ||
| 262 | out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32))); | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | return out; | ||
| 267 | } | ||
| 268 | |||
| 269 | NcaID PlaceholderCache::Generate() { | ||
| 270 | std::random_device device; | ||
| 271 | std::mt19937 gen(device()); | ||
| 272 | std::uniform_int_distribution<u64> distribution(1, std::numeric_limits<u64>::max()); | ||
| 273 | |||
| 274 | NcaID out{}; | ||
| 275 | |||
| 276 | const auto v1 = distribution(gen); | ||
| 277 | const auto v2 = distribution(gen); | ||
| 278 | std::memcpy(out.data(), &v1, sizeof(u64)); | ||
| 279 | std::memcpy(out.data() + sizeof(u64), &v2, sizeof(u64)); | ||
| 280 | |||
| 281 | return out; | ||
| 282 | } | ||
| 283 | |||
| 130 | VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, | 284 | VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, |
| 131 | std::string_view path) const { | 285 | std::string_view path) const { |
| 132 | const auto file = dir->GetFileRelative(path); | 286 | const auto file = dir->GetFileRelative(path); |
| @@ -169,14 +323,18 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, | |||
| 169 | 323 | ||
| 170 | VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { | 324 | VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { |
| 171 | VirtualFile file; | 325 | VirtualFile file; |
| 172 | // Try all four modes of file storage: | 326 | // Try all five relevant modes of file storage: |
| 173 | // (bit 1 = uppercase/lower, bit 0 = within a two-digit dir) | 327 | // (bit 2 = uppercase/lower, bit 1 = within a two-digit dir, bit 0 = .cnmt suffix) |
| 174 | // 00: /000000**/{:032X}.nca | 328 | // 000: /000000**/{:032X}.nca |
| 175 | // 01: /{:032X}.nca | 329 | // 010: /{:032X}.nca |
| 176 | // 10: /000000**/{:032x}.nca | 330 | // 100: /000000**/{:032x}.nca |
| 177 | // 11: /{:032x}.nca | 331 | // 110: /{:032x}.nca |
| 178 | for (u8 i = 0; i < 4; ++i) { | 332 | // 111: /{:032x}.cnmt.nca |
| 179 | const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0); | 333 | for (u8 i = 0; i < 8; ++i) { |
| 334 | if ((i % 2) == 1 && i != 7) | ||
| 335 | continue; | ||
| 336 | const auto path = | ||
| 337 | GetRelativePathFromNcaID(id, (i & 0b100) == 0, (i & 0b010) == 0, (i & 0b001) == 0b001); | ||
| 180 | file = OpenFileOrDirectoryConcat(dir, path); | 338 | file = OpenFileOrDirectoryConcat(dir, path); |
| 181 | if (file != nullptr) | 339 | if (file != nullptr) |
| 182 | return file; | 340 | return file; |
| @@ -472,7 +630,7 @@ InstallResult RegisteredCache::RawInstallNCA(const NCA& nca, const VfsCopyFuncti | |||
| 472 | memcpy(id.data(), hash.data(), 16); | 630 | memcpy(id.data(), hash.data(), 16); |
| 473 | } | 631 | } |
| 474 | 632 | ||
| 475 | std::string path = GetRelativePathFromNcaID(id, false, true); | 633 | std::string path = GetRelativePathFromNcaID(id, false, true, false); |
| 476 | 634 | ||
| 477 | if (GetFileAtID(id) != nullptr && !overwrite_if_exists) { | 635 | if (GetFileAtID(id) != nullptr && !overwrite_if_exists) { |
| 478 | LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping..."); | 636 | LOG_WARNING(Loader, "Attempting to overwrite existing NCA. Skipping..."); |
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 4398d63e1..d1eec240e 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h | |||
| @@ -25,6 +25,8 @@ enum class NCAContentType : u8; | |||
| 25 | enum class TitleType : u8; | 25 | enum class TitleType : u8; |
| 26 | 26 | ||
| 27 | struct ContentRecord; | 27 | struct ContentRecord; |
| 28 | struct MetaRecord; | ||
| 29 | class RegisteredCache; | ||
| 28 | 30 | ||
| 29 | using NcaID = std::array<u8, 0x10>; | 31 | using NcaID = std::array<u8, 0x10>; |
| 30 | using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; | 32 | using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; |
| @@ -89,6 +91,27 @@ protected: | |||
| 89 | Core::Crypto::KeyManager keys; | 91 | Core::Crypto::KeyManager keys; |
| 90 | }; | 92 | }; |
| 91 | 93 | ||
| 94 | class PlaceholderCache { | ||
| 95 | public: | ||
| 96 | explicit PlaceholderCache(VirtualDir dir); | ||
| 97 | |||
| 98 | bool Create(const NcaID& id, u64 size) const; | ||
| 99 | bool Delete(const NcaID& id) const; | ||
| 100 | bool Exists(const NcaID& id) const; | ||
| 101 | bool Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const; | ||
| 102 | bool Register(RegisteredCache* cache, const NcaID& placeholder, const NcaID& install) const; | ||
| 103 | bool CleanAll() const; | ||
| 104 | std::optional<std::array<u8, 0x10>> GetRightsID(const NcaID& id) const; | ||
| 105 | u64 Size(const NcaID& id) const; | ||
| 106 | bool SetSize(const NcaID& id, u64 new_size) const; | ||
| 107 | std::vector<NcaID> List() const; | ||
| 108 | |||
| 109 | static NcaID Generate(); | ||
| 110 | |||
| 111 | private: | ||
| 112 | VirtualDir dir; | ||
| 113 | }; | ||
| 114 | |||
| 92 | /* | 115 | /* |
| 93 | * A class that catalogues NCAs in the registered directory structure. | 116 | * A class that catalogues NCAs in the registered directory structure. |
| 94 | * Nintendo's registered format follows this structure: | 117 | * Nintendo's registered format follows this structure: |
| @@ -103,6 +126,8 @@ protected: | |||
| 103 | * when 4GB splitting can be ignored.) | 126 | * when 4GB splitting can be ignored.) |
| 104 | */ | 127 | */ |
| 105 | class RegisteredCache : public ContentProvider { | 128 | class RegisteredCache : public ContentProvider { |
| 129 | friend class PlaceholderCache; | ||
| 130 | |||
| 106 | public: | 131 | public: |
| 107 | // Parsing function defines the conversion from raw file to NCA. If there are other steps | 132 | // Parsing function defines the conversion from raw file to NCA. If there are other steps |
| 108 | // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom | 133 | // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom |
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp index b2ccb2926..84cd4684c 100644 --- a/src/core/file_sys/romfs_factory.cpp +++ b/src/core/file_sys/romfs_factory.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include "common/common_types.h" | 7 | #include "common/common_types.h" |
| 8 | #include "common/logging/log.h" | 8 | #include "common/logging/log.h" |
| 9 | #include "core/core.h" | 9 | #include "core/core.h" |
| 10 | #include "core/file_sys/card_image.h" | ||
| 10 | #include "core/file_sys/content_archive.h" | 11 | #include "core/file_sys/content_archive.h" |
| 11 | #include "core/file_sys/nca_metadata.h" | 12 | #include "core/file_sys/nca_metadata.h" |
| 12 | #include "core/file_sys/patch_manager.h" | 13 | #include "core/file_sys/patch_manager.h" |
| @@ -34,7 +35,7 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) { | |||
| 34 | this->update_raw = std::move(update_raw); | 35 | this->update_raw = std::move(update_raw); |
| 35 | } | 36 | } |
| 36 | 37 | ||
| 37 | ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { | 38 | ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() const { |
| 38 | if (!updatable) | 39 | if (!updatable) |
| 39 | return MakeResult<VirtualFile>(file); | 40 | return MakeResult<VirtualFile>(file); |
| 40 | 41 | ||
| @@ -43,7 +44,8 @@ ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() { | |||
| 43 | patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); | 44 | patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw)); |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, ContentRecordType type) { | 47 | ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, |
| 48 | ContentRecordType type) const { | ||
| 47 | std::shared_ptr<NCA> res; | 49 | std::shared_ptr<NCA> res; |
| 48 | 50 | ||
| 49 | switch (storage) { | 51 | switch (storage) { |
| @@ -51,13 +53,17 @@ ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage, Conte | |||
| 51 | res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type); | 53 | res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type); |
| 52 | break; | 54 | break; |
| 53 | case StorageId::NandSystem: | 55 | case StorageId::NandSystem: |
| 54 | res = Service::FileSystem::GetSystemNANDContents()->GetEntry(title_id, type); | 56 | res = |
| 57 | Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry( | ||
| 58 | title_id, type); | ||
| 55 | break; | 59 | break; |
| 56 | case StorageId::NandUser: | 60 | case StorageId::NandUser: |
| 57 | res = Service::FileSystem::GetUserNANDContents()->GetEntry(title_id, type); | 61 | res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry( |
| 62 | title_id, type); | ||
| 58 | break; | 63 | break; |
| 59 | case StorageId::SdCard: | 64 | case StorageId::SdCard: |
| 60 | res = Service::FileSystem::GetSDMCContents()->GetEntry(title_id, type); | 65 | res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry( |
| 66 | title_id, type); | ||
| 61 | break; | 67 | break; |
| 62 | default: | 68 | default: |
| 63 | UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage)); | 69 | UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage)); |
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h index 7724c0b23..da63a313a 100644 --- a/src/core/file_sys/romfs_factory.h +++ b/src/core/file_sys/romfs_factory.h | |||
| @@ -33,8 +33,8 @@ public: | |||
| 33 | ~RomFSFactory(); | 33 | ~RomFSFactory(); |
| 34 | 34 | ||
| 35 | void SetPackedUpdate(VirtualFile update_raw); | 35 | void SetPackedUpdate(VirtualFile update_raw); |
| 36 | ResultVal<VirtualFile> OpenCurrentProcess(); | 36 | ResultVal<VirtualFile> OpenCurrentProcess() const; |
| 37 | ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type); | 37 | ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const; |
| 38 | 38 | ||
| 39 | private: | 39 | private: |
| 40 | VirtualFile file; | 40 | VirtualFile file; |
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp index 7974b031d..f77cc02ac 100644 --- a/src/core/file_sys/savedata_factory.cpp +++ b/src/core/file_sys/savedata_factory.cpp | |||
| @@ -15,22 +15,8 @@ namespace FileSys { | |||
| 15 | 15 | ||
| 16 | constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; | 16 | constexpr char SAVE_DATA_SIZE_FILENAME[] = ".yuzu_save_size"; |
| 17 | 17 | ||
| 18 | std::string SaveDataDescriptor::DebugInfo() const { | 18 | namespace { |
| 19 | return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, save_id={:016X}, " | 19 | void PrintSaveDataDescriptorWarnings(SaveDataDescriptor meta) { |
| 20 | "rank={}, index={}]", | ||
| 21 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id, | ||
| 22 | static_cast<u8>(rank), index); | ||
| 23 | } | ||
| 24 | |||
| 25 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) { | ||
| 26 | // Delete all temporary storages | ||
| 27 | // On hardware, it is expected that temporary storage be empty at first use. | ||
| 28 | dir->DeleteSubdirectoryRecursive("temp"); | ||
| 29 | } | ||
| 30 | |||
| 31 | SaveDataFactory::~SaveDataFactory() = default; | ||
| 32 | |||
| 33 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) { | ||
| 34 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { | 20 | if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) { |
| 35 | if (meta.zero_1 != 0) { | 21 | if (meta.zero_1 != 0) { |
| 36 | LOG_WARNING(Service_FS, | 22 | LOG_WARNING(Service_FS, |
| @@ -65,23 +51,51 @@ ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, const SaveDat | |||
| 65 | "non-zero ({:016X}{:016X})", | 51 | "non-zero ({:016X}{:016X})", |
| 66 | meta.user_id[1], meta.user_id[0]); | 52 | meta.user_id[1], meta.user_id[0]); |
| 67 | } | 53 | } |
| 54 | } | ||
| 55 | } // Anonymous namespace | ||
| 68 | 56 | ||
| 69 | std::string save_directory = | 57 | std::string SaveDataDescriptor::DebugInfo() const { |
| 70 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); | 58 | return fmt::format("[type={:02X}, title_id={:016X}, user_id={:016X}{:016X}, " |
| 59 | "save_id={:016X}, " | ||
| 60 | "rank={}, index={}]", | ||
| 61 | static_cast<u8>(type), title_id, user_id[1], user_id[0], save_id, | ||
| 62 | static_cast<u8>(rank), index); | ||
| 63 | } | ||
| 71 | 64 | ||
| 72 | // TODO(DarkLordZach): Try to not create when opening, there are dedicated create save methods. | 65 | SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) { |
| 73 | // But, user_ids don't match so this works for now. | 66 | // Delete all temporary storages |
| 67 | // On hardware, it is expected that temporary storage be empty at first use. | ||
| 68 | dir->DeleteSubdirectoryRecursive("temp"); | ||
| 69 | } | ||
| 74 | 70 | ||
| 75 | auto out = dir->GetDirectoryRelative(save_directory); | 71 | SaveDataFactory::~SaveDataFactory() = default; |
| 72 | |||
| 73 | ResultVal<VirtualDir> SaveDataFactory::Create(SaveDataSpaceId space, | ||
| 74 | const SaveDataDescriptor& meta) const { | ||
| 75 | PrintSaveDataDescriptorWarnings(meta); | ||
| 76 | |||
| 77 | const auto save_directory = | ||
| 78 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); | ||
| 79 | |||
| 80 | auto out = dir->CreateDirectoryRelative(save_directory); | ||
| 76 | 81 | ||
| 82 | // Return an error if the save data doesn't actually exist. | ||
| 77 | if (out == nullptr) { | 83 | if (out == nullptr) { |
| 78 | // TODO(bunnei): This is a work-around to always create a save data directory if it does not | 84 | // TODO(DarkLordZach): Find out correct error code. |
| 79 | // already exist. This is a hack, as we do not understand yet how this works on hardware. | 85 | return ResultCode(-1); |
| 80 | // Without a save data directory, many games will assert on boot. This should not have any | ||
| 81 | // bad side-effects. | ||
| 82 | out = dir->CreateDirectoryRelative(save_directory); | ||
| 83 | } | 86 | } |
| 84 | 87 | ||
| 88 | return MakeResult<VirtualDir>(std::move(out)); | ||
| 89 | } | ||
| 90 | |||
| 91 | ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, | ||
| 92 | const SaveDataDescriptor& meta) const { | ||
| 93 | |||
| 94 | const auto save_directory = | ||
| 95 | GetFullPath(space, meta.type, meta.title_id, meta.user_id, meta.save_id); | ||
| 96 | |||
| 97 | auto out = dir->GetDirectoryRelative(save_directory); | ||
| 98 | |||
| 85 | // Return an error if the save data doesn't actually exist. | 99 | // Return an error if the save data doesn't actually exist. |
| 86 | if (out == nullptr) { | 100 | if (out == nullptr) { |
| 87 | // TODO(Subv): Find out correct error code. | 101 | // TODO(Subv): Find out correct error code. |
| @@ -152,7 +166,7 @@ SaveDataSize SaveDataFactory::ReadSaveDataSize(SaveDataType type, u64 title_id, | |||
| 152 | } | 166 | } |
| 153 | 167 | ||
| 154 | void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, | 168 | void SaveDataFactory::WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, |
| 155 | SaveDataSize new_value) { | 169 | SaveDataSize new_value) const { |
| 156 | const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0); | 170 | const auto path = GetFullPath(SaveDataSpaceId::NandUser, type, title_id, user_id, 0); |
| 157 | const auto dir = GetOrCreateDirectoryRelative(this->dir, path); | 171 | const auto dir = GetOrCreateDirectoryRelative(this->dir, path); |
| 158 | 172 | ||
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h index b73654571..991e57aa1 100644 --- a/src/core/file_sys/savedata_factory.h +++ b/src/core/file_sys/savedata_factory.h | |||
| @@ -64,7 +64,8 @@ public: | |||
| 64 | explicit SaveDataFactory(VirtualDir dir); | 64 | explicit SaveDataFactory(VirtualDir dir); |
| 65 | ~SaveDataFactory(); | 65 | ~SaveDataFactory(); |
| 66 | 66 | ||
| 67 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta); | 67 | ResultVal<VirtualDir> Create(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; |
| 68 | ResultVal<VirtualDir> Open(SaveDataSpaceId space, const SaveDataDescriptor& meta) const; | ||
| 68 | 69 | ||
| 69 | VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; | 70 | VirtualDir GetSaveDataSpaceDirectory(SaveDataSpaceId space) const; |
| 70 | 71 | ||
| @@ -73,7 +74,8 @@ public: | |||
| 73 | u128 user_id, u64 save_id); | 74 | u128 user_id, u64 save_id); |
| 74 | 75 | ||
| 75 | SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; | 76 | SaveDataSize ReadSaveDataSize(SaveDataType type, u64 title_id, u128 user_id) const; |
| 76 | void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, SaveDataSize new_value); | 77 | void WriteSaveDataSize(SaveDataType type, u64 title_id, u128 user_id, |
| 78 | SaveDataSize new_value) const; | ||
| 77 | 79 | ||
| 78 | private: | 80 | private: |
| 79 | VirtualDir dir; | 81 | VirtualDir dir; |
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp index bd3a57058..5113a1ca6 100644 --- a/src/core/file_sys/sdmc_factory.cpp +++ b/src/core/file_sys/sdmc_factory.cpp | |||
| @@ -6,6 +6,7 @@ | |||
| 6 | #include "core/file_sys/registered_cache.h" | 6 | #include "core/file_sys/registered_cache.h" |
| 7 | #include "core/file_sys/sdmc_factory.h" | 7 | #include "core/file_sys/sdmc_factory.h" |
| 8 | #include "core/file_sys/xts_archive.h" | 8 | #include "core/file_sys/xts_archive.h" |
| 9 | #include "core/settings.h" | ||
| 9 | 10 | ||
| 10 | namespace FileSys { | 11 | namespace FileSys { |
| 11 | 12 | ||
| @@ -14,16 +15,38 @@ SDMCFactory::SDMCFactory(VirtualDir dir_) | |||
| 14 | GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), | 15 | GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/registered"), |
| 15 | [](const VirtualFile& file, const NcaID& id) { | 16 | [](const VirtualFile& file, const NcaID& id) { |
| 16 | return NAX{file, id}.GetDecrypted(); | 17 | return NAX{file, id}.GetDecrypted(); |
| 17 | })) {} | 18 | })), |
| 19 | placeholder(std::make_unique<PlaceholderCache>( | ||
| 20 | GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents/placehld"))) {} | ||
| 18 | 21 | ||
| 19 | SDMCFactory::~SDMCFactory() = default; | 22 | SDMCFactory::~SDMCFactory() = default; |
| 20 | 23 | ||
| 21 | ResultVal<VirtualDir> SDMCFactory::Open() { | 24 | ResultVal<VirtualDir> SDMCFactory::Open() const { |
| 22 | return MakeResult<VirtualDir>(dir); | 25 | return MakeResult<VirtualDir>(dir); |
| 23 | } | 26 | } |
| 24 | 27 | ||
| 28 | VirtualDir SDMCFactory::GetSDMCContentDirectory() const { | ||
| 29 | return GetOrCreateDirectoryRelative(dir, "/Nintendo/Contents"); | ||
| 30 | } | ||
| 31 | |||
| 25 | RegisteredCache* SDMCFactory::GetSDMCContents() const { | 32 | RegisteredCache* SDMCFactory::GetSDMCContents() const { |
| 26 | return contents.get(); | 33 | return contents.get(); |
| 27 | } | 34 | } |
| 28 | 35 | ||
| 36 | PlaceholderCache* SDMCFactory::GetSDMCPlaceholder() const { | ||
| 37 | return placeholder.get(); | ||
| 38 | } | ||
| 39 | |||
| 40 | VirtualDir SDMCFactory::GetImageDirectory() const { | ||
| 41 | return GetOrCreateDirectoryRelative(dir, "/Nintendo/Album"); | ||
| 42 | } | ||
| 43 | |||
| 44 | u64 SDMCFactory::GetSDMCFreeSpace() const { | ||
| 45 | return GetSDMCTotalSpace() - dir->GetSize(); | ||
| 46 | } | ||
| 47 | |||
| 48 | u64 SDMCFactory::GetSDMCTotalSpace() const { | ||
| 49 | return static_cast<u64>(Settings::values.sdmc_size); | ||
| 50 | } | ||
| 51 | |||
| 29 | } // namespace FileSys | 52 | } // namespace FileSys |
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h index 42794ba5b..42dc4e08a 100644 --- a/src/core/file_sys/sdmc_factory.h +++ b/src/core/file_sys/sdmc_factory.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | namespace FileSys { | 11 | namespace FileSys { |
| 12 | 12 | ||
| 13 | class RegisteredCache; | 13 | class RegisteredCache; |
| 14 | class PlaceholderCache; | ||
| 14 | 15 | ||
| 15 | /// File system interface to the SDCard archive | 16 | /// File system interface to the SDCard archive |
| 16 | class SDMCFactory { | 17 | class SDMCFactory { |
| @@ -18,13 +19,23 @@ public: | |||
| 18 | explicit SDMCFactory(VirtualDir dir); | 19 | explicit SDMCFactory(VirtualDir dir); |
| 19 | ~SDMCFactory(); | 20 | ~SDMCFactory(); |
| 20 | 21 | ||
| 21 | ResultVal<VirtualDir> Open(); | 22 | ResultVal<VirtualDir> Open() const; |
| 23 | |||
| 24 | VirtualDir GetSDMCContentDirectory() const; | ||
| 25 | |||
| 22 | RegisteredCache* GetSDMCContents() const; | 26 | RegisteredCache* GetSDMCContents() const; |
| 27 | PlaceholderCache* GetSDMCPlaceholder() const; | ||
| 28 | |||
| 29 | VirtualDir GetImageDirectory() const; | ||
| 30 | |||
| 31 | u64 GetSDMCFreeSpace() const; | ||
| 32 | u64 GetSDMCTotalSpace() const; | ||
| 23 | 33 | ||
| 24 | private: | 34 | private: |
| 25 | VirtualDir dir; | 35 | VirtualDir dir; |
| 26 | 36 | ||
| 27 | std::unique_ptr<RegisteredCache> contents; | 37 | std::unique_ptr<RegisteredCache> contents; |
| 38 | std::unique_ptr<PlaceholderCache> placeholder; | ||
| 28 | }; | 39 | }; |
| 29 | 40 | ||
| 30 | } // namespace FileSys | 41 | } // namespace FileSys |
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index 730221fd6..ef3084681 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp | |||
| @@ -248,7 +248,8 @@ void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { | |||
| 248 | 248 | ||
| 249 | void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { | 249 | void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { |
| 250 | for (const auto& outer_file : files) { | 250 | for (const auto& outer_file : files) { |
| 251 | if (outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") { | 251 | if (outer_file->GetName().size() < 9 || |
| 252 | outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") { | ||
| 252 | continue; | 253 | continue; |
| 253 | } | 254 | } |
| 254 | 255 | ||