summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar bunnei2018-10-18 21:48:09 -0400
committerGravatar GitHub2018-10-18 21:48:09 -0400
commit7f152f22737dbd632568dded33d1fe1435d80376 (patch)
tree01fd3dd3ad3f368c27a36647642850ba2eed08b1 /src
parentMerge pull request #1521 from ogniK5377/imp-mmu (diff)
parentcontent_archive: Simpify assignment of bktr_base_romfs in the constructor (diff)
downloadyuzu-7f152f22737dbd632568dded33d1fe1435d80376.tar.gz
yuzu-7f152f22737dbd632568dded33d1fe1435d80376.tar.xz
yuzu-7f152f22737dbd632568dded33d1fe1435d80376.zip
Merge pull request #1511 from lioncash/content
content_archive: Minor reorganization changes
Diffstat (limited to 'src')
-rw-r--r--src/core/file_sys/content_archive.cpp531
-rw-r--r--src/core/file_sys/content_archive.h19
2 files changed, 292 insertions, 258 deletions
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 6dcec7816..6c356d85d 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -97,11 +97,288 @@ union NCASectionHeader {
97}; 97};
98static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); 98static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
99 99
100bool IsValidNCA(const NCAHeader& header) { 100static bool IsValidNCA(const NCAHeader& header) {
101 // TODO(DarkLordZach): Add NCA2/NCA0 support. 101 // TODO(DarkLordZach): Add NCA2/NCA0 support.
102 return header.magic == Common::MakeMagic('N', 'C', 'A', '3'); 102 return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
103} 103}
104 104
105NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
106 : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
107 if (file == nullptr) {
108 status = Loader::ResultStatus::ErrorNullFile;
109 return;
110 }
111
112 if (sizeof(NCAHeader) != file->ReadObject(&header)) {
113 LOG_ERROR(Loader, "File reader errored out during header read.");
114 status = Loader::ResultStatus::ErrorBadNCAHeader;
115 return;
116 }
117
118 if (!HandlePotentialHeaderDecryption()) {
119 return;
120 }
121
122 has_rights_id = std::any_of(header.rights_id.begin(), header.rights_id.end(),
123 [](char c) { return c != '\0'; });
124
125 const std::vector<NCASectionHeader> sections = ReadSectionHeaders();
126 is_update = std::any_of(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
127 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
128 });
129
130 if (!ReadSections(sections, bktr_base_ivfc_offset)) {
131 return;
132 }
133
134 status = Loader::ResultStatus::Success;
135}
136
137NCA::~NCA() = default;
138
139bool NCA::CheckSupportedNCA(const NCAHeader& nca_header) {
140 if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
141 status = Loader::ResultStatus::ErrorNCA2;
142 return false;
143 }
144
145 if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
146 status = Loader::ResultStatus::ErrorNCA0;
147 return false;
148 }
149
150 return true;
151}
152
153bool NCA::HandlePotentialHeaderDecryption() {
154 if (IsValidNCA(header)) {
155 return true;
156 }
157
158 if (!CheckSupportedNCA(header)) {
159 return false;
160 }
161
162 NCAHeader dec_header{};
163 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
164 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
165 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
166 Core::Crypto::Op::Decrypt);
167 if (IsValidNCA(dec_header)) {
168 header = dec_header;
169 encrypted = true;
170 } else {
171 if (!CheckSupportedNCA(dec_header)) {
172 return false;
173 }
174
175 if (keys.HasKey(Core::Crypto::S256KeyType::Header)) {
176 status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
177 } else {
178 status = Loader::ResultStatus::ErrorMissingHeaderKey;
179 }
180 return false;
181 }
182
183 return true;
184}
185
186std::vector<NCASectionHeader> NCA::ReadSectionHeaders() const {
187 const std::ptrdiff_t number_sections =
188 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
189 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
190
191 std::vector<NCASectionHeader> sections(number_sections);
192 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
193
194 if (encrypted) {
195 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
196 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
197 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
198 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
199 Core::Crypto::Op::Decrypt);
200 } else {
201 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
202 }
203
204 return sections;
205}
206
207bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset) {
208 for (std::size_t i = 0; i < sections.size(); ++i) {
209 const auto& section = sections[i];
210
211 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
212 if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) {
213 return false;
214 }
215 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
216 if (!ReadPFS0Section(section, header.section_tables[i])) {
217 return false;
218 }
219 }
220 }
221
222 return true;
223}
224
225bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
226 u64 bktr_base_ivfc_offset) {
227 const std::size_t base_offset = entry.media_offset * MEDIA_OFFSET_MULTIPLIER;
228 ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
229 const std::size_t romfs_offset = base_offset + ivfc_offset;
230 const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
231 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
232 auto dec = Decrypt(section, raw, romfs_offset);
233
234 if (dec == nullptr) {
235 if (status != Loader::ResultStatus::Success)
236 return false;
237 if (has_rights_id)
238 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
239 else
240 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
241 return false;
242 }
243
244 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
245 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
246 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
247 status = Loader::ResultStatus::ErrorBadBKTRHeader;
248 return false;
249 }
250
251 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
252 section.bktr.subsection.offset) {
253 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
254 return false;
255 }
256
257 const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
258 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
259 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
260 return false;
261 }
262
263 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
264 RelocationBlock relocation_block{};
265 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
266 sizeof(RelocationBlock)) {
267 status = Loader::ResultStatus::ErrorBadRelocationBlock;
268 return false;
269 }
270 SubsectionBlock subsection_block{};
271 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
272 sizeof(RelocationBlock)) {
273 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
274 return false;
275 }
276
277 std::vector<RelocationBucketRaw> relocation_buckets_raw(
278 (section.bktr.relocation.size - sizeof(RelocationBlock)) / sizeof(RelocationBucketRaw));
279 if (dec->ReadBytes(relocation_buckets_raw.data(),
280 section.bktr.relocation.size - sizeof(RelocationBlock),
281 section.bktr.relocation.offset + sizeof(RelocationBlock) - offset) !=
282 section.bktr.relocation.size - sizeof(RelocationBlock)) {
283 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
284 return false;
285 }
286
287 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
288 (section.bktr.subsection.size - sizeof(SubsectionBlock)) / sizeof(SubsectionBucketRaw));
289 if (dec->ReadBytes(subsection_buckets_raw.data(),
290 section.bktr.subsection.size - sizeof(SubsectionBlock),
291 section.bktr.subsection.offset + sizeof(SubsectionBlock) - offset) !=
292 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
293 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
294 return false;
295 }
296
297 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
298 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
299 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
300 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
301 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
302 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
303
304 u32 ctr_low;
305 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
306 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
307 subsection_buckets.back().entries.push_back({size, {0}, 0});
308
309 boost::optional<Core::Crypto::Key128> key = boost::none;
310 if (encrypted) {
311 if (has_rights_id) {
312 status = Loader::ResultStatus::Success;
313 key = GetTitlekey();
314 if (key == boost::none) {
315 status = Loader::ResultStatus::ErrorMissingTitlekey;
316 return false;
317 }
318 } else {
319 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
320 if (key == boost::none) {
321 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
322 return false;
323 }
324 }
325 }
326
327 if (bktr_base_romfs == nullptr) {
328 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
329 return false;
330 }
331
332 auto bktr = std::make_shared<BKTR>(
333 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
334 relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
335 encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
336 section.raw.section_ctr);
337
338 // BKTR applies to entire IVFC, so make an offset version to level 6
339 files.push_back(std::make_shared<OffsetVfsFile>(
340 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
341 } else {
342 files.push_back(std::move(dec));
343 }
344
345 romfs = files.back();
346 return true;
347}
348
349bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry) {
350 const u64 offset = (static_cast<u64>(entry.media_offset) * MEDIA_OFFSET_MULTIPLIER) +
351 section.pfs0.pfs0_header_offset;
352 const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
353
354 auto dec = Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
355 if (dec != nullptr) {
356 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
357
358 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
359 dirs.push_back(std::move(npfs));
360 if (IsDirectoryExeFS(dirs.back()))
361 exefs = dirs.back();
362 } else {
363 if (has_rights_id)
364 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
365 else
366 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
367 return false;
368 }
369 } else {
370 if (status != Loader::ResultStatus::Success)
371 return false;
372 if (has_rights_id)
373 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
374 else
375 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
376 return false;
377 }
378
379 return true;
380}
381
105u8 NCA::GetCryptoRevision() const { 382u8 NCA::GetCryptoRevision() const {
106 u8 master_key_id = header.crypto_type; 383 u8 master_key_id = header.crypto_type;
107 if (header.crypto_type_2 > master_key_id) 384 if (header.crypto_type_2 > master_key_id)
@@ -167,7 +444,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
167 return titlekey; 444 return titlekey;
168} 445}
169 446
170VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting_offset) { 447VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 starting_offset) {
171 if (!encrypted) 448 if (!encrypted)
172 return in; 449 return in;
173 450
@@ -215,256 +492,6 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
215 } 492 }
216} 493}
217 494
218NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
219 : file(std::move(file_)),
220 bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
221 status = Loader::ResultStatus::Success;
222
223 if (file == nullptr) {
224 status = Loader::ResultStatus::ErrorNullFile;
225 return;
226 }
227
228 if (sizeof(NCAHeader) != file->ReadObject(&header)) {
229 LOG_ERROR(Loader, "File reader errored out during header read.");
230 status = Loader::ResultStatus::ErrorBadNCAHeader;
231 return;
232 }
233
234 encrypted = false;
235
236 if (!IsValidNCA(header)) {
237 if (header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
238 status = Loader::ResultStatus::ErrorNCA2;
239 return;
240 }
241 if (header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
242 status = Loader::ResultStatus::ErrorNCA0;
243 return;
244 }
245
246 NCAHeader dec_header{};
247 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
248 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
249 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
250 Core::Crypto::Op::Decrypt);
251 if (IsValidNCA(dec_header)) {
252 header = dec_header;
253 encrypted = true;
254 } else {
255 if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
256 status = Loader::ResultStatus::ErrorNCA2;
257 return;
258 }
259 if (dec_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
260 status = Loader::ResultStatus::ErrorNCA0;
261 return;
262 }
263
264 if (!keys.HasKey(Core::Crypto::S256KeyType::Header))
265 status = Loader::ResultStatus::ErrorMissingHeaderKey;
266 else
267 status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
268 return;
269 }
270 }
271
272 has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
273 [](char c) { return c == '\0'; }) != header.rights_id.end();
274
275 const std::ptrdiff_t number_sections =
276 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
277 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
278
279 std::vector<NCASectionHeader> sections(number_sections);
280 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
281
282 if (encrypted) {
283 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
284 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
285 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
286 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
287 Core::Crypto::Op::Decrypt);
288 } else {
289 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
290 }
291
292 is_update = std::find_if(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
293 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
294 }) != sections.end();
295 ivfc_offset = 0;
296
297 for (std::ptrdiff_t i = 0; i < number_sections; ++i) {
298 auto section = sections[i];
299
300 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
301 const std::size_t base_offset =
302 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
303 ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
304 const std::size_t romfs_offset = base_offset + ivfc_offset;
305 const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
306 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
307 auto dec = Decrypt(section, raw, romfs_offset);
308
309 if (dec == nullptr) {
310 if (status != Loader::ResultStatus::Success)
311 return;
312 if (has_rights_id)
313 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
314 else
315 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
316 return;
317 }
318
319 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
320 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
321 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
322 status = Loader::ResultStatus::ErrorBadBKTRHeader;
323 return;
324 }
325
326 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
327 section.bktr.subsection.offset) {
328 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
329 return;
330 }
331
332 const u64 size =
333 MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
334 header.section_tables[i].media_offset);
335 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
336 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
337 return;
338 }
339
340 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
341 RelocationBlock relocation_block{};
342 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
343 sizeof(RelocationBlock)) {
344 status = Loader::ResultStatus::ErrorBadRelocationBlock;
345 return;
346 }
347 SubsectionBlock subsection_block{};
348 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
349 sizeof(RelocationBlock)) {
350 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
351 return;
352 }
353
354 std::vector<RelocationBucketRaw> relocation_buckets_raw(
355 (section.bktr.relocation.size - sizeof(RelocationBlock)) /
356 sizeof(RelocationBucketRaw));
357 if (dec->ReadBytes(relocation_buckets_raw.data(),
358 section.bktr.relocation.size - sizeof(RelocationBlock),
359 section.bktr.relocation.offset + sizeof(RelocationBlock) -
360 offset) !=
361 section.bktr.relocation.size - sizeof(RelocationBlock)) {
362 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
363 return;
364 }
365
366 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
367 (section.bktr.subsection.size - sizeof(SubsectionBlock)) /
368 sizeof(SubsectionBucketRaw));
369 if (dec->ReadBytes(subsection_buckets_raw.data(),
370 section.bktr.subsection.size - sizeof(SubsectionBlock),
371 section.bktr.subsection.offset + sizeof(SubsectionBlock) -
372 offset) !=
373 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
374 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
375 return;
376 }
377
378 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
379 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
380 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
381 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
382 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
383 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
384
385 u32 ctr_low;
386 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
387 subsection_buckets.back().entries.push_back(
388 {section.bktr.relocation.offset, {0}, ctr_low});
389 subsection_buckets.back().entries.push_back({size, {0}, 0});
390
391 boost::optional<Core::Crypto::Key128> key = boost::none;
392 if (encrypted) {
393 if (has_rights_id) {
394 status = Loader::ResultStatus::Success;
395 key = GetTitlekey();
396 if (key == boost::none) {
397 status = Loader::ResultStatus::ErrorMissingTitlekey;
398 return;
399 }
400 } else {
401 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
402 if (key == boost::none) {
403 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
404 return;
405 }
406 }
407 }
408
409 if (bktr_base_romfs == nullptr) {
410 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
411 return;
412 }
413
414 auto bktr = std::make_shared<BKTR>(
415 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
416 relocation_block, relocation_buckets, subsection_block, subsection_buckets,
417 encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset,
418 bktr_base_ivfc_offset, section.raw.section_ctr);
419
420 // BKTR applies to entire IVFC, so make an offset version to level 6
421
422 files.push_back(std::make_shared<OffsetVfsFile>(
423 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
424 romfs = files.back();
425 } else {
426 files.push_back(std::move(dec));
427 romfs = files.back();
428 }
429 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
430 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
431 MEDIA_OFFSET_MULTIPLIER) +
432 section.pfs0.pfs0_header_offset;
433 u64 size = MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
434 header.section_tables[i].media_offset);
435 auto dec =
436 Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
437 if (dec != nullptr) {
438 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
439
440 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
441 dirs.push_back(std::move(npfs));
442 if (IsDirectoryExeFS(dirs.back()))
443 exefs = dirs.back();
444 } else {
445 if (has_rights_id)
446 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
447 else
448 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
449 return;
450 }
451 } else {
452 if (status != Loader::ResultStatus::Success)
453 return;
454 if (has_rights_id)
455 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
456 else
457 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
458 return;
459 }
460 }
461 }
462
463 status = Loader::ResultStatus::Success;
464}
465
466NCA::~NCA() = default;
467
468Loader::ResultStatus NCA::GetStatus() const { 495Loader::ResultStatus NCA::GetStatus() const {
469 return status; 496 return status;
470} 497}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index f9f66cae9..1c903cd3f 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -73,8 +73,6 @@ inline bool IsDirectoryExeFS(const std::shared_ptr<VfsDirectory>& pfs) {
73 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr; 73 return pfs->GetFile("main") != nullptr && pfs->GetFile("main.npdm") != nullptr;
74} 74}
75 75
76bool IsValidNCA(const NCAHeader& header);
77
78// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner. 76// An implementation of VfsDirectory that represents a Nintendo Content Archive (NCA) conatiner.
79// After construction, use GetStatus to determine if the file is valid and ready to be used. 77// After construction, use GetStatus to determine if the file is valid and ready to be used.
80class NCA : public ReadOnlyVfsDirectory { 78class NCA : public ReadOnlyVfsDirectory {
@@ -106,10 +104,19 @@ protected:
106 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 104 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
107 105
108private: 106private:
107 bool CheckSupportedNCA(const NCAHeader& header);
108 bool HandlePotentialHeaderDecryption();
109
110 std::vector<NCASectionHeader> ReadSectionHeaders() const;
111 bool ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset);
112 bool ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
113 u64 bktr_base_ivfc_offset);
114 bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
115
109 u8 GetCryptoRevision() const; 116 u8 GetCryptoRevision() const;
110 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; 117 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
111 boost::optional<Core::Crypto::Key128> GetTitlekey(); 118 boost::optional<Core::Crypto::Key128> GetTitlekey();
112 VirtualFile Decrypt(NCASectionHeader header, VirtualFile in, u64 starting_offset); 119 VirtualFile Decrypt(const NCASectionHeader& header, VirtualFile in, u64 starting_offset);
113 120
114 std::vector<VirtualDir> dirs; 121 std::vector<VirtualDir> dirs;
115 std::vector<VirtualFile> files; 122 std::vector<VirtualFile> files;
@@ -118,15 +125,15 @@ private:
118 VirtualDir exefs = nullptr; 125 VirtualDir exefs = nullptr;
119 VirtualFile file; 126 VirtualFile file;
120 VirtualFile bktr_base_romfs; 127 VirtualFile bktr_base_romfs;
121 u64 ivfc_offset; 128 u64 ivfc_offset = 0;
122 129
123 NCAHeader header{}; 130 NCAHeader header{};
124 bool has_rights_id{}; 131 bool has_rights_id{};
125 132
126 Loader::ResultStatus status{}; 133 Loader::ResultStatus status{};
127 134
128 bool encrypted; 135 bool encrypted = false;
129 bool is_update; 136 bool is_update = false;
130 137
131 Core::Crypto::KeyManager keys; 138 Core::Crypto::KeyManager keys;
132}; 139};