diff options
| author | 2018-08-25 19:01:46 -0400 | |
|---|---|---|
| committer | 2018-09-04 16:22:25 -0400 | |
| commit | 1efe5a76b11a5e7fd8071686acaf1b060e721aaa (patch) | |
| tree | a78d2d299481467dd5d72e21b0151f409daf05ff | |
| parent | registration: Add RegisteredCacheUnion (diff) | |
| download | yuzu-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.cpp | 171 | ||||
| -rw-r--r-- | src/core/file_sys/content_archive.h | 8 |
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 | }; |
| 69 | static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); | 70 | static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size."); |
| 70 | 71 | ||
| 72 | struct 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 | }; | ||
| 80 | static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size."); | ||
| 81 | |||
| 82 | struct 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 | }; | ||
| 90 | static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size."); | ||
| 91 | |||
| 71 | union NCASectionHeader { | 92 | union NCASectionHeader { |
| 72 | NCASectionRaw raw; | 93 | NCASectionRaw raw; |
| 73 | PFS0Superblock pfs0; | 94 | PFS0Superblock pfs0; |
| 74 | RomFSSuperblock romfs; | 95 | RomFSSuperblock romfs; |
| 96 | BKTRSuperblock bktr; | ||
| 75 | }; | 97 | }; |
| 76 | static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size."); | 98 | static_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 | ||
| 193 | NCA::NCA(VirtualFile file_) : file(std::move(file_)) { | 218 | NCA::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 | ||
| 351 | u64 NCA::GetTitleId() const { | 490 | u64 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 | ||
| 498 | bool NCA::IsUpdate() const { | ||
| 499 | return is_update; | ||
| 500 | } | ||
| 501 | |||
| 357 | VirtualFile NCA::GetRomFS() const { | 502 | VirtualFile 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 | ||
| 369 | bool NCA::IsUpdate() const { | ||
| 370 | return is_update; | ||
| 371 | } | ||
| 372 | |||
| 373 | bool NCA::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | 514 | bool 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. |
| 80 | class NCA : public ReadOnlyVfsDirectory { | 80 | class NCA : public ReadOnlyVfsDirectory { |
| 81 | public: | 81 | public: |
| 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 | |||
| 100 | protected: | 99 | protected: |
| 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 | }; |