summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/core/file_sys/content_archive.cpp528
-rw-r--r--src/core/file_sys/content_archive.h15
2 files changed, 290 insertions, 253 deletions
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 0872a378b..0f7cc3fbf 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -102,6 +102,284 @@ bool IsValidNCA(const NCAHeader& header) {
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_)),
107 bktr_base_romfs(bktr_base_romfs_ ? std::move(bktr_base_romfs_) : nullptr) {
108 if (file == nullptr) {
109 status = Loader::ResultStatus::ErrorNullFile;
110 return;
111 }
112
113 if (sizeof(NCAHeader) != file->ReadObject(&header)) {
114 LOG_ERROR(Loader, "File reader errored out during header read.");
115 status = Loader::ResultStatus::ErrorBadNCAHeader;
116 return;
117 }
118
119 if (!HandlePotentialHeaderDecryption()) {
120 return;
121 }
122
123 has_rights_id = std::find_if_not(header.rights_id.begin(), header.rights_id.end(),
124 [](char c) { return c == '\0'; }) != header.rights_id.end();
125
126 const std::vector<NCASectionHeader> sections = ReadSectionHeaders();
127 is_update = std::any_of(sections.begin(), sections.end(), [](const NCASectionHeader& header) {
128 return header.raw.header.crypto_type == NCASectionCryptoType::BKTR;
129 });
130
131 if (!ReadSections(sections, bktr_base_ivfc_offset)) {
132 return;
133 }
134
135 status = Loader::ResultStatus::Success;
136}
137
138NCA::~NCA() = default;
139
140bool NCA::CheckSupportedNCA(const NCAHeader& nca_header) {
141 if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '2')) {
142 status = Loader::ResultStatus::ErrorNCA2;
143 return false;
144 }
145
146 if (nca_header.magic == Common::MakeMagic('N', 'C', 'A', '0')) {
147 status = Loader::ResultStatus::ErrorNCA0;
148 return false;
149 }
150
151 return true;
152}
153
154bool NCA::HandlePotentialHeaderDecryption() {
155 if (IsValidNCA(header)) {
156 return true;
157 }
158
159 if (!CheckSupportedNCA(header)) {
160 return false;
161 }
162
163 NCAHeader dec_header{};
164 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
165 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
166 cipher.XTSTranscode(&header, sizeof(NCAHeader), &dec_header, 0, 0x200,
167 Core::Crypto::Op::Decrypt);
168 if (IsValidNCA(dec_header)) {
169 header = dec_header;
170 encrypted = true;
171 } else {
172 if (!CheckSupportedNCA(dec_header)) {
173 return false;
174 }
175
176 if (keys.HasKey(Core::Crypto::S256KeyType::Header)) {
177 status = Loader::ResultStatus::ErrorIncorrectHeaderKey;
178 } else {
179 status = Loader::ResultStatus::ErrorMissingHeaderKey;
180 }
181 return false;
182 }
183
184 return true;
185}
186
187std::vector<NCASectionHeader> NCA::ReadSectionHeaders() const {
188 const std::ptrdiff_t number_sections =
189 std::count_if(std::begin(header.section_tables), std::end(header.section_tables),
190 [](NCASectionTableEntry entry) { return entry.media_offset > 0; });
191
192 std::vector<NCASectionHeader> sections(number_sections);
193 const auto length_sections = SECTION_HEADER_SIZE * number_sections;
194
195 if (encrypted) {
196 auto raw = file->ReadBytes(length_sections, SECTION_HEADER_OFFSET);
197 Core::Crypto::AESCipher<Core::Crypto::Key256> cipher(
198 keys.GetKey(Core::Crypto::S256KeyType::Header), Core::Crypto::Mode::XTS);
199 cipher.XTSTranscode(raw.data(), length_sections, sections.data(), 2, SECTION_HEADER_SIZE,
200 Core::Crypto::Op::Decrypt);
201 } else {
202 file->ReadBytes(sections.data(), length_sections, SECTION_HEADER_OFFSET);
203 }
204
205 return sections;
206}
207
208bool NCA::ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset) {
209 for (std::size_t i = 0; i < sections.size(); ++i) {
210 const auto& section = sections[i];
211
212 if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
213 if (!ReadRomFSSection(section, header.section_tables[i], bktr_base_ivfc_offset)) {
214 return false;
215 }
216 } else if (section.raw.header.filesystem_type == NCASectionFilesystemType::PFS0) {
217 if (!ReadPFS0Section(section, header.section_tables[i])) {
218 return false;
219 }
220 }
221 }
222
223 return true;
224}
225
226bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
227 u64 bktr_base_ivfc_offset) {
228 const std::size_t base_offset = entry.media_offset * MEDIA_OFFSET_MULTIPLIER;
229 ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
230 const std::size_t romfs_offset = base_offset + ivfc_offset;
231 const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
232 auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
233 auto dec = Decrypt(section, raw, romfs_offset);
234
235 if (dec == nullptr) {
236 if (status != Loader::ResultStatus::Success)
237 return false;
238 if (has_rights_id)
239 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
240 else
241 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
242 return false;
243 }
244
245 if (section.raw.header.crypto_type == NCASectionCryptoType::BKTR) {
246 if (section.bktr.relocation.magic != Common::MakeMagic('B', 'K', 'T', 'R') ||
247 section.bktr.subsection.magic != Common::MakeMagic('B', 'K', 'T', 'R')) {
248 status = Loader::ResultStatus::ErrorBadBKTRHeader;
249 return false;
250 }
251
252 if (section.bktr.relocation.offset + section.bktr.relocation.size !=
253 section.bktr.subsection.offset) {
254 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAfterRelocation;
255 return false;
256 }
257
258 const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
259 if (section.bktr.subsection.offset + section.bktr.subsection.size != size) {
260 status = Loader::ResultStatus::ErrorBKTRSubsectionNotAtEnd;
261 return false;
262 }
263
264 const u64 offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
265 RelocationBlock relocation_block{};
266 if (dec->ReadObject(&relocation_block, section.bktr.relocation.offset - offset) !=
267 sizeof(RelocationBlock)) {
268 status = Loader::ResultStatus::ErrorBadRelocationBlock;
269 return false;
270 }
271 SubsectionBlock subsection_block{};
272 if (dec->ReadObject(&subsection_block, section.bktr.subsection.offset - offset) !=
273 sizeof(RelocationBlock)) {
274 status = Loader::ResultStatus::ErrorBadSubsectionBlock;
275 return false;
276 }
277
278 std::vector<RelocationBucketRaw> relocation_buckets_raw(
279 (section.bktr.relocation.size - sizeof(RelocationBlock)) / sizeof(RelocationBucketRaw));
280 if (dec->ReadBytes(relocation_buckets_raw.data(),
281 section.bktr.relocation.size - sizeof(RelocationBlock),
282 section.bktr.relocation.offset + sizeof(RelocationBlock) - offset) !=
283 section.bktr.relocation.size - sizeof(RelocationBlock)) {
284 status = Loader::ResultStatus::ErrorBadRelocationBuckets;
285 return false;
286 }
287
288 std::vector<SubsectionBucketRaw> subsection_buckets_raw(
289 (section.bktr.subsection.size - sizeof(SubsectionBlock)) / sizeof(SubsectionBucketRaw));
290 if (dec->ReadBytes(subsection_buckets_raw.data(),
291 section.bktr.subsection.size - sizeof(SubsectionBlock),
292 section.bktr.subsection.offset + sizeof(SubsectionBlock) - offset) !=
293 section.bktr.subsection.size - sizeof(SubsectionBlock)) {
294 status = Loader::ResultStatus::ErrorBadSubsectionBuckets;
295 return false;
296 }
297
298 std::vector<RelocationBucket> relocation_buckets(relocation_buckets_raw.size());
299 std::transform(relocation_buckets_raw.begin(), relocation_buckets_raw.end(),
300 relocation_buckets.begin(), &ConvertRelocationBucketRaw);
301 std::vector<SubsectionBucket> subsection_buckets(subsection_buckets_raw.size());
302 std::transform(subsection_buckets_raw.begin(), subsection_buckets_raw.end(),
303 subsection_buckets.begin(), &ConvertSubsectionBucketRaw);
304
305 u32 ctr_low;
306 std::memcpy(&ctr_low, section.raw.section_ctr.data(), sizeof(ctr_low));
307 subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
308 subsection_buckets.back().entries.push_back({size, {0}, 0});
309
310 boost::optional<Core::Crypto::Key128> key = boost::none;
311 if (encrypted) {
312 if (has_rights_id) {
313 status = Loader::ResultStatus::Success;
314 key = GetTitlekey();
315 if (key == boost::none) {
316 status = Loader::ResultStatus::ErrorMissingTitlekey;
317 return false;
318 }
319 } else {
320 key = GetKeyAreaKey(NCASectionCryptoType::BKTR);
321 if (key == boost::none) {
322 status = Loader::ResultStatus::ErrorMissingKeyAreaKey;
323 return false;
324 }
325 }
326 }
327
328 if (bktr_base_romfs == nullptr) {
329 status = Loader::ResultStatus::ErrorMissingBKTRBaseRomFS;
330 return false;
331 }
332
333 auto bktr = std::make_shared<BKTR>(
334 bktr_base_romfs, std::make_shared<OffsetVfsFile>(file, romfs_size, base_offset),
335 relocation_block, relocation_buckets, subsection_block, subsection_buckets, encrypted,
336 encrypted ? key.get() : Core::Crypto::Key128{}, base_offset, bktr_base_ivfc_offset,
337 section.raw.section_ctr);
338
339 // BKTR applies to entire IVFC, so make an offset version to level 6
340 files.push_back(std::make_shared<OffsetVfsFile>(
341 bktr, romfs_size, section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset));
342 } else {
343 files.push_back(std::move(dec));
344 }
345
346 romfs = files.back();
347 return true;
348}
349
350bool NCA::ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry) {
351 const u64 offset = (static_cast<u64>(entry.media_offset) * MEDIA_OFFSET_MULTIPLIER) +
352 section.pfs0.pfs0_header_offset;
353 const u64 size = MEDIA_OFFSET_MULTIPLIER * (entry.media_end_offset - entry.media_offset);
354
355 auto dec = Decrypt(section, std::make_shared<OffsetVfsFile>(file, size, offset), offset);
356 if (dec != nullptr) {
357 auto npfs = std::make_shared<PartitionFilesystem>(std::move(dec));
358
359 if (npfs->GetStatus() == Loader::ResultStatus::Success) {
360 dirs.push_back(std::move(npfs));
361 if (IsDirectoryExeFS(dirs.back()))
362 exefs = dirs.back();
363 } else {
364 if (has_rights_id)
365 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
366 else
367 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
368 return false;
369 }
370 } else {
371 if (status != Loader::ResultStatus::Success)
372 return false;
373 if (has_rights_id)
374 status = Loader::ResultStatus::ErrorIncorrectTitlekeyOrTitlekek;
375 else
376 status = Loader::ResultStatus::ErrorIncorrectKeyAreaKey;
377 return false;
378 }
379
380 return true;
381}
382
105u8 NCA::GetCryptoRevision() const { 383u8 NCA::GetCryptoRevision() const {
106 u8 master_key_id = header.crypto_type; 384 u8 master_key_id = header.crypto_type;
107 if (header.crypto_type_2 > master_key_id) 385 if (header.crypto_type_2 > master_key_id)
@@ -215,256 +493,6 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
215 } 493 }
216} 494}
217 495
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 const 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 { 496Loader::ResultStatus NCA::GetStatus() const {
469 return status; 497 return status;
470} 498}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index d02ea4f4b..e5d3d3c6a 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -106,6 +106,15 @@ protected:
106 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; 106 bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
107 107
108private: 108private:
109 bool CheckSupportedNCA(const NCAHeader& header);
110 bool HandlePotentialHeaderDecryption();
111
112 std::vector<NCASectionHeader> ReadSectionHeaders() const;
113 bool ReadSections(const std::vector<NCASectionHeader>& sections, u64 bktr_base_ivfc_offset);
114 bool ReadRomFSSection(const NCASectionHeader& section, const NCASectionTableEntry& entry,
115 u64 bktr_base_ivfc_offset);
116 bool ReadPFS0Section(const NCASectionHeader& section, const NCASectionTableEntry& entry);
117
109 u8 GetCryptoRevision() const; 118 u8 GetCryptoRevision() const;
110 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const; 119 boost::optional<Core::Crypto::Key128> GetKeyAreaKey(NCASectionCryptoType type) const;
111 boost::optional<Core::Crypto::Key128> GetTitlekey(); 120 boost::optional<Core::Crypto::Key128> GetTitlekey();
@@ -118,15 +127,15 @@ private:
118 VirtualDir exefs = nullptr; 127 VirtualDir exefs = nullptr;
119 VirtualFile file; 128 VirtualFile file;
120 VirtualFile bktr_base_romfs; 129 VirtualFile bktr_base_romfs;
121 u64 ivfc_offset; 130 u64 ivfc_offset = 0;
122 131
123 NCAHeader header{}; 132 NCAHeader header{};
124 bool has_rights_id{}; 133 bool has_rights_id{};
125 134
126 Loader::ResultStatus status{}; 135 Loader::ResultStatus status{};
127 136
128 bool encrypted; 137 bool encrypted = false;
129 bool is_update; 138 bool is_update = false;
130 139
131 Core::Crypto::KeyManager keys; 140 Core::Crypto::KeyManager keys;
132}; 141};