registered_cache.h 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #pragma once
  4. #include <array>
  5. #include <functional>
  6. #include <memory>
  7. #include <string>
  8. #include <vector>
  9. #include <boost/container/flat_map.hpp>
  10. #include "common/common_types.h"
  11. #include "core/crypto/key_manager.h"
  12. #include "core/file_sys/vfs.h"
  13. namespace FileSys {
  14. class CNMT;
  15. class NCA;
  16. class NSP;
  17. class XCI;
  18. enum class ContentRecordType : u8;
  19. enum class NCAContentType : u8;
  20. enum class TitleType : u8;
  21. struct ContentRecord;
  22. struct MetaRecord;
  23. class RegisteredCache;
  24. using NcaID = std::array<u8, 0x10>;
  25. using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
  26. using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
  27. enum class InstallResult {
  28. Success,
  29. OverwriteExisting,
  30. ErrorAlreadyExists,
  31. ErrorCopyFailed,
  32. ErrorMetaFailed,
  33. ErrorBaseInstall,
  34. };
  35. struct ContentProviderEntry {
  36. u64 title_id;
  37. ContentRecordType type;
  38. std::string DebugInfo() const;
  39. };
  40. constexpr u64 GetUpdateTitleID(u64 base_title_id) {
  41. return base_title_id | 0x800;
  42. }
  43. ContentRecordType GetCRTypeFromNCAType(NCAContentType type);
  44. // boost flat_map requires operator< for O(log(n)) lookups.
  45. bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
  46. // std unique requires operator== to identify duplicates.
  47. bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
  48. bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
  49. class ContentProvider {
  50. public:
  51. virtual ~ContentProvider();
  52. virtual void Refresh() = 0;
  53. virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0;
  54. bool HasEntry(ContentProviderEntry entry) const;
  55. virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0;
  56. virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0;
  57. VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;
  58. virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0;
  59. VirtualFile GetEntryRaw(ContentProviderEntry entry) const;
  60. virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0;
  61. std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;
  62. virtual std::vector<ContentProviderEntry> ListEntries() const;
  63. // If a parameter is not std::nullopt, it will be filtered for from all entries.
  64. virtual std::vector<ContentProviderEntry> ListEntriesFilter(
  65. std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
  66. std::optional<u64> title_id = {}) const = 0;
  67. protected:
  68. // A single instance of KeyManager to be used by GetEntry()
  69. Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
  70. };
  71. class PlaceholderCache {
  72. public:
  73. explicit PlaceholderCache(VirtualDir dir);
  74. bool Create(const NcaID& id, u64 size) const;
  75. bool Delete(const NcaID& id) const;
  76. bool Exists(const NcaID& id) const;
  77. bool Write(const NcaID& id, u64 offset, const std::vector<u8>& data) const;
  78. bool Register(RegisteredCache* cache, const NcaID& placeholder, const NcaID& install) const;
  79. bool CleanAll() const;
  80. std::optional<std::array<u8, 0x10>> GetRightsID(const NcaID& id) const;
  81. u64 Size(const NcaID& id) const;
  82. bool SetSize(const NcaID& id, u64 new_size) const;
  83. std::vector<NcaID> List() const;
  84. static NcaID Generate();
  85. private:
  86. VirtualDir dir;
  87. };
  88. /*
  89. * A class that catalogues NCAs in the registered directory structure.
  90. * Nintendo's registered format follows this structure:
  91. *
  92. * Root
  93. * | 000000XX <- XX is the ____ two digits of the NcaID
  94. * | <hash>.nca <- hash is the NcaID (first half of SHA256 over entire file) (folder)
  95. * | 00
  96. * | 01 <- Actual content split along 4GB boundaries. (optional)
  97. *
  98. * (This impl also supports substituting the nca dir for an nca file, as that's more convenient
  99. * when 4GB splitting can be ignored.)
  100. */
  101. class RegisteredCache : public ContentProvider {
  102. friend class PlaceholderCache;
  103. public:
  104. // Parsing function defines the conversion from raw file to NCA. If there are other steps
  105. // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
  106. // parsing function.
  107. explicit RegisteredCache(
  108. VirtualDir dir, ContentProviderParsingFunction parsing_function =
  109. [](const VirtualFile& file, const NcaID& id) { return file; });
  110. ~RegisteredCache() override;
  111. void Refresh() override;
  112. bool HasEntry(u64 title_id, ContentRecordType type) const override;
  113. std::optional<u32> GetEntryVersion(u64 title_id) const override;
  114. VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
  115. VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
  116. std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
  117. // If a parameter is not std::nullopt, it will be filtered for from all entries.
  118. std::vector<ContentProviderEntry> ListEntriesFilter(
  119. std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
  120. std::optional<u64> title_id = {}) const override;
  121. // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
  122. // there is a meta NCA and all of them are accessible.
  123. InstallResult InstallEntry(const XCI& xci, bool overwrite_if_exists = false,
  124. const VfsCopyFunction& copy = &VfsRawCopy);
  125. InstallResult InstallEntry(const NSP& nsp, bool overwrite_if_exists = false,
  126. const VfsCopyFunction& copy = &VfsRawCopy);
  127. // Due to the fact that we must use Meta-type NCAs to determine the existence of files, this
  128. // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a
  129. // dir inside the NAND called 'yuzu_meta' and store the raw CNMT there.
  130. // TODO(DarkLordZach): Author real meta-type NCAs and install those.
  131. InstallResult InstallEntry(const NCA& nca, TitleType type, bool overwrite_if_exists = false,
  132. const VfsCopyFunction& copy = &VfsRawCopy);
  133. // Removes an existing entry based on title id
  134. bool RemoveExistingEntry(u64 title_id) const;
  135. private:
  136. template <typename T>
  137. void IterateAllMetadata(std::vector<T>& out,
  138. std::function<T(const CNMT&, const ContentRecord&)> proc,
  139. std::function<bool(const CNMT&, const ContentRecord&)> filter) const;
  140. std::vector<NcaID> AccumulateFiles() const;
  141. void ProcessFiles(const std::vector<NcaID>& ids);
  142. void AccumulateYuzuMeta();
  143. std::optional<NcaID> GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const;
  144. VirtualFile GetFileAtID(NcaID id) const;
  145. VirtualFile OpenFileOrDirectoryConcat(const VirtualDir& open_dir, std::string_view path) const;
  146. InstallResult RawInstallNCA(const NCA& nca, const VfsCopyFunction& copy,
  147. bool overwrite_if_exists, std::optional<NcaID> override_id = {});
  148. bool RawInstallYuzuMeta(const CNMT& cnmt);
  149. VirtualDir dir;
  150. ContentProviderParsingFunction parser;
  151. // maps tid -> NcaID of meta
  152. std::map<u64, NcaID> meta_id;
  153. // maps tid -> meta
  154. std::map<u64, CNMT> meta;
  155. // maps tid -> meta for CNMT in yuzu_meta
  156. std::map<u64, CNMT> yuzu_meta;
  157. };
  158. enum class ContentProviderUnionSlot {
  159. SysNAND, ///< System NAND
  160. UserNAND, ///< User NAND
  161. SDMC, ///< SD Card
  162. FrontendManual, ///< Frontend-defined game list or similar
  163. };
  164. // Combines multiple ContentProvider(s) (i.e. SysNAND, UserNAND, SDMC) into one interface.
  165. class ContentProviderUnion : public ContentProvider {
  166. public:
  167. ~ContentProviderUnion() override;
  168. void SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider);
  169. void ClearSlot(ContentProviderUnionSlot slot);
  170. void Refresh() override;
  171. bool HasEntry(u64 title_id, ContentRecordType type) const override;
  172. std::optional<u32> GetEntryVersion(u64 title_id) const override;
  173. VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
  174. VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
  175. std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
  176. std::vector<ContentProviderEntry> ListEntriesFilter(
  177. std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
  178. std::optional<u64> title_id) const override;
  179. std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> ListEntriesFilterOrigin(
  180. std::optional<ContentProviderUnionSlot> origin = {},
  181. std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
  182. std::optional<u64> title_id = {}) const;
  183. std::optional<ContentProviderUnionSlot> GetSlotForEntry(u64 title_id,
  184. ContentRecordType type) const;
  185. private:
  186. std::map<ContentProviderUnionSlot, ContentProvider*> providers;
  187. };
  188. class ManualContentProvider : public ContentProvider {
  189. public:
  190. ~ManualContentProvider() override;
  191. void AddEntry(TitleType title_type, ContentRecordType content_type, u64 title_id,
  192. VirtualFile file);
  193. void ClearAllEntries();
  194. void Refresh() override;
  195. bool HasEntry(u64 title_id, ContentRecordType type) const override;
  196. std::optional<u32> GetEntryVersion(u64 title_id) const override;
  197. VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
  198. VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
  199. std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
  200. std::vector<ContentProviderEntry> ListEntriesFilter(
  201. std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
  202. std::optional<u64> title_id) const override;
  203. private:
  204. std::map<std::tuple<TitleType, ContentRecordType, u64>, VirtualFile> entries;
  205. };
  206. } // namespace FileSys