summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGravatar Lioncash2018-10-16 12:12:50 -0400
committerGravatar Lioncash2018-10-16 13:22:28 -0400
commitd6604fa765890fd7583440362d4e0bdbb82a8215 (patch)
treedeac7c8328e2aca3491f878570919cc8f0ac971c /src
parentcontent_archive: Pass and take NCASectionHeader instance by reference (diff)
downloadyuzu-d6604fa765890fd7583440362d4e0bdbb82a8215.tar.gz
yuzu-d6604fa765890fd7583440362d4e0bdbb82a8215.tar.xz
yuzu-d6604fa765890fd7583440362d4e0bdbb82a8215.zip
content_archive: Split loading into separate functions
The constructor alone is pretty large, the reading code should be split into its consistuent parts to make it easier to understand it without having to build a mental model of a 300+ line function.
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};