content_archive.cpp 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
  2. // SPDX-License-Identifier: GPL-2.0-or-later
  3. #include <algorithm>
  4. #include <cstring>
  5. #include <optional>
  6. #include <utility>
  7. #include "common/logging/log.h"
  8. #include "core/crypto/aes_util.h"
  9. #include "core/crypto/ctr_encryption_layer.h"
  10. #include "core/crypto/key_manager.h"
  11. #include "core/file_sys/content_archive.h"
  12. #include "core/file_sys/nca_patch.h"
  13. #include "core/file_sys/partition_filesystem.h"
  14. #include "core/file_sys/vfs_offset.h"
  15. #include "core/loader/loader.h"
  16. namespace FileSys {
  17. // Media offsets in headers are stored divided by 512. Mult. by this to get real offset.
  18. constexpr u64 MEDIA_OFFSET_MULTIPLIER = 0x200;
  19. constexpr u64 SECTION_HEADER_SIZE = 0x200;
  20. constexpr u64 SECTION_HEADER_OFFSET = 0x400;
  21. constexpr u32 IVFC_MAX_LEVEL = 6;
  22. enum class NCASectionFilesystemType : u8 {
  23. PFS0 = 0x2,
  24. ROMFS = 0x3,
  25. };
  26. struct IVFCLevel {
  27. u64_le offset;
  28. u64_le size;
  29. u32_le block_size;
  30. u32_le reserved;
  31. };
  32. static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
  33. struct IVFCHeader {
  34. u32_le magic;
  35. u32_le magic_number;
  36. INSERT_PADDING_BYTES_NOINIT(8);
  37. std::array<IVFCLevel, 6> levels;
  38. INSERT_PADDING_BYTES_NOINIT(64);
  39. };
  40. static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
  41. struct NCASectionHeaderBlock {
  42. INSERT_PADDING_BYTES_NOINIT(3);
  43. NCASectionFilesystemType filesystem_type;
  44. NCASectionCryptoType crypto_type;
  45. INSERT_PADDING_BYTES_NOINIT(3);
  46. };
  47. static_assert(sizeof(NCASectionHeaderBlock) == 0x8, "NCASectionHeaderBlock has incorrect size.");
  48. struct NCASectionRaw {
  49. NCASectionHeaderBlock header;
  50. std::array<u8, 0x138> block_data;
  51. std::array<u8, 0x8> section_ctr;
  52. INSERT_PADDING_BYTES_NOINIT(0xB8);
  53. };
  54. static_assert(sizeof(NCASectionRaw) == 0x200, "NCASectionRaw has incorrect size.");
  55. struct PFS0Superblock {
  56. NCASectionHeaderBlock header_block;
  57. std::array<u8, 0x20> hash;
  58. u32_le size;
  59. INSERT_PADDING_BYTES_NOINIT(4);
  60. u64_le hash_table_offset;
  61. u64_le hash_table_size;
  62. u64_le pfs0_header_offset;
  63. u64_le pfs0_size;
  64. INSERT_PADDING_BYTES_NOINIT(0x1B0);
  65. };
  66. static_assert(sizeof(PFS0Superblock) == 0x200, "PFS0Superblock has incorrect size.");
  67. struct RomFSSuperblock {
  68. NCASectionHeaderBlock header_block;
  69. IVFCHeader ivfc;
  70. INSERT_PADDING_BYTES_NOINIT(0x118);
  71. };
  72. static_assert(sizeof(RomFSSuperblock) == 0x200, "RomFSSuperblock has incorrect size.");
  73. struct BKTRHeader {
  74. u64_le offset;
  75. u64_le size;
  76. u32_le magic;
  77. INSERT_PADDING_BYTES_NOINIT(0x4);
  78. u32_le number_entries;
  79. INSERT_PADDING_BYTES_NOINIT(0x4);
  80. };
  81. static_assert(sizeof(BKTRHeader) == 0x20, "BKTRHeader has incorrect size.");
  82. struct BKTRSuperblock {
  83. NCASectionHeaderBlock header_block;
  84. IVFCHeader ivfc;
  85. INSERT_PADDING_BYTES_NOINIT(0x18);
  86. BKTRHeader relocation;
  87. BKTRHeader subsection;
  88. INSERT_PADDING_BYTES_NOINIT(0xC0);
  89. };
  90. static_assert(sizeof(BKTRSuperblock) == 0x200, "BKTRSuperblock has incorrect size.");
  91. union NCASectionHeader {
  92. NCASectionRaw raw{};
  93. PFS0Superblock pfs0;
  94. RomFSSuperblock romfs;
  95. BKTRSuperblock bktr;
  96. };
  97. static_assert(sizeof(NCASectionHeader) == 0x200, "NCASectionHeader has incorrect size.");
  98. static bool IsValidNCA(const NCAHeader& header) {
  99. // TODO(DarkLordZach): Add NCA2/NCA0 support.
  100. return header.magic == Common::MakeMagic('N', 'C', 'A', '3');
  101. }
  102. NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
  103. : file(std::move(file_)),
  104. bktr_base_romfs(std::move(bktr_base_romfs_)), keys{Core::Crypto::KeyManager::Instance()} {
  105. if (file == nullptr) {
  106. status = Loader::ResultStatus::ErrorNullFile;
  107. return;
  108. }
  109. if (sizeof(NCAHeader) != file->ReadObject(&header)) {
  110. LOG_ERROR(Loader, "File reader errored out during header read.");
  111. status = Loader::ResultStatus::ErrorBadNCAHeader;
  112. return;
  113. }
  114. if (!HandlePotentialHeaderDecryption()) {
  115. return;
  116. }
  117. has_rights_id = std::ranges::any_of(header.rights_id, [](char c) { return c != '\0'; });
  118. const std::vector<NCASectionHeader> sections = ReadSectionHeaders();
  119. is_update = std::ranges::any_of(sections, [](const NCASectionHeader& nca_header) {
  120. return nca_header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
  121. });
  122. if (!ReadSections(sections, bktr_base_ivfc_offset)) {
  123. return;
  124. }
  125. status = Loader::ResultStatus::Success;
  126. }
  127. NCA::~NCA() = default;
  128. bool NCA::CheckSupportedNCA(const NCAHeader& nca_header) {
  129. if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
  130. status = Loader::ResultStatus::ErrorNCA2;
  131. return false;
  132. }
  133. if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
  134. status = Loader::ResultStatus::ErrorNCA0;
  135. return false;
  136. }
  137. return true;
  138. }
  139. bool NCA::HandlePotentialHeaderDecryption() {
  140. if (IsValidNCA(header)) {
  141. return true;
  142. }
  143. if (!CheckSupportedNCA(header)) {
  144. return false;
  145. }
  146. NCAHeader dec_header{};
  147. Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
  148. keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
  149. cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
  150. Core::Crypto::Op::Decrypt);
  151. if (IsValidNCA(dec_header)) {
  152. header = dec_header;
  153. encrypted = true;
  154. } else {
  155. if (!CheckSupportedNCA(dec_header)) {
  156. return false;
  157. }
  158. if (keys.HasKey(Core::Crypto::S256KeyType::Header)) {
  159. status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
  160. } else {
  161. status = Loader::ResultStatus::ErrorMissingHeaderKey;
  162. }
  163. return false;
  164. }
  165. return true;
  166. }
  167. std::vector<NCASectionHeader> NCA::ReadSectionHeaders() const {
  168. const std::ptrdiff_t number_sections =
  169. std::ranges::count_if(header.section_tables, [](const NCASectionTableEntry& entry) {
  170. return entry.media_offset > 0;
  171. });
  172. std::vector<NCASectionHeader> sections(number_sections);
  173. const auto length_sections = SECTION_HEADER_SIZE * number_sections;
  174. if (encrypted) {
  175. auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
  176. Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
  177. keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
  178. cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
  179. Core::Crypto::Op::Decrypt);
  180. } else {
  181. file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
  182. }
  183. return sections;
  184. }
  185. bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset) {
  186. for (std::size_t i = 0; i < sections.size(); ++i) {
  187. const auto& section = sections[i];
  188. if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
  189. if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) {
  190. return false;
  191. }
  192. } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
  193. if (!ReadPFS0Section(section, header.section_tables[i])) {
  194. return false;
  195. }
  196. }
  197. }
  198. return true;
  199. }
  200. bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
  201. u64 bktr_base_ivfc_offset) {
  202. const std::size_t base_offset = entry.media_offset * MEDIA_OFFSET_MULTIPLIER;
  203. ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
  204. const std::size_t romfs_offset = base_offset + ivfc_offset;
  205. const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
  206. auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
  207. auto dec = Decrypt(section, raw, romfs_offset);
  208. if (dec == nullptr) {
  209. if (status != Loader::ResultStatus::Success)
  210. return false;
  211. if (has_rights_id)
  212. status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
  213. else
  214. status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
  215. return false;
  216. }
  217. if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
  218. if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
  219. section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
  220. status = Loader::ResultStatus::ErrorBadBKTRHeader;
  221. return false;
  222. }
  223. if (section.bktr.relocation.offset + section.bktr.relocation.size !=
  224. section.bktr.subsection.offset) {
  225. status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
  226. return false;
  227. }
  228. const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
  229. if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
  230. status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
  231. return false;
  232. }
  233. const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
  234. RelocationBlock relocation_block{};
  235. if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
  236. sizeof(RelocationBlock)) {
  237. status = Loader::ResultStatus::ErrorBadRelocationBlock;
  238. return false;
  239. }
  240. SubsectionBlock subsection_block{};
  241. if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
  242. sizeof(RelocationBlock)) {
  243. status = Loader::ResultStatus::ErrorBadSubsectionBlock;
  244. return false;
  245. }
  246. std::vector<RelocationBucketRaw> relocation_buckets_raw(
  247. (section.bktr.relocation.size - sizeof(RelocationBlock)) / sizeof(RelocationBucketRaw));
  248. if (dec->ReadBytes(relocation_buckets_raw.data(),
  249. section.bktr.relocation.size - sizeof(RelocationBlock),
  250. section.bktr.relocation.offset + sizeof(RelocationBlock) - offset) !=
  251. section.bktr.relocation.size - sizeof(RelocationBlock)) {
  252. status = Loader::ResultStatus::ErrorBadRelocationBuckets;
  253. return false;
  254. }
  255. std::vector<SubsectionBucketRaw> subsection_buckets_raw(
  256. (section.bktr.subsection.size - sizeof(SubsectionBlock)) / sizeof(SubsectionBucketRaw));
  257. if (dec->ReadBytes(subsection_buckets_raw.data(),
  258. section.bktr.subsection.size - sizeof(SubsectionBlock),
  259. section.bktr.subsection.offset + sizeof(SubsectionBlock) - offset) !=
  260. section.bktr.subsection.size - sizeof(SubsectionBlock)) {
  261. status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
  262. return false;
  263. }
  264. std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
  265. std::ranges::transform(relocation_buckets_raw, relocation_buckets.begin(),
  266. &ConvertRelocationBucketRaw);
  267. std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
  268. std::ranges::transform(subsection_buckets_raw, subsection_buckets.begin(),
  269. &ConvertSubsectionBucketRaw);
  270. u32 ctr_low;
  271. std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
  272. subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
  273. subsection_buckets.back().entries.push_back({size, {0}, 0});
  274. std::optional<Core::Crypto::Key128> key;
  275. if (encrypted) {
  276. if (has_rights_id) {
  277. status = Loader::ResultStatus::Success;
  278. key = GetTitlekey();
  279. if (!key) {
  280. status = Loader::ResultStatus::ErrorMissingTitlekey;
  281. return false;
  282. }
  283. } else {
  284. key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
  285. if (!key) {
  286. status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
  287. return false;
  288. }
  289. }
  290. }
  291. if (bktr_base_romfs == nullptr) {
  292. status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
  293. return false;
  294. }
  295. auto bktr = std::make_shared<BKTR>(
  296. bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
  297. relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
  298. encrypted ? *key : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
  299. section.raw.section_ctr);
  300. // BKTR applies to entire IVFC, so make an offset version to level 6
  301. files.push_back(std::make_shared<OffsetVfsFile>(
  302. bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
  303. } else {
  304. files.push_back(std::move(dec));
  305. }
  306. romfs = files.back();
  307. return true;
  308. }
  309. bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry) {
  310. const u64 offset = (static_cast<u64>(entry.media_offset) * MEDIA_OFFSET_MULTIPLIER) +
  311. section.pfs0.pfs0_header_offset;
  312. const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
  313. auto dec = Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
  314. if (dec != nullptr) {
  315. auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
  316. if (npfs->GetStatus() == Loader::ResultStatus::Success) {
  317. dirs.push_back(std::move(npfs));
  318. if (IsDirectoryExeFS(dirs.back()))
  319. exefs = dirs.back();
  320. else if (IsDirectoryLogoPartition(dirs.back()))
  321. logo = dirs.back();
  322. } else {
  323. if (has_rights_id)
  324. status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
  325. else
  326. status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
  327. return false;
  328. }
  329. } else {
  330. if (status != Loader::ResultStatus::Success)
  331. return false;
  332. if (has_rights_id)
  333. status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
  334. else
  335. status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
  336. return false;
  337. }
  338. return true;
  339. }
  340. u8 NCA::GetCryptoRevision() const {
  341. u8 master_key_id = header.crypto_type;
  342. if (header.crypto_type_2 > master_key_id)
  343. master_key_id = header.crypto_type_2;
  344. if (master_key_id > 0)
  345. --master_key_id;
  346. return master_key_id;
  347. }
  348. std::optional<Core::Crypto::Key128> NCA::GetKeyAreaKey(NCASectionCryptoType type) const {
  349. const auto master_key_id = GetCryptoRevision();
  350. if (!keys.HasKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index)) {
  351. return std::nullopt;
  352. }
  353. std::vector<u8> key_area(header.key_area.begin(), header.key_area.end());
  354. Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
  355. keys.GetKey(Core::Crypto::S128KeyType::KeyArea, master_key_id, header.key_index),
  356. Core::Crypto::Mode::ECB);
  357. cipher.Transcode(key_area.data(), key_area.size(), key_area.data(), Core::Crypto::Op::Decrypt);
  358. Core::Crypto::Key128 out{};
  359. if (type == NCASectionCryptoType::XTS) {
  360. std::copy(key_area.begin(), key_area.begin() + 0x10, out.begin());
  361. } else if (type == NCASectionCryptoType::CTR || type == NCASectionCryptoType::BKTR) {
  362. std::copy(key_area.begin() + 0x20, key_area.begin() + 0x30, out.begin());
  363. } else {
  364. LOG_CRITICAL(Crypto, "Called GetKeyAreaKey on invalid NCASectionCryptoType type={:02X}",
  365. type);
  366. }
  367. u128 out_128{};
  368. std::memcpy(out_128.data(), out.data(), sizeof(u128));
  369. LOG_TRACE(Crypto, "called with crypto_rev={:02X}, kak_index={:02X}, key={:016X}{:016X}",
  370. master_key_id, header.key_index, out_128[1], out_128[0]);
  371. return out;
  372. }
  373. std::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
  374. const auto master_key_id = GetCryptoRevision();
  375. u128 rights_id{};
  376. memcpy(rights_id.data(), header.rights_id.data(), 16);
  377. if (rights_id == u128{}) {
  378. status = Loader::ResultStatus::ErrorInvalidRightsID;
  379. return std::nullopt;
  380. }
  381. auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
  382. if (titlekey == Core::Crypto::Key128{}) {
  383. status = Loader::ResultStatus::ErrorMissingTitlekey;
  384. return std::nullopt;
  385. }
  386. if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
  387. status = Loader::ResultStatus::ErrorMissingTitlekek;
  388. return std::nullopt;
  389. }
  390. Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
  391. keys.GetKey(Core::Crypto::S128KeyType::Titlekek, master_key_id), Core::Crypto::Mode::ECB);
  392. cipher.Transcode(titlekey.data(), titlekey.size(), titlekey.data(), Core::Crypto::Op::Decrypt);
  393. return titlekey;
  394. }
  395. VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 starting_offset) {
  396. if (!encrypted)
  397. return in;
  398. switch (s_header.raw.header.crypto_type) {
  399. case NCASectionCryptoType::NONE:
  400. LOG_TRACE(Crypto, "called with mode=NONE");
  401. return in;
  402. case NCASectionCryptoType::CTR:
  403. // During normal BKTR decryption, this entire function is skipped. This is for the metadata,
  404. // which uses the same CTR as usual.
  405. case NCASectionCryptoType::BKTR:
  406. LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
  407. {
  408. std::optional<Core::Crypto::Key128> key;
  409. if (has_rights_id) {
  410. status = Loader::ResultStatus::Success;
  411. key = GetTitlekey();
  412. if (!key) {
  413. if (status == Loader::ResultStatus::Success)
  414. status = Loader::ResultStatus::ErrorMissingTitlekey;
  415. return nullptr;
  416. }
  417. } else {
  418. key = GetKeyAreaKey(NCASectionCryptoType::CTR);
  419. if (!key) {
  420. status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
  421. return nullptr;
  422. }
  423. }
  424. auto out = std::make_shared<Core::Crypto::CTREncryptionLayer>(std::move(in), *key,
  425. starting_offset);
  426. Core::Crypto::CTREncryptionLayer::IVData iv{};
  427. for (std::size_t i = 0; i < 8; ++i) {
  428. iv[i] = s_header.raw.section_ctr[8 - i - 1];
  429. }
  430. out->SetIV(iv);
  431. return std::static_pointer_cast<VfsFile>(out);
  432. }
  433. case NCASectionCryptoType::XTS:
  434. // TODO(DarkLordZach): Find a test case for XTS-encrypted NCAs
  435. default:
  436. LOG_ERROR(Crypto, "called with unhandled crypto type={:02X}",
  437. s_header.raw.header.crypto_type);
  438. return nullptr;
  439. }
  440. }
  441. Loader::ResultStatus NCA::GetStatus() const {
  442. return status;
  443. }
  444. std::vector<VirtualFile> NCA::GetFiles() const {
  445. if (status != Loader::ResultStatus::Success) {
  446. return {};
  447. }
  448. return files;
  449. }
  450. std::vector<VirtualDir> NCA::GetSubdirectories() const {
  451. if (status != Loader::ResultStatus::Success) {
  452. return {};
  453. }
  454. return dirs;
  455. }
  456. std::string NCA::GetName() const {
  457. return file->GetName();
  458. }
  459. VirtualDir NCA::GetParentDirectory() const {
  460. return file->GetContainingDirectory();
  461. }
  462. NCAContentType NCA::GetType() const {
  463. return header.content_type;
  464. }
  465. u64 NCA::GetTitleId() const {
  466. if (is_update || status == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS)
  467. return header.title_id | 0x800;
  468. return header.title_id;
  469. }
  470. std::array<u8, 16> NCA::GetRightsId() const {
  471. return header.rights_id;
  472. }
  473. u32 NCA::GetSDKVersion() const {
  474. return header.sdk_version;
  475. }
  476. bool NCA::IsUpdate() const {
  477. return is_update;
  478. }
  479. VirtualFile NCA::GetRomFS() const {
  480. return romfs;
  481. }
  482. VirtualDir NCA::GetExeFS() const {
  483. return exefs;
  484. }
  485. VirtualFile NCA::GetBaseFile() const {
  486. return file;
  487. }
  488. u64 NCA::GetBaseIVFCOffset() const {
  489. return ivfc_offset;
  490. }
  491. VirtualDir NCA::GetLogoPartition() const {
  492. return logo;
  493. }
  494. } // namespace FileSys