summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/core/file_sys/registered_cache.cpp275
-rw-r--r--src/core/file_sys/registered_cache.h156
2 files changed, 279 insertions, 152 deletions
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 1c6bacace..3946ff871 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -23,19 +23,19 @@ namespace FileSys {
23// The size of blocks to use when vfs raw copying into nand. 23// The size of blocks to use when vfs raw copying into nand.
24constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000; 24constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
25 25
26std::string RegisteredCacheEntry::DebugInfo() const { 26std::string ContentProviderEntry::DebugInfo() const {
27 return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type)); 27 return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
28} 28}
29 29
30bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { 30bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
31 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type); 31 return (lhs.title_id < rhs.title_id) || (lhs.title_id == rhs.title_id && lhs.type < rhs.type);
32} 32}
33 33
34bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { 34bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
35 return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type); 35 return std::tie(lhs.title_id, lhs.type) == std::tie(rhs.title_id, rhs.type);
36} 36}
37 37
38bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) { 38bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs) {
39 return !operator==(lhs, rhs); 39 return !operator==(lhs, rhs);
40} 40}
41 41
@@ -84,7 +84,7 @@ static std::string GetCNMTName(TitleType type, u64 title_id) {
84 return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); 84 return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
85} 85}
86 86
87static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) { 87ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
88 switch (type) { 88 switch (type) {
89 case NCAContentType::Program: 89 case NCAContentType::Program:
90 // TODO(DarkLordZach): Differentiate between Program and Patch 90 // TODO(DarkLordZach): Differentiate between Program and Patch
@@ -104,6 +104,28 @@ static ContentRecordType GetCRTypeFromNCAType(NCAContentType type) {
104 } 104 }
105} 105}
106 106
107ContentProvider::~ContentProvider() = default;
108
109bool ContentProvider::HasEntry(ContentProviderEntry entry) const {
110 return HasEntry(entry.title_id, entry.type);
111}
112
113VirtualFile ContentProvider::GetEntryUnparsed(ContentProviderEntry entry) const {
114 return GetEntryUnparsed(entry.title_id, entry.type);
115}
116
117VirtualFile ContentProvider::GetEntryRaw(ContentProviderEntry entry) const {
118 return GetEntryRaw(entry.title_id, entry.type);
119}
120
121std::unique_ptr<NCA> ContentProvider::GetEntry(ContentProviderEntry entry) const {
122 return GetEntry(entry.title_id, entry.type);
123}
124
125std::vector<ContentProviderEntry> ContentProvider::ListEntries() const {
126 return ListEntriesFilter(std::nullopt, std::nullopt, std::nullopt);
127}
128
107VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, 129VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
108 std::string_view path) const { 130 std::string_view path) const {
109 const auto file = dir->GetFileRelative(path); 131 const auto file = dir->GetFileRelative(path);
@@ -161,8 +183,8 @@ VirtualFile RegisteredCache::GetFileAtID(NcaID id) const {
161 return file; 183 return file;
162} 184}
163 185
164static std::optional<NcaID> CheckMapForContentRecord( 186static std::optional<NcaID> CheckMapForContentRecord(const std::map<u64, CNMT>& map, u64 title_id,
165 const boost::container::flat_map<u64, CNMT>& map, u64 title_id, ContentRecordType type) { 187 ContentRecordType type) {
166 if (map.find(title_id) == map.end()) 188 if (map.find(title_id) == map.end())
167 return {}; 189 return {};
168 190
@@ -268,7 +290,7 @@ void RegisteredCache::Refresh() {
268 AccumulateYuzuMeta(); 290 AccumulateYuzuMeta();
269} 291}
270 292
271RegisteredCache::RegisteredCache(VirtualDir dir_, RegisteredCacheParsingFunction parsing_function) 293RegisteredCache::RegisteredCache(VirtualDir dir_, ContentProviderParsingFunction parsing_function)
272 : dir(std::move(dir_)), parser(std::move(parsing_function)) { 294 : dir(std::move(dir_)), parser(std::move(parsing_function)) {
273 Refresh(); 295 Refresh();
274} 296}
@@ -279,19 +301,11 @@ bool RegisteredCache::HasEntry(u64 title_id, ContentRecordType type) const {
279 return GetEntryRaw(title_id, type) != nullptr; 301 return GetEntryRaw(title_id, type) != nullptr;
280} 302}
281 303
282bool RegisteredCache::HasEntry(RegisteredCacheEntry entry) const {
283 return GetEntryRaw(entry) != nullptr;
284}
285
286VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { 304VirtualFile RegisteredCache::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
287 const auto id = GetNcaIDFromMetadata(title_id, type); 305 const auto id = GetNcaIDFromMetadata(title_id, type);
288 return id ? GetFileAtID(*id) : nullptr; 306 return id ? GetFileAtID(*id) : nullptr;
289} 307}
290 308
291VirtualFile RegisteredCache::GetEntryUnparsed(RegisteredCacheEntry entry) const {
292 return GetEntryUnparsed(entry.title_id, entry.type);
293}
294
295std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const { 309std::optional<u32> RegisteredCache::GetEntryVersion(u64 title_id) const {
296 const auto meta_iter = meta.find(title_id); 310 const auto meta_iter = meta.find(title_id);
297 if (meta_iter != meta.end()) 311 if (meta_iter != meta.end())
@@ -309,10 +323,6 @@ VirtualFile RegisteredCache::GetEntryRaw(u64 title_id, ContentRecordType type) c
309 return id ? parser(GetFileAtID(*id), *id) : nullptr; 323 return id ? parser(GetFileAtID(*id), *id) : nullptr;
310} 324}
311 325
312VirtualFile RegisteredCache::GetEntryRaw(RegisteredCacheEntry entry) const {
313 return GetEntryRaw(entry.title_id, entry.type);
314}
315
316std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const { 326std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType type) const {
317 const auto raw = GetEntryRaw(title_id, type); 327 const auto raw = GetEntryRaw(title_id, type);
318 if (raw == nullptr) 328 if (raw == nullptr)
@@ -320,10 +330,6 @@ std::unique_ptr<NCA> RegisteredCache::GetEntry(u64 title_id, ContentRecordType t
320 return std::make_unique<NCA>(raw, nullptr, 0, keys); 330 return std::make_unique<NCA>(raw, nullptr, 0, keys);
321} 331}
322 332
323std::unique_ptr<NCA> RegisteredCache::GetEntry(RegisteredCacheEntry entry) const {
324 return GetEntry(entry.title_id, entry.type);
325}
326
327template <typename T> 333template <typename T>
328void RegisteredCache::IterateAllMetadata( 334void RegisteredCache::IterateAllMetadata(
329 std::vector<T>& out, std::function<T(const CNMT&, const ContentRecord&)> proc, 335 std::vector<T>& out, std::function<T(const CNMT&, const ContentRecord&)> proc,
@@ -348,25 +354,14 @@ void RegisteredCache::IterateAllMetadata(
348 } 354 }
349} 355}
350 356
351std::vector<RegisteredCacheEntry> RegisteredCache::ListEntries() const { 357std::vector<ContentProviderEntry> RegisteredCache::ListEntriesFilter(
352 std::vector<RegisteredCacheEntry> out;
353 IterateAllMetadata<RegisteredCacheEntry>(
354 out,
355 [](const CNMT& c, const ContentRecord& r) {
356 return RegisteredCacheEntry{c.GetTitleID(), r.type};
357 },
358 [](const CNMT& c, const ContentRecord& r) { return true; });
359 return out;
360}
361
362std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter(
363 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, 358 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
364 std::optional<u64> title_id) const { 359 std::optional<u64> title_id) const {
365 std::vector<RegisteredCacheEntry> out; 360 std::vector<ContentProviderEntry> out;
366 IterateAllMetadata<RegisteredCacheEntry>( 361 IterateAllMetadata<ContentProviderEntry>(
367 out, 362 out,
368 [](const CNMT& c, const ContentRecord& r) { 363 [](const CNMT& c, const ContentRecord& r) {
369 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 364 return ContentProviderEntry{c.GetTitleID(), r.type};
370 }, 365 },
371 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { 366 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) {
372 if (title_type && *title_type != c.GetType()) 367 if (title_type && *title_type != c.GetType())
@@ -521,37 +516,56 @@ bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
521 }) != yuzu_meta.end(); 516 }) != yuzu_meta.end();
522} 517}
523 518
524RegisteredCacheUnion::RegisteredCacheUnion(std::vector<RegisteredCache*> caches) 519ContentProviderUnion::~ContentProviderUnion() = default;
525 : caches(std::move(caches)) {}
526 520
527void RegisteredCacheUnion::Refresh() { 521void ContentProviderUnion::SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider) {
528 for (const auto& c : caches) 522 providers[slot] = provider;
529 c->Refresh();
530} 523}
531 524
532bool RegisteredCacheUnion::HasEntry(u64 title_id, ContentRecordType type) const { 525void ContentProviderUnion::ClearSlot(ContentProviderUnionSlot slot) {
533 return std::any_of(caches.begin(), caches.end(), [title_id, type](const auto& cache) { 526 providers[slot] = nullptr;
534 return cache->HasEntry(title_id, type);
535 });
536} 527}
537 528
538bool RegisteredCacheUnion::HasEntry(RegisteredCacheEntry entry) const { 529void ContentProviderUnion::Refresh() {
539 return HasEntry(entry.title_id, entry.type); 530 for (auto& provider : providers) {
531 if (provider.second == nullptr)
532 continue;
533
534 provider.second->Refresh();
535 }
540} 536}
541 537
542std::optional<u32> RegisteredCacheUnion::GetEntryVersion(u64 title_id) const { 538bool ContentProviderUnion::HasEntry(u64 title_id, ContentRecordType type) const {
543 for (const auto& c : caches) { 539 for (const auto& provider : providers) {
544 const auto res = c->GetEntryVersion(title_id); 540 if (provider.second == nullptr)
545 if (res) 541 continue;
542
543 if (provider.second->HasEntry(title_id, type))
544 return true;
545 }
546
547 return false;
548}
549
550std::optional<u32> ContentProviderUnion::GetEntryVersion(u64 title_id) const {
551 for (const auto& provider : providers) {
552 if (provider.second == nullptr)
553 continue;
554
555 const auto res = provider.second->GetEntryVersion(title_id);
556 if (res != std::nullopt)
546 return res; 557 return res;
547 } 558 }
548 559
549 return {}; 560 return std::nullopt;
550} 561}
551 562
552VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const { 563VirtualFile ContentProviderUnion::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
553 for (const auto& c : caches) { 564 for (const auto& provider : providers) {
554 const auto res = c->GetEntryUnparsed(title_id, type); 565 if (provider.second == nullptr)
566 continue;
567
568 const auto res = provider.second->GetEntryUnparsed(title_id, type);
555 if (res != nullptr) 569 if (res != nullptr)
556 return res; 570 return res;
557 } 571 }
@@ -559,13 +573,12 @@ VirtualFile RegisteredCacheUnion::GetEntryUnparsed(u64 title_id, ContentRecordTy
559 return nullptr; 573 return nullptr;
560} 574}
561 575
562VirtualFile RegisteredCacheUnion::GetEntryUnparsed(RegisteredCacheEntry entry) const { 576VirtualFile ContentProviderUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const {
563 return GetEntryUnparsed(entry.title_id, entry.type); 577 for (const auto& provider : providers) {
564} 578 if (provider.second == nullptr)
579 continue;
565 580
566VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType type) const { 581 const auto res = provider.second->GetEntryRaw(title_id, type);
567 for (const auto& c : caches) {
568 const auto res = c->GetEntryRaw(title_id, type);
569 if (res != nullptr) 582 if (res != nullptr)
570 return res; 583 return res;
571 } 584 }
@@ -573,30 +586,56 @@ VirtualFile RegisteredCacheUnion::GetEntryRaw(u64 title_id, ContentRecordType ty
573 return nullptr; 586 return nullptr;
574} 587}
575 588
576VirtualFile RegisteredCacheUnion::GetEntryRaw(RegisteredCacheEntry entry) const { 589std::unique_ptr<NCA> ContentProviderUnion::GetEntry(u64 title_id, ContentRecordType type) const {
577 return GetEntryRaw(entry.title_id, entry.type); 590 for (const auto& provider : providers) {
578} 591 if (provider.second == nullptr)
592 continue;
579 593
580std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(u64 title_id, ContentRecordType type) const { 594 auto res = provider.second->GetEntry(title_id, type);
581 const auto raw = GetEntryRaw(title_id, type); 595 if (res != nullptr)
582 if (raw == nullptr) 596 return res;
583 return nullptr; 597 }
584 return std::make_unique<NCA>(raw); 598
599 return nullptr;
585} 600}
586 601
587std::unique_ptr<NCA> RegisteredCacheUnion::GetEntry(RegisteredCacheEntry entry) const { 602std::vector<ContentProviderEntry> ContentProviderUnion::ListEntriesFilter(
588 return GetEntry(entry.title_id, entry.type); 603 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
604 std::optional<u64> title_id) const {
605 std::vector<ContentProviderEntry> out;
606
607 for (const auto& provider : providers) {
608 if (provider.second == nullptr)
609 continue;
610
611 const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id);
612 std::copy(vec.begin(), vec.end(), std::back_inserter(out));
613 }
614
615 std::sort(out.begin(), out.end());
616 out.erase(std::unique(out.begin(), out.end()), out.end());
617 return out;
589} 618}
590 619
591std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const { 620std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>>
592 std::vector<RegisteredCacheEntry> out; 621ContentProviderUnion::ListEntriesFilterOrigin(std::optional<ContentProviderUnionSlot> origin,
593 for (const auto& c : caches) { 622 std::optional<TitleType> title_type,
594 c->IterateAllMetadata<RegisteredCacheEntry>( 623 std::optional<ContentRecordType> record_type,
595 out, 624 std::optional<u64> title_id) const {
596 [](const CNMT& c, const ContentRecord& r) { 625 std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> out;
597 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 626
598 }, 627 for (const auto& provider : providers) {
599 [](const CNMT& c, const ContentRecord& r) { return true; }); 628 if (provider.second == nullptr)
629 continue;
630
631 if (origin.has_value() && *origin != provider.first)
632 continue;
633
634 const auto vec = provider.second->ListEntriesFilter(title_type, record_type, title_id);
635 std::transform(vec.begin(), vec.end(), std::back_inserter(out),
636 [&provider](const ContentProviderEntry& entry) {
637 return std::make_pair(provider.first, entry);
638 });
600 } 639 }
601 640
602 std::sort(out.begin(), out.end()); 641 std::sort(out.begin(), out.end());
@@ -604,25 +643,61 @@ std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntries() const {
604 return out; 643 return out;
605} 644}
606 645
607std::vector<RegisteredCacheEntry> RegisteredCacheUnion::ListEntriesFilter( 646ManualContentProvider::~ManualContentProvider() = default;
647
648void ManualContentProvider::AddEntry(TitleType title_type, ContentRecordType content_type,
649 u64 title_id, VirtualFile file) {
650 entries.insert_or_assign({title_type, content_type, title_id}, file);
651}
652
653void ManualContentProvider::ClearAllEntries() {
654 entries.clear();
655}
656
657void ManualContentProvider::Refresh() {}
658
659bool ManualContentProvider::HasEntry(u64 title_id, ContentRecordType type) const {
660 return GetEntryRaw(title_id, type) != nullptr;
661}
662
663std::optional<u32> ManualContentProvider::GetEntryVersion(u64 title_id) const {
664 return std::nullopt;
665}
666
667VirtualFile ManualContentProvider::GetEntryUnparsed(u64 title_id, ContentRecordType type) const {
668 return GetEntryRaw(title_id, type);
669}
670
671VirtualFile ManualContentProvider::GetEntryRaw(u64 title_id, ContentRecordType type) const {
672 const auto iter =
673 std::find_if(entries.begin(), entries.end(), [title_id, type](const auto& entry) {
674 const auto [title_type, content_type, e_title_id] = entry.first;
675 return content_type == type && e_title_id == title_id;
676 });
677 if (iter == entries.end())
678 return nullptr;
679 return iter->second;
680}
681
682std::unique_ptr<NCA> ManualContentProvider::GetEntry(u64 title_id, ContentRecordType type) const {
683 const auto res = GetEntryRaw(title_id, type);
684 if (res == nullptr)
685 return nullptr;
686 return std::make_unique<NCA>(res, nullptr, 0, keys);
687}
688
689std::vector<ContentProviderEntry> ManualContentProvider::ListEntriesFilter(
608 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type, 690 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
609 std::optional<u64> title_id) const { 691 std::optional<u64> title_id) const {
610 std::vector<RegisteredCacheEntry> out; 692 std::vector<ContentProviderEntry> out;
611 for (const auto& c : caches) { 693
612 c->IterateAllMetadata<RegisteredCacheEntry>( 694 for (const auto& entry : entries) {
613 out, 695 const auto [e_title_type, e_content_type, e_title_id] = entry.first;
614 [](const CNMT& c, const ContentRecord& r) { 696 if ((title_type == std::nullopt || e_title_type == *title_type) &&
615 return RegisteredCacheEntry{c.GetTitleID(), r.type}; 697 (record_type == std::nullopt || e_content_type == *record_type) &&
616 }, 698 (title_id == std::nullopt || e_title_id == *title_id)) {
617 [&title_type, &record_type, &title_id](const CNMT& c, const ContentRecord& r) { 699 out.emplace_back(ContentProviderEntry{e_title_id, e_content_type});
618 if (title_type && *title_type != c.GetType()) 700 }
619 return false;
620 if (record_type && *record_type != r.type)
621 return false;
622 if (title_id && *title_id != c.GetTitleID())
623 return false;
624 return true;
625 });
626 } 701 }
627 702
628 std::sort(out.begin(), out.end()); 703 std::sort(out.begin(), out.end());
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index 3b77af4e0..ec9052653 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -21,12 +21,13 @@ class NSP;
21class XCI; 21class XCI;
22 22
23enum class ContentRecordType : u8; 23enum class ContentRecordType : u8;
24enum class NCAContentType : u8;
24enum class TitleType : u8; 25enum class TitleType : u8;
25 26
26struct ContentRecord; 27struct ContentRecord;
27 28
28using NcaID = std::array<u8, 0x10>; 29using NcaID = std::array<u8, 0x10>;
29using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>; 30using ContentProviderParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
30using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>; 31using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
31 32
32enum class InstallResult { 33enum class InstallResult {
@@ -36,7 +37,7 @@ enum class InstallResult {
36 ErrorMetaFailed, 37 ErrorMetaFailed,
37}; 38};
38 39
39struct RegisteredCacheEntry { 40struct ContentProviderEntry {
40 u64 title_id; 41 u64 title_id;
41 ContentRecordType type; 42 ContentRecordType type;
42 43
@@ -47,12 +48,46 @@ constexpr u64 GetUpdateTitleID(u64 base_title_id) {
47 return base_title_id | 0x800; 48 return base_title_id | 0x800;
48} 49}
49 50
51ContentRecordType GetCRTypeFromNCAType(NCAContentType type);
52
50// boost flat_map requires operator< for O(log(n)) lookups. 53// boost flat_map requires operator< for O(log(n)) lookups.
51bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 54bool operator<(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
52 55
53// std unique requires operator== to identify duplicates. 56// std unique requires operator== to identify duplicates.
54bool operator==(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 57bool operator==(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
55bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs); 58bool operator!=(const ContentProviderEntry& lhs, const ContentProviderEntry& rhs);
59
60class ContentProvider {
61public:
62 virtual ~ContentProvider();
63
64 virtual void Refresh() = 0;
65
66 virtual bool HasEntry(u64 title_id, ContentRecordType type) const = 0;
67 virtual bool HasEntry(ContentProviderEntry entry) const;
68
69 virtual std::optional<u32> GetEntryVersion(u64 title_id) const = 0;
70
71 virtual VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const = 0;
72 virtual VirtualFile GetEntryUnparsed(ContentProviderEntry entry) const;
73
74 virtual VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const = 0;
75 virtual VirtualFile GetEntryRaw(ContentProviderEntry entry) const;
76
77 virtual std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const = 0;
78 virtual std::unique_ptr<NCA> GetEntry(ContentProviderEntry entry) const;
79
80 virtual std::vector<ContentProviderEntry> ListEntries() const;
81
82 // If a parameter is not std::nullopt, it will be filtered for from all entries.
83 virtual std::vector<ContentProviderEntry> ListEntriesFilter(
84 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
85 std::optional<u64> title_id = {}) const = 0;
86
87protected:
88 // A single instance of KeyManager to be used by GetEntry()
89 Core::Crypto::KeyManager keys;
90};
56 91
57/* 92/*
58 * A class that catalogues NCAs in the registered directory structure. 93 * A class that catalogues NCAs in the registered directory structure.
@@ -67,39 +102,32 @@ bool operator!=(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs
67 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient 102 * (This impl also supports substituting the nca dir for an nca file, as that's more convenient
68 * when 4GB splitting can be ignored.) 103 * when 4GB splitting can be ignored.)
69 */ 104 */
70class RegisteredCache { 105class RegisteredCache : public ContentProvider {
71 friend class RegisteredCacheUnion;
72
73public: 106public:
74 // Parsing function defines the conversion from raw file to NCA. If there are other steps 107 // Parsing function defines the conversion from raw file to NCA. If there are other steps
75 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom 108 // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom
76 // parsing function. 109 // parsing function.
77 explicit RegisteredCache(VirtualDir dir, 110 explicit RegisteredCache(VirtualDir dir,
78 RegisteredCacheParsingFunction parsing_function = 111 ContentProviderParsingFunction parsing_function =
79 [](const VirtualFile& file, const NcaID& id) { return file; }); 112 [](const VirtualFile& file, const NcaID& id) { return file; });
80 ~RegisteredCache(); 113 ~RegisteredCache() override;
81 114
82 void Refresh(); 115 void Refresh() override;
83 116
84 bool HasEntry(u64 title_id, ContentRecordType type) const; 117 bool HasEntry(u64 title_id, ContentRecordType type) const override;
85 bool HasEntry(RegisteredCacheEntry entry) const;
86 118
87 std::optional<u32> GetEntryVersion(u64 title_id) const; 119 std::optional<u32> GetEntryVersion(u64 title_id) const override;
88 120
89 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const; 121 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
90 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
91 122
92 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const; 123 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
93 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
94 124
95 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const; 125 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
96 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
97 126
98 std::vector<RegisteredCacheEntry> ListEntries() const;
99 // If a parameter is not std::nullopt, it will be filtered for from all entries. 127 // If a parameter is not std::nullopt, it will be filtered for from all entries.
100 std::vector<RegisteredCacheEntry> ListEntriesFilter( 128 std::vector<ContentProviderEntry> ListEntriesFilter(
101 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, 129 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
102 std::optional<u64> title_id = {}) const; 130 std::optional<u64> title_id = {}) const override;
103 131
104 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure 132 // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure
105 // there is a meta NCA and all of them are accessible. 133 // there is a meta NCA and all of them are accessible.
@@ -131,46 +159,70 @@ private:
131 bool RawInstallYuzuMeta(const CNMT& cnmt); 159 bool RawInstallYuzuMeta(const CNMT& cnmt);
132 160
133 VirtualDir dir; 161 VirtualDir dir;
134 RegisteredCacheParsingFunction parser; 162 ContentProviderParsingFunction parser;
135 Core::Crypto::KeyManager keys;
136 163
137 // maps tid -> NcaID of meta 164 // maps tid -> NcaID of meta
138 boost::container::flat_map<u64, NcaID> meta_id; 165 std::map<u64, NcaID> meta_id;
139 // maps tid -> meta 166 // maps tid -> meta
140 boost::container::flat_map<u64, CNMT> meta; 167 std::map<u64, CNMT> meta;
141 // maps tid -> meta for CNMT in yuzu_meta 168 // maps tid -> meta for CNMT in yuzu_meta
142 boost::container::flat_map<u64, CNMT> yuzu_meta; 169 std::map<u64, CNMT> yuzu_meta;
143}; 170};
144 171
145// Combines multiple RegisteredCaches (i.e. SysNAND, UserNAND, SDMC) into one interface. 172enum class ContentProviderUnionSlot {
146class RegisteredCacheUnion { 173 SysNAND, ///< System NAND
147public: 174 UserNAND, ///< User NAND
148 explicit RegisteredCacheUnion(std::vector<RegisteredCache*> caches); 175 SDMC, ///< SD Card
149 176 FrontendManual, ///< Frontend-defined game list or similar
150 void Refresh(); 177};
151
152 bool HasEntry(u64 title_id, ContentRecordType type) const;
153 bool HasEntry(RegisteredCacheEntry entry) const;
154
155 std::optional<u32> GetEntryVersion(u64 title_id) const;
156
157 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const;
158 VirtualFile GetEntryUnparsed(RegisteredCacheEntry entry) const;
159
160 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const;
161 VirtualFile GetEntryRaw(RegisteredCacheEntry entry) const;
162
163 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const;
164 std::unique_ptr<NCA> GetEntry(RegisteredCacheEntry entry) const;
165 178
166 std::vector<RegisteredCacheEntry> ListEntries() const; 179// Combines multiple ContentProvider(s) (i.e. SysNAND, UserNAND, SDMC) into one interface.
167 // If a parameter is not std::nullopt, it will be filtered for from all entries. 180class ContentProviderUnion : public ContentProvider {
168 std::vector<RegisteredCacheEntry> ListEntriesFilter( 181public:
182 ~ContentProviderUnion() override;
183
184 void SetSlot(ContentProviderUnionSlot slot, ContentProvider* provider);
185 void ClearSlot(ContentProviderUnionSlot slot);
186
187 void Refresh() override;
188 bool HasEntry(u64 title_id, ContentRecordType type) const override;
189 std::optional<u32> GetEntryVersion(u64 title_id) const override;
190 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
191 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
192 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
193 std::vector<ContentProviderEntry> ListEntriesFilter(
194 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
195 std::optional<u64> title_id) const override;
196
197 std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> ListEntriesFilterOrigin(
198 std::optional<ContentProviderUnionSlot> origin = {},
169 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {}, 199 std::optional<TitleType> title_type = {}, std::optional<ContentRecordType> record_type = {},
170 std::optional<u64> title_id = {}) const; 200 std::optional<u64> title_id = {}) const;
171 201
172private: 202private:
173 std::vector<RegisteredCache*> caches; 203 std::map<ContentProviderUnionSlot, ContentProvider*> providers;
204};
205
206class ManualContentProvider : public ContentProvider {
207public:
208 ~ManualContentProvider() override;
209
210 void AddEntry(TitleType title_type, ContentRecordType content_type, u64 title_id,
211 VirtualFile file);
212 void ClearAllEntries();
213
214 void Refresh() override;
215 bool HasEntry(u64 title_id, ContentRecordType type) const override;
216 std::optional<u32> GetEntryVersion(u64 title_id) const override;
217 VirtualFile GetEntryUnparsed(u64 title_id, ContentRecordType type) const override;
218 VirtualFile GetEntryRaw(u64 title_id, ContentRecordType type) const override;
219 std::unique_ptr<NCA> GetEntry(u64 title_id, ContentRecordType type) const override;
220 std::vector<ContentProviderEntry> ListEntriesFilter(
221 std::optional<TitleType> title_type, std::optional<ContentRecordType> record_type,
222 std::optional<u64> title_id) const override;
223
224private:
225 std::map<std::tuple<TitleType, ContentRecordType, u64>, VirtualFile> entries;
174}; 226};
175 227
176} // namespace FileSys 228} // namespace FileSys