summaryrefslogtreecommitdiff
path: root/src/core/file_sys
diff options
context:
space:
mode:
authorGravatar Feng Chen2021-07-20 13:10:05 +0800
committerGravatar GitHub2021-07-20 01:10:05 -0400
commit07073734ed3785d1dee487f0c898a645fbd5f03c (patch)
treee7c72b615b7a551cc1fb8a6a336bce60e5a0d314 /src/core/file_sys
parentMerge pull request #6580 from ReinUsesLisp/xfb-radv (diff)
downloadyuzu-07073734ed3785d1dee487f0c898a645fbd5f03c.tar.gz
yuzu-07073734ed3785d1dee487f0c898a645fbd5f03c.tar.xz
yuzu-07073734ed3785d1dee487f0c898a645fbd5f03c.zip
file_sys: Support load game collection (#6582)
Adds support for loading games with multiple programs embedded within such as the Dragon Quest 1+2+3 Collection
Diffstat (limited to 'src/core/file_sys')
-rw-r--r--src/core/file_sys/card_image.cpp18
-rw-r--r--src/core/file_sys/card_image.h3
-rw-r--r--src/core/file_sys/submission_package.cpp71
-rw-r--r--src/core/file_sys/submission_package.h11
4 files changed, 57 insertions, 46 deletions
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index db2f6a955..755d3303e 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -29,7 +29,7 @@ constexpr std::array partition_names{
29 "logo", 29 "logo",
30}; 30};
31 31
32XCI::XCI(VirtualFile file_, std::size_t program_index) 32XCI::XCI(VirtualFile file_, u64 program_id, size_t program_index)
33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA}, 33 : file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
34 partitions(partition_names.size()), 34 partitions(partition_names.size()),
35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} { 35 partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
@@ -63,12 +63,12 @@ XCI::XCI(VirtualFile file_, std::size_t program_index)
63 63
64 secure_partition = std::make_shared<NSP>( 64 secure_partition = std::make_shared<NSP>(
65 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]), 65 main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]),
66 program_index); 66 program_id, program_index);
67 67
68 ncas = secure_partition->GetNCAsCollapsed(); 68 ncas = secure_partition->GetNCAsCollapsed();
69 program = 69 program =
70 secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program); 70 secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program);
71 program_nca_status = secure_partition->GetProgramStatus(secure_partition->GetProgramTitleID()); 71 program_nca_status = secure_partition->GetProgramStatus();
72 if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) { 72 if (program_nca_status == Loader::ResultStatus::ErrorNSPMissingProgramNCA) {
73 program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; 73 program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA;
74 } 74 }
@@ -174,6 +174,10 @@ u64 XCI::GetProgramTitleID() const {
174 return secure_partition->GetProgramTitleID(); 174 return secure_partition->GetProgramTitleID();
175} 175}
176 176
177std::vector<u64> XCI::GetProgramTitleIDs() const {
178 return secure_partition->GetProgramTitleIDs();
179}
180
177u32 XCI::GetSystemUpdateVersion() { 181u32 XCI::GetSystemUpdateVersion() {
178 const auto update = GetPartition(XCIPartition::Update); 182 const auto update = GetPartition(XCIPartition::Update);
179 if (update == nullptr) { 183 if (update == nullptr) {
@@ -229,9 +233,11 @@ const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const {
229} 233}
230 234
231std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const { 235std::shared_ptr<NCA> XCI::GetNCAByType(NCAContentType type) const {
232 const auto iter = 236 const auto program_id = secure_partition->GetProgramTitleID();
233 std::find_if(ncas.begin(), ncas.end(), 237 const auto iter = std::find_if(
234 [type](const std::shared_ptr<NCA>& nca) { return nca->GetType() == type; }); 238 ncas.begin(), ncas.end(), [this, type, program_id](const std::shared_ptr<NCA>& nca) {
239 return nca->GetType() == type && nca->GetTitleId() == program_id;
240 });
235 return iter == ncas.end() ? nullptr : *iter; 241 return iter == ncas.end() ? nullptr : *iter;
236} 242}
237 243
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index 4960e90fe..0fd9fa87c 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -78,7 +78,7 @@ enum class XCIPartition : u8 { Update, Normal, Secure, Logo };
78 78
79class XCI : public ReadOnlyVfsDirectory { 79class XCI : public ReadOnlyVfsDirectory {
80public: 80public:
81 explicit XCI(VirtualFile file, std::size_t program_index = 0); 81 explicit XCI(VirtualFile file, u64 program_id = 0, size_t program_index = 0);
82 ~XCI() override; 82 ~XCI() override;
83 83
84 Loader::ResultStatus GetStatus() const; 84 Loader::ResultStatus GetStatus() const;
@@ -104,6 +104,7 @@ public:
104 VirtualFile GetLogoPartitionRaw() const; 104 VirtualFile GetLogoPartitionRaw() const;
105 105
106 u64 GetProgramTitleID() const; 106 u64 GetProgramTitleID() const;
107 std::vector<u64> GetProgramTitleIDs() const;
107 u32 GetSystemUpdateVersion(); 108 u32 GetSystemUpdateVersion();
108 u64 GetSystemUpdateTitleID() const; 109 u64 GetSystemUpdateTitleID() const;
109 110
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index d51d469e3..f192dffa5 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -20,8 +20,9 @@
20 20
21namespace FileSys { 21namespace FileSys {
22 22
23NSP::NSP(VirtualFile file_, std::size_t program_index_) 23NSP::NSP(VirtualFile file_, u64 title_id_, std::size_t program_index_)
24 : file(std::move(file_)), program_index(program_index_), status{Loader::ResultStatus::Success}, 24 : file(std::move(file_)), expected_program_id(title_id_),
25 program_index(program_index_), status{Loader::ResultStatus::Success},
25 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} { 26 pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
26 if (pfs->GetStatus() != Loader::ResultStatus::Success) { 27 if (pfs->GetStatus() != Loader::ResultStatus::Success) {
27 status = pfs->GetStatus(); 28 status = pfs->GetStatus();
@@ -46,60 +47,59 @@ Loader::ResultStatus NSP::GetStatus() const {
46 return status; 47 return status;
47} 48}
48 49
49Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { 50Loader::ResultStatus NSP::GetProgramStatus() const {
50 if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) { 51 if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) {
51 return Loader::ResultStatus::Success; 52 return Loader::ResultStatus::Success;
52 } 53 }
53 54
54 const auto iter = program_status.find(title_id); 55 const auto iter = program_status.find(GetProgramTitleID());
55 if (iter == program_status.end()) 56 if (iter == program_status.end())
56 return Loader::ResultStatus::ErrorNSPMissingProgramNCA; 57 return Loader::ResultStatus::ErrorNSPMissingProgramNCA;
57 return iter->second; 58 return iter->second;
58} 59}
59 60
60u64 NSP::GetFirstTitleID() const {
61 if (IsExtractedType()) {
62 return GetProgramTitleID();
63 }
64
65 if (program_status.empty())
66 return 0;
67 return program_status.begin()->first;
68}
69
70u64 NSP::GetProgramTitleID() const { 61u64 NSP::GetProgramTitleID() const {
71 if (IsExtractedType()) { 62 if (IsExtractedType()) {
72 if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) { 63 return GetExtractedTitleID() + program_index;
73 return 0; 64 }
74 }
75 65
76 ProgramMetadata meta; 66 auto program_id = expected_program_id;
77 if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) { 67 if (program_id == 0) {
78 return meta.GetTitleID(); 68 if (!program_status.empty()) {
79 } else { 69 program_id = program_status.begin()->first;
80 return 0;
81 } 70 }
82 } 71 }
83 72
84 const auto out = GetFirstTitleID(); 73 program_id = program_id + program_index;
85 if ((out & 0x800) == 0) 74 if (program_status.find(program_id) != program_status.end()) {
86 return out; 75 return program_id;
76 }
87 77
88 const auto ids = GetTitleIDs(); 78 const auto ids = GetProgramTitleIDs();
89 const auto iter = 79 const auto iter =
90 std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; }); 80 std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; });
91 return iter == ids.end() ? out : *iter; 81 return iter == ids.end() ? 0 : *iter;
82}
83
84u64 NSP::GetExtractedTitleID() const {
85 if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) {
86 return 0;
87 }
88
89 ProgramMetadata meta;
90 if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) {
91 return meta.GetTitleID();
92 } else {
93 return 0;
94 }
92} 95}
93 96
94std::vector<u64> NSP::GetTitleIDs() const { 97std::vector<u64> NSP::GetProgramTitleIDs() const {
95 if (IsExtractedType()) { 98 if (IsExtractedType()) {
96 return {GetProgramTitleID()}; 99 return {GetExtractedTitleID()};
97 } 100 }
98 101
99 std::vector<u64> out; 102 std::vector<u64> out{program_ids.cbegin(), program_ids.cend()};
100 out.reserve(ncas.size());
101 for (const auto& kv : ncas)
102 out.push_back(kv.first);
103 return out; 103 return out;
104} 104}
105 105
@@ -146,7 +146,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
146 if (extracted) 146 if (extracted)
147 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 147 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
148 148
149 const auto title_id_iter = ncas.find(title_id + program_index); 149 const auto title_id_iter = ncas.find(title_id);
150 if (title_id_iter == ncas.end()) 150 if (title_id_iter == ncas.end())
151 return nullptr; 151 return nullptr;
152 152
@@ -160,7 +160,7 @@ std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType
160VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const { 160VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const {
161 if (extracted) 161 if (extracted)
162 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); 162 LOG_WARNING(Service_FS, "called on an NSP that is of type extracted.");
163 const auto nca = GetNCA(title_id, type); 163 const auto nca = GetNCA(title_id, type, title_type);
164 if (nca != nullptr) 164 if (nca != nullptr)
165 return nca->GetBaseFile(); 165 return nca->GetBaseFile();
166 return nullptr; 166 return nullptr;
@@ -286,6 +286,7 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
286 286
287 if (next_nca->GetType() == NCAContentType::Program) { 287 if (next_nca->GetType() == NCAContentType::Program) {
288 program_status[next_nca->GetTitleId()] = next_nca->GetStatus(); 288 program_status[next_nca->GetTitleId()] = next_nca->GetStatus();
289 program_ids.insert(next_nca->GetTitleId() & 0xFFFFFFFFFFFFF000);
289 } 290 }
290 291
291 if (next_nca->GetStatus() != Loader::ResultStatus::Success && 292 if (next_nca->GetStatus() != Loader::ResultStatus::Success &&
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index ecb3b6f15..030f36c09 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -6,6 +6,7 @@
6 6
7#include <map> 7#include <map>
8#include <memory> 8#include <memory>
9#include <set>
9#include <vector> 10#include <vector>
10#include "common/common_types.h" 11#include "common/common_types.h"
11#include "core/file_sys/vfs.h" 12#include "core/file_sys/vfs.h"
@@ -27,15 +28,15 @@ enum class ContentRecordType : u8;
27 28
28class NSP : public ReadOnlyVfsDirectory { 29class NSP : public ReadOnlyVfsDirectory {
29public: 30public:
30 explicit NSP(VirtualFile file_, std::size_t program_index_ = 0); 31 explicit NSP(VirtualFile file_, u64 title_id = 0, std::size_t program_index_ = 0);
31 ~NSP() override; 32 ~NSP() override;
32 33
33 Loader::ResultStatus GetStatus() const; 34 Loader::ResultStatus GetStatus() const;
34 Loader::ResultStatus GetProgramStatus(u64 title_id) const; 35 Loader::ResultStatus GetProgramStatus() const;
35 // Should only be used when one title id can be assured. 36 // Should only be used when one title id can be assured.
36 u64 GetFirstTitleID() const;
37 u64 GetProgramTitleID() const; 37 u64 GetProgramTitleID() const;
38 std::vector<u64> GetTitleIDs() const; 38 u64 GetExtractedTitleID() const;
39 std::vector<u64> GetProgramTitleIDs() const;
39 40
40 bool IsExtractedType() const; 41 bool IsExtractedType() const;
41 42
@@ -69,6 +70,7 @@ private:
69 70
70 VirtualFile file; 71 VirtualFile file;
71 72
73 const u64 expected_program_id;
72 const std::size_t program_index; 74 const std::size_t program_index;
73 75
74 bool extracted = false; 76 bool extracted = false;
@@ -78,6 +80,7 @@ private:
78 std::shared_ptr<PartitionFilesystem> pfs; 80 std::shared_ptr<PartitionFilesystem> pfs;
79 // Map title id -> {map type -> NCA} 81 // Map title id -> {map type -> NCA}
80 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas; 82 std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
83 std::set<u64> program_ids;
81 std::vector<VirtualFile> ticket_files; 84 std::vector<VirtualFile> ticket_files;
82 85
83 Core::Crypto::KeyManager& keys; 86 Core::Crypto::KeyManager& keys;