| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  | // Copyright 2018 yuzu emulator team
 | 
					
						
							|  |  |  | // Licensed under GPLv2 or any later version
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-09-05 12:06:47 -04:00
										 |  |  | #include <algorithm>
 | 
					
						
							|  |  |  | #include <cstring>
 | 
					
						
							|  |  |  | #include <string_view>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  | #include <fmt/ostream.h>
 | 
					
						
							| 
									
										
										
										
											2018-09-05 12:06:47 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  | #include "common/hex_util.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-05 12:06:47 -04:00
										 |  |  | #include "common/logging/log.h"
 | 
					
						
							|  |  |  | #include "core/crypto/key_manager.h"
 | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  | #include "core/file_sys/content_archive.h"
 | 
					
						
							|  |  |  | #include "core/file_sys/nca_metadata.h"
 | 
					
						
							| 
									
										
										
										
											2018-09-04 14:44:40 -04:00
										 |  |  | #include "core/file_sys/partition_filesystem.h"
 | 
					
						
							| 
									
										
										
										
											2019-06-10 00:21:41 -04:00
										 |  |  | #include "core/file_sys/program_metadata.h"
 | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  | #include "core/file_sys/submission_package.h"
 | 
					
						
							|  |  |  | #include "core/loader/loader.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace FileSys { | 
					
						
							| 
									
										
										
										
											2018-10-03 01:20:54 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-24 15:16:24 -08:00
										 |  |  | NSP::NSP(VirtualFile file_, std::size_t program_index) | 
					
						
							|  |  |  |     : file(std::move(file_)), program_index(program_index), status{Loader::ResultStatus::Success}, | 
					
						
							| 
									
										
										
										
											2020-08-23 14:20:37 -04:00
										 |  |  |       pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} { | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  |     if (pfs->GetStatus() != Loader::ResultStatus::Success) { | 
					
						
							|  |  |  |         status = pfs->GetStatus(); | 
					
						
							|  |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 01:43:34 -04:00
										 |  |  |     const auto files = pfs->GetFiles(); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  |     if (IsDirectoryExeFS(pfs)) { | 
					
						
							|  |  |  |         extracted = true; | 
					
						
							| 
									
										
										
										
											2018-10-03 01:43:34 -04:00
										 |  |  |         InitializeExeFSAndRomFS(files); | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 01:20:54 -04:00
										 |  |  |     SetTicketKeys(files); | 
					
						
							| 
									
										
										
										
											2018-10-03 01:35:38 -04:00
										 |  |  |     ReadNCAs(files); | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 22:42:54 -04:00
										 |  |  | NSP::~NSP() = default; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  | Loader::ResultStatus NSP::GetStatus() const { | 
					
						
							|  |  |  |     return status; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { | 
					
						
							| 
									
										
										
										
											2019-06-10 00:21:41 -04:00
										 |  |  |     if (IsExtractedType() && GetExeFS() != nullptr && FileSys::IsDirectoryExeFS(GetExeFS())) { | 
					
						
							|  |  |  |         return Loader::ResultStatus::Success; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 22:42:54 -04:00
										 |  |  |     const auto iter = program_status.find(title_id); | 
					
						
							|  |  |  |     if (iter == program_status.end()) | 
					
						
							|  |  |  |         return Loader::ResultStatus::ErrorNSPMissingProgramNCA; | 
					
						
							|  |  |  |     return iter->second; | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | u64 NSP::GetFirstTitleID() const { | 
					
						
							| 
									
										
										
										
											2019-06-10 00:19:23 -04:00
										 |  |  |     if (IsExtractedType()) { | 
					
						
							|  |  |  |         return GetProgramTitleID(); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  |     if (program_status.empty()) | 
					
						
							|  |  |  |         return 0; | 
					
						
							|  |  |  |     return program_status.begin()->first; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | u64 NSP::GetProgramTitleID() const { | 
					
						
							| 
									
										
										
										
											2019-06-10 00:19:23 -04:00
										 |  |  |     if (IsExtractedType()) { | 
					
						
							|  |  |  |         if (GetExeFS() == nullptr || !IsDirectoryExeFS(GetExeFS())) { | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         ProgramMetadata meta; | 
					
						
							|  |  |  |         if (meta.Load(GetExeFS()->GetFile("main.npdm")) == Loader::ResultStatus::Success) { | 
					
						
							|  |  |  |             return meta.GetTitleID(); | 
					
						
							|  |  |  |         } else { | 
					
						
							|  |  |  |             return 0; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 22:42:54 -04:00
										 |  |  |     const auto out = GetFirstTitleID(); | 
					
						
							|  |  |  |     if ((out & 0x800) == 0) | 
					
						
							|  |  |  |         return out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const auto ids = GetTitleIDs(); | 
					
						
							|  |  |  |     const auto iter = | 
					
						
							|  |  |  |         std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; }); | 
					
						
							|  |  |  |     return iter == ids.end() ? out : *iter; | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<u64> NSP::GetTitleIDs() const { | 
					
						
							| 
									
										
										
										
											2019-06-10 00:19:23 -04:00
										 |  |  |     if (IsExtractedType()) { | 
					
						
							|  |  |  |         return {GetProgramTitleID()}; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  |     std::vector<u64> out; | 
					
						
							| 
									
										
										
										
											2018-08-25 22:42:54 -04:00
										 |  |  |     out.reserve(ncas.size()); | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  |     for (const auto& kv : ncas) | 
					
						
							|  |  |  |         out.push_back(kv.first); | 
					
						
							|  |  |  |     return out; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | bool NSP::IsExtractedType() const { | 
					
						
							|  |  |  |     return extracted; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | VirtualFile NSP::GetRomFS() const { | 
					
						
							|  |  |  |     return romfs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | VirtualDir NSP::GetExeFS() const { | 
					
						
							|  |  |  |     return exefs; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<std::shared_ptr<NCA>> NSP::GetNCAsCollapsed() const { | 
					
						
							|  |  |  |     if (extracted) | 
					
						
							|  |  |  |         LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | 
					
						
							|  |  |  |     std::vector<std::shared_ptr<NCA>> out; | 
					
						
							|  |  |  |     for (const auto& map : ncas) { | 
					
						
							|  |  |  |         for (const auto& inner_map : map.second) | 
					
						
							|  |  |  |             out.push_back(inner_map.second); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     return out; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const { | 
					
						
							|  |  |  |     if (extracted) | 
					
						
							|  |  |  |         LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | 
					
						
							|  |  |  |     std::multimap<u64, std::shared_ptr<NCA>> out; | 
					
						
							|  |  |  |     for (const auto& map : ncas) { | 
					
						
							|  |  |  |         for (const auto& inner_map : map.second) | 
					
						
							| 
									
										
										
										
											2018-08-25 22:42:54 -04:00
										 |  |  |             out.emplace(map.first, inner_map.second); | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  |     } | 
					
						
							|  |  |  |     return out; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-28 00:03:38 -05:00
										 |  |  | std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> | 
					
						
							|  |  |  | NSP::GetNCAs() const { | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  |     return ncas; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-28 00:03:38 -05:00
										 |  |  | std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type, TitleType title_type) const { | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  |     if (extracted) | 
					
						
							|  |  |  |         LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-24 15:16:24 -08:00
										 |  |  |     const auto title_id_iter = ncas.find(title_id + program_index); | 
					
						
							| 
									
										
										
										
											2018-08-25 22:42:54 -04:00
										 |  |  |     if (title_id_iter == ncas.end()) | 
					
						
							|  |  |  |         return nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-28 00:03:38 -05:00
										 |  |  |     const auto type_iter = title_id_iter->second.find({title_type, type}); | 
					
						
							| 
									
										
										
										
											2018-08-25 22:42:54 -04:00
										 |  |  |     if (type_iter == title_id_iter->second.end()) | 
					
						
							|  |  |  |         return nullptr; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return type_iter->second; | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-12-28 00:03:38 -05:00
										 |  |  | VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type, TitleType title_type) const { | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  |     if (extracted) | 
					
						
							|  |  |  |         LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | 
					
						
							|  |  |  |     const auto nca = GetNCA(title_id, type); | 
					
						
							|  |  |  |     if (nca != nullptr) | 
					
						
							|  |  |  |         return nca->GetBaseFile(); | 
					
						
							|  |  |  |     return nullptr; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<Core::Crypto::Key128> NSP::GetTitlekey() const { | 
					
						
							|  |  |  |     if (extracted) | 
					
						
							|  |  |  |         LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | 
					
						
							|  |  |  |     std::vector<Core::Crypto::Key128> out; | 
					
						
							|  |  |  |     for (const auto& ticket_file : ticket_files) { | 
					
						
							|  |  |  |         if (ticket_file == nullptr || | 
					
						
							|  |  |  |             ticket_file->GetSize() < | 
					
						
							|  |  |  |                 Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-08-25 22:42:54 -04:00
										 |  |  |         out.emplace_back(); | 
					
						
							|  |  |  |         ticket_file->Read(out.back().data(), out.back().size(), | 
					
						
							|  |  |  |                           Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  |     } | 
					
						
							|  |  |  |     return out; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<VirtualFile> NSP::GetFiles() const { | 
					
						
							|  |  |  |     return pfs->GetFiles(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::vector<VirtualDir> NSP::GetSubdirectories() const { | 
					
						
							|  |  |  |     return pfs->GetSubdirectories(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | std::string NSP::GetName() const { | 
					
						
							|  |  |  |     return file->GetName(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | VirtualDir NSP::GetParentDirectory() const { | 
					
						
							|  |  |  |     return file->GetContainingDirectory(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-12 20:46:14 +02:00
										 |  |  | void NSP::SetTicketKeys(const std::vector<VirtualFile>& files) { | 
					
						
							|  |  |  |     for (const auto& ticket_file : files) { | 
					
						
							|  |  |  |         if (ticket_file == nullptr) { | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (ticket_file->GetExtension() != "tik") { | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if (ticket_file->GetSize() < | 
					
						
							|  |  |  |             Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         Core::Crypto::Key128 key{}; | 
					
						
							|  |  |  |         ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // We get the name without the extension in order to create the rights ID.
 | 
					
						
							|  |  |  |         std::string name_only(ticket_file->GetName()); | 
					
						
							|  |  |  |         name_only.erase(name_only.size() - 4); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const auto rights_id_raw = Common::HexStringToArray<16>(name_only); | 
					
						
							|  |  |  |         u128 rights_id; | 
					
						
							|  |  |  |         std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); | 
					
						
							|  |  |  |         keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 01:43:34 -04:00
										 |  |  | void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) { | 
					
						
							|  |  |  |     exefs = pfs; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-02 02:34:40 -04:00
										 |  |  |     const auto iter = std::find_if(files.begin(), files.end(), [](const VirtualFile& entry) { | 
					
						
							|  |  |  |         return entry->GetName().rfind(".romfs") != std::string::npos; | 
					
						
							| 
									
										
										
										
											2018-10-03 01:43:34 -04:00
										 |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-02 02:34:40 -04:00
										 |  |  |     if (iter == files.end()) { | 
					
						
							| 
									
										
										
										
											2018-10-03 01:43:34 -04:00
										 |  |  |         return; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-02 02:34:40 -04:00
										 |  |  |     romfs = *iter; | 
					
						
							| 
									
										
										
										
											2018-10-03 01:43:34 -04:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 01:35:38 -04:00
										 |  |  | void NSP::ReadNCAs(const std::vector<VirtualFile>& files) { | 
					
						
							|  |  |  |     for (const auto& outer_file : files) { | 
					
						
							| 
									
										
										
										
											2019-04-10 12:35:42 -04:00
										 |  |  |         if (outer_file->GetName().size() < 9 || | 
					
						
							|  |  |  |             outer_file->GetName().substr(outer_file->GetName().size() - 9) != ".cnmt.nca") { | 
					
						
							| 
									
										
										
										
											2018-10-03 01:35:38 -04:00
										 |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const auto nca = std::make_shared<NCA>(outer_file); | 
					
						
							|  |  |  |         if (nca->GetStatus() != Loader::ResultStatus::Success) { | 
					
						
							|  |  |  |             program_status[nca->GetTitleId()] = nca->GetStatus(); | 
					
						
							|  |  |  |             continue; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         const auto section0 = nca->GetSubdirectories()[0]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         for (const auto& inner_file : section0->GetFiles()) { | 
					
						
							| 
									
										
										
										
											2019-06-12 17:27:06 -04:00
										 |  |  |             if (inner_file->GetExtension() != "cnmt") { | 
					
						
							| 
									
										
										
										
											2018-10-03 01:35:38 -04:00
										 |  |  |                 continue; | 
					
						
							| 
									
										
										
										
											2019-06-12 17:27:06 -04:00
										 |  |  |             } | 
					
						
							| 
									
										
										
										
											2018-10-03 01:35:38 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |             const CNMT cnmt(inner_file); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-17 20:44:51 -04:00
										 |  |  |             ncas[cnmt.GetTitleID()][{cnmt.GetType(), ContentRecordType::Meta}] = nca; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 01:35:38 -04:00
										 |  |  |             for (const auto& rec : cnmt.GetContentRecords()) { | 
					
						
							| 
									
										
										
										
											2019-06-12 17:27:06 -04:00
										 |  |  |                 const auto id_string = Common::HexToString(rec.nca_id, false); | 
					
						
							|  |  |  |                 auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 01:35:38 -04:00
										 |  |  |                 if (next_file == nullptr) { | 
					
						
							| 
									
										
										
										
											2019-07-01 06:46:05 +01:00
										 |  |  |                     if (rec.type != ContentRecordType::DeltaFragment) { | 
					
						
							|  |  |  |                         LOG_WARNING(Service_FS, | 
					
						
							|  |  |  |                                     "NCA with ID {}.nca is listed in content metadata, but cannot " | 
					
						
							|  |  |  |                                     "be found in PFS. NSP appears to be corrupted.", | 
					
						
							|  |  |  |                                     id_string); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-10-03 01:35:38 -04:00
										 |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-20 21:28:16 +02:00
										 |  |  |                 auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0); | 
					
						
							| 
									
										
										
										
											2020-09-24 08:55:51 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-06-12 17:27:06 -04:00
										 |  |  |                 if (next_nca->GetType() == NCAContentType::Program) { | 
					
						
							| 
									
										
										
										
											2020-09-17 20:44:51 -04:00
										 |  |  |                     program_status[next_nca->GetTitleId()] = next_nca->GetStatus(); | 
					
						
							| 
									
										
										
										
											2019-06-12 17:27:06 -04:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2020-09-24 08:55:51 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 if (next_nca->GetStatus() != Loader::ResultStatus::Success && | 
					
						
							|  |  |  |                     next_nca->GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | 
					
						
							|  |  |  |                     continue; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 // If the last 3 hexadecimal digits of the CNMT TitleID is 0x800 or is missing the
 | 
					
						
							|  |  |  |                 // BKTRBaseRomFS, this is an update NCA. Otherwise, this is a base NCA.
 | 
					
						
							|  |  |  |                 if ((cnmt.GetTitleID() & 0x800) != 0 || | 
					
						
							|  |  |  |                     next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) { | 
					
						
							|  |  |  |                     // If the last 3 hexadecimal digits of the NCA's TitleID is between 0x1 and
 | 
					
						
							|  |  |  |                     // 0x7FF, this is a multi-program update NCA. Otherwise, this is a regular
 | 
					
						
							|  |  |  |                     // update NCA.
 | 
					
						
							|  |  |  |                     if ((next_nca->GetTitleId() & 0x7FF) != 0 && | 
					
						
							|  |  |  |                         (next_nca->GetTitleId() & 0x800) == 0) { | 
					
						
							|  |  |  |                         ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = | 
					
						
							|  |  |  |                             std::move(next_nca); | 
					
						
							|  |  |  |                     } else { | 
					
						
							|  |  |  |                         ncas[cnmt.GetTitleID()][{cnmt.GetType(), rec.type}] = std::move(next_nca); | 
					
						
							|  |  |  |                     } | 
					
						
							|  |  |  |                 } else { | 
					
						
							| 
									
										
										
										
											2020-09-17 20:44:51 -04:00
										 |  |  |                     ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = std::move(next_nca); | 
					
						
							| 
									
										
										
										
											2018-10-05 08:53:45 -04:00
										 |  |  |                 } | 
					
						
							| 
									
										
										
										
											2018-10-03 01:35:38 -04:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             break; | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2018-08-25 11:45:26 -04:00
										 |  |  | } // namespace FileSys
 |