summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Zach Hilman2018-08-25 19:01:46 -0400
committerGravatar Zach Hilman2018-09-04 16:22:25 -0400
commit1efe5a76b11a5e7fd8071686acaf1b060e721aaa (patch)
treea78d2d299481467dd5d72e21b0151f409daf05ff
parentregistration: Add RegisteredCacheUnion (diff)
downloadyuzu-1efe5a76b11a5e7fd8071686acaf1b060e721aaa.tar.gz
yuzu-1efe5a76b11a5e7fd8071686acaf1b060e721aaa.tar.xz
yuzu-1efe5a76b11a5e7fd8071686acaf1b060e721aaa.zip
content_archive: Add BKTR header parsing to NCA
-rw-r--r--src/core/file_sys/content_archive.cpp171
-rw-r--r--src/core/file_sys/content_archive.h8
2 files changed, 160 insertions, 19 deletions
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 7cfb6f36b..f0d376bf5 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -12,6 +12,7 @@
12#include "core/crypto/aes_util.h" 12#include "core/crypto/aes_util.h"
13#include "core/crypto/ctr_encryption_layer.h" 13#include "core/crypto/ctr_encryption_layer.h"
14#include "core/file_sys/content_archive.h" 14#include "core/file_sys/content_archive.h"
15#include "core/file_sys/nca_patch.h"
15#include "core/file_sys/partition_filesystem.h" 16#include "core/file_sys/partition_filesystem.h"
16#include "core/file_sys/romfs.h" 17#include "core/file_sys/romfs.h"
17#include "core/file_sys/vfs_offset.h" 18#include "core/file_sys/vfs_offset.h"
@@ -68,10 +69,31 @@ struct RomFSSuperblock {
68}; 69};
69static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); 70static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
70 71
72struct BKTRHeader {
73 u64_le offset;
74 u64_le size;
75 u32_le magic;
76 INSERT_PADDING_BYTES(0x4);
77 u32_le number_entries;
78 INSERT_PADDING_BYTES(0x4);
79};
80static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size.");
81
82struct BKTRSuperblock {
83 NCASectionHeaderBlock header_block;
84 IVFCHeader ivfc;
85 INSERT_PADDING_BYTES(0x18);
86 BKTRHeader relocation;
87 BKTRHeader subsection;
88 INSERT_PADDING_BYTES(0xC0);
89};
90static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size.");
91
71union NCASectionHeader { 92union NCASectionHeader {
72 NCASectionRaw raw; 93 NCASectionRaw raw;
73 PFS0Superblock pfs0; 94 PFS0Superblock pfs0;
74 RomFSSuperblock romfs; 95 RomFSSuperblock romfs;
96 BKTRSuperblock bktr;
75}; 97};
76static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); 98static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
77 99
@@ -104,7 +126,7 @@ boost::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType ty
104 Core::Crypto::Key128 out; 126 Core::Crypto::Key128 out;
105 if (type == NCASectionCryptoType::XTS) 127 if (type == NCASectionCryptoType::XTS)
106 std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin()); 128 std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
107 else if (type == NCASectionCryptoType::CTR) 129 else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR)
108 std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin()); 130 std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin());
109 else 131 else
110 LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}", 132 LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}",
@@ -154,6 +176,9 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
154 LOG_DEBUG(Crypto, "called with mode=NONE"); 176 LOG_DEBUG(Crypto, "called with mode=NONE");
155 return in; 177 return in;
156 case NCASectionCryptoType::CTR: 178 case NCASectionCryptoType::CTR:
179 // During normal BKTR decryption, this entire function is skipped. This is for the metadata,
180 // which uses the same CTR as usual.
181 case NCASectionCryptoType::BKTR:
157 LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset); 182 LOG_DEBUG(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
158 { 183 {
159 boost::optional<Core::Crypto::Key128> key = boost::none; 184 boost::optional<Core::Crypto::Key128> key = boost::none;
@@ -190,7 +215,9 @@ VirtualFile NCA::Decrypt(NCASectionHeader s_header, VirtualFile in, u64 starting
190 } 215 }
191} 216}
192 217
193NCA::NCA(VirtualFile file_) : file(std::move(file_)) { 218NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_)
219 : file(std::move(file_)),
220 bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
194 status = Loader::ResultStatus::Success; 221 status = Loader::ResultStatus::Success;
195 222
196 if (file == nullptr) { 223 if (file == nullptr) {
@@ -270,17 +297,15 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
270 auto section = sections[i]; 297 auto section = sections[i];
271 298
272 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) { 299 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
300 const size_t base_offset =
301 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
273 const size_t romfs_offset = 302 const size_t romfs_offset =
274 header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER + 303 base_offset + section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
275 section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
276 const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size; 304 const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
277 auto dec = 305 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
278 Decrypt(section, std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset), 306 auto dec = Decrypt(section, raw, romfs_offset);
279 romfs_offset); 307
280 if (dec != nullptr) { 308 if (dec == nullptr) {
281 files.push_back(std::move(dec));
282 romfs = files.back();
283 } else {
284 if (status != Loader::ResultStatus::Success) 309 if (status != Loader::ResultStatus::Success)
285 return; 310 return;
286 if (has_rights_id) 311 if (has_rights_id)
@@ -289,6 +314,120 @@ NCA::NCA(VirtualFile file_) : file(std::move(file_)) {
289 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey; 314 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
290 return; 315 return;
291 } 316 }
317
318 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
319 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
320 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
321 status = Loader::ResultStatus::ErrorBadBKTRHeader;
322 return;
323 }
324
325 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
326 section.bktr.subsection.offset) {
327 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
328 return;
329 }
330
331 const u64 size =
332 MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
333 header.section_tables[i].media_offset);
334 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
335 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
336 return;
337 }
338
339 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
340 RelocationBlock relocation_block{};
341 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
342 sizeof(RelocationBlock)) {
343 status = Loader::ResultStatus::ErrorBadRelocationBlock;
344 return;
345 }
346 SubsectionBlock subsection_block{};
347 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
348 sizeof(RelocationBlock)) {
349 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
350 return;
351 }
352
353 std::vector<RelocationBucketRaw> relocation_buckets_raw(
354 (section.bktr.relocation.size - sizeof(RelocationBlock)) /
355 sizeof(RelocationBucketRaw));
356 if (dec->ReadBytes(relocation_buckets_raw.data(),
357 section.bktr.relocation.size - sizeof(RelocationBlock),
358 section.bktr.relocation.offset + sizeof(RelocationBlock) -
359 offset) !=
360 section.bktr.relocation.size - sizeof(RelocationBlock)) {
361 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
362 return;
363 }
364
365 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
366 (section.bktr.subsection.size - sizeof(SubsectionBlock)) /
367 sizeof(SubsectionBucketRaw));
368 if (dec->ReadBytes(subsection_buckets_raw.data(),
369 section.bktr.subsection.size - sizeof(SubsectionBlock),
370 section.bktr.subsection.offset + sizeof(SubsectionBlock) -
371 offset) !=
372 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
373 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
374 return;
375 }
376
377 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
378 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
379 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
380 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
381 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
382 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
383
384 u32 ctr_low;
385 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
386 subsection_buckets.back().entries.push_back(
387 {section.bktr.relocation.offset, {0}, ctr_low});
388 subsection_buckets.back().entries.push_back({size, {0}, 0});
389
390 boost::optional<Core::Crypto::Key128> key = boost::none;
391 if (encrypted) {
392 if (has_rights_id) {
393 status = Loader::ResultStatus::Success;
394 key = GetTitlekey();
395 if (key == boost::none) {
396 status = Loader::ResultStatus::ErrorMissingTitlekey;
397 return;
398 }
399 } else {
400 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
401 if (key == boost::none) {
402 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
403 return;
404 }
405 }
406 }
407
408 if (bktr_base_romfs == nullptr) {
409 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
410 return;
411 }
412
413 auto bktr = std::make_shared<BKTR>(
414 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
415 relocation_block, relocation_buckets, subsection_block, subsection_buckets,
416 encrypted, encrypted ? key.get() : Core::Crypto::Key128{}, base_offset,
417 romfs_offset - base_offset, section.raw.section_ctr);
418
419 // BKTR applies to entire IVFC, so make an offset version to level 6
420
421 files.push_back(std::make_shared<OffsetVfsFile>(
422 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
423 romfs = files.back();
424 } else {
425 files.push_back(std::move(dec));
426 romfs = files.back();
427 const u64 raw_size =
428 MEDIA_OFFSET_MULTIPLIER * (header.section_tables[i].media_end_offset -
429 header.section_tables[i].media_offset);
430 }
292 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) { 431 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
293 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) * 432 u64 offset = (static_cast<u64>(header.section_tables[i].media_offset) *
294 MEDIA_OFFSET_MULTIPLIER) + 433 MEDIA_OFFSET_MULTIPLIER) +
@@ -349,11 +488,17 @@ NCAContentType NCA::GetType() const {
349} 488}
350 489
351u64 NCA::GetTitleId() const { 490u64 NCA::GetTitleId() const {
491 if (is_update || status == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS)
492 return header.title_id | 0x800;
352 if (status != Loader::ResultStatus::Success) 493 if (status != Loader::ResultStatus::Success)
353 return {}; 494 return {};
354 return header.title_id; 495 return header.title_id;
355} 496}
356 497
498bool NCA::IsUpdate() const {
499 return is_update;
500}
501
357VirtualFile NCA::GetRomFS() const { 502VirtualFile NCA::GetRomFS() const {
358 return romfs; 503 return romfs;
359} 504}
@@ -366,10 +511,6 @@ VirtualFile NCA::GetBaseFile() const {
366 return file; 511 return file;
367} 512}
368 513
369bool NCA::IsUpdate() const {
370 return is_update;
371}
372
373bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { 514bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
374 return false; 515 return false;
375} 516}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 0ea666cac..104226f3a 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -79,7 +79,7 @@ bool IsValidNCA(const NCAHeader& header);
79// After construction, use GetStatus to determine if the file is valid and ready to be used. 79// After construction, use GetStatus to determine if the file is valid and ready to be used.
80class NCA : public ReadOnlyVfsDirectory { 80class NCA : public ReadOnlyVfsDirectory {
81public: 81public:
82 explicit NCA(VirtualFile file); 82 explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr);
83 Loader::ResultStatus GetStatus() const; 83 Loader::ResultStatus GetStatus() const;
84 84
85 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override; 85 std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
@@ -89,14 +89,13 @@ public:
89 89
90 NCAContentType GetType() const; 90 NCAContentType GetType() const;
91 u64 GetTitleId() const; 91 u64 GetTitleId() const;
92 bool IsUpdate() const;
92 93
93 VirtualFile GetRomFS() const; 94 VirtualFile GetRomFS() const;
94 VirtualDir GetExeFS() const; 95 VirtualDir GetExeFS() const;
95 96
96 VirtualFile GetBaseFile() const; 97 VirtualFile GetBaseFile() const;
97 98
98 bool IsUpdate() const;
99
100protected: 99protected:
101 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 100 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
102 101
@@ -112,14 +111,15 @@ private:
112 VirtualFile romfs = nullptr; 111 VirtualFile romfs = nullptr;
113 VirtualDir exefs = nullptr; 112 VirtualDir exefs = nullptr;
114 VirtualFile file; 113 VirtualFile file;
114 VirtualFile bktr_base_romfs;
115 115
116 NCAHeader header{}; 116 NCAHeader header{};
117 bool has_rights_id{}; 117 bool has_rights_id{};
118 bool is_update{};
119 118
120 Loader::ResultStatus status{}; 119 Loader::ResultStatus status{};
121 120
122 bool encrypted; 121 bool encrypted;
122 bool is_update;
123 123
124 Core::Crypto::KeyManager keys; 124 Core::Crypto::KeyManager keys;
125}; 125};