|
| 1 | +// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project |
| 2 | +// SPDX-License-Identifier: GPL-2.0-or-later |
| 3 | + |
| 4 | +#include <vector> |
| 5 | + |
| 6 | +#include "yuzu_common/common_types.h" |
| 7 | +#include "core/core.h" |
| 8 | +#include "core/file_sys/content_archive.h" |
| 9 | +#include "core/file_sys/control_metadata.h" |
| 10 | +#include "core/file_sys/filesystem.h" |
| 11 | +#include "core/file_sys/nca_metadata.h" |
| 12 | +#include "core/file_sys/patch_manager.h" |
| 13 | +#include "core/file_sys/registered_cache.h" |
| 14 | +#include "core/file_sys/romfs_factory.h" |
| 15 | +#include "core/file_sys/submission_package.h" |
| 16 | +#include "core/loader/deconstructed_rom_directory.h" |
| 17 | +#include "core/loader/nca.h" |
| 18 | +#include "core/loader/nsp.h" |
| 19 | +#include "system_loader.h" |
| 20 | + |
| 21 | +namespace Loader { |
| 22 | + |
| 23 | +AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file_, |
| 24 | + const FileSys::FileSystemController& fsc, |
| 25 | + const FileSys::ContentProvider& content_provider, u64 program_id, |
| 26 | + std::size_t program_index) |
| 27 | + : AppLoader(file_), nsp(std::make_unique<FileSys::NSP>(file_, program_id, program_index)) { |
| 28 | + |
| 29 | + if (nsp->GetStatus() != LoaderResultStatus::Success) { |
| 30 | + return; |
| 31 | + } |
| 32 | + |
| 33 | + if (nsp->IsExtractedType()) { |
| 34 | + secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>( |
| 35 | + nsp->GetExeFS(), false, file->GetName() == "hbl.nsp"); |
| 36 | + } else { |
| 37 | + const auto control_nca = |
| 38 | + nsp->GetNCA(nsp->GetProgramTitleID(), LoaderContentRecordType::Control); |
| 39 | + if (control_nca == nullptr || control_nca->GetStatus() != LoaderResultStatus::Success) { |
| 40 | + return; |
| 41 | + } |
| 42 | + |
| 43 | + std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] { |
| 44 | + const FileSys::PatchManager pm{nsp->GetProgramTitleID(), fsc, content_provider}; |
| 45 | + return pm.ParseControlNCA(*control_nca); |
| 46 | + }(); |
| 47 | + |
| 48 | + secondary_loader = std::make_unique<AppLoader_NCA>( |
| 49 | + nsp->GetNCAFile(nsp->GetProgramTitleID(), LoaderContentRecordType::Program)); |
| 50 | + } |
| 51 | +} |
| 52 | + |
| 53 | +AppLoader_NSP::~AppLoader_NSP() = default; |
| 54 | + |
| 55 | +FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& nsp_file) { |
| 56 | + const FileSys::NSP nsp(nsp_file); |
| 57 | + |
| 58 | + if (nsp.GetStatus() == LoaderResultStatus::Success) { |
| 59 | + // Extracted Type case |
| 60 | + if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr && |
| 61 | + FileSys::IsDirectoryExeFS(nsp.GetExeFS())) { |
| 62 | + return FileType::NSP; |
| 63 | + } |
| 64 | + |
| 65 | + // Non-Extracted Type case |
| 66 | + const auto program_id = nsp.GetProgramTitleID(); |
| 67 | + if (!nsp.IsExtractedType() && |
| 68 | + nsp.GetNCA(program_id, LoaderContentRecordType::Program) != nullptr && |
| 69 | + AppLoader_NCA::IdentifyType( |
| 70 | + nsp.GetNCAFile(program_id, LoaderContentRecordType::Program)) == FileType::NCA) { |
| 71 | + return FileType::NSP; |
| 72 | + } |
| 73 | + } |
| 74 | + return FileType::Error; |
| 75 | +} |
| 76 | + |
| 77 | +AppLoader_NSP::LoadResult AppLoader_NSP::Load(Systemloader & loader) { |
| 78 | + if (is_loaded) { |
| 79 | + return {LoaderResultStatus::ErrorAlreadyLoaded, {}}; |
| 80 | + } |
| 81 | + |
| 82 | + const auto title_id = nsp->GetProgramTitleID(); |
| 83 | + |
| 84 | + if (!nsp->IsExtractedType() && title_id == 0) { |
| 85 | + return {LoaderResultStatus::ErrorNSPMissingProgramNCA, {}}; |
| 86 | + } |
| 87 | + |
| 88 | + const auto nsp_status = nsp->GetStatus(); |
| 89 | + if (nsp_status != LoaderResultStatus::Success) { |
| 90 | + return {nsp_status, {}}; |
| 91 | + } |
| 92 | + |
| 93 | + const auto nsp_program_status = nsp->GetProgramStatus(); |
| 94 | + if (nsp_program_status != LoaderResultStatus::Success) { |
| 95 | + return {nsp_program_status, {}}; |
| 96 | + } |
| 97 | + |
| 98 | + if (!nsp->IsExtractedType() && |
| 99 | + nsp->GetNCA(title_id, LoaderContentRecordType::Program) == nullptr) { |
| 100 | + return {LoaderResultStatus::ErrorNSPMissingProgramNCA, {}}; |
| 101 | + } |
| 102 | + |
| 103 | + const auto result = secondary_loader->Load(loader); |
| 104 | + if (result.first != LoaderResultStatus::Success) { |
| 105 | + return result; |
| 106 | + } |
| 107 | + |
| 108 | + if (nsp->IsExtractedType()) { |
| 109 | + UNIMPLEMENTED(); |
| 110 | + } |
| 111 | + |
| 112 | + FileSys::VirtualFile update_raw; |
| 113 | + if (ReadUpdateRaw(update_raw) == LoaderResultStatus::Success && update_raw != nullptr) { |
| 114 | + loader.GetFileSystemController().SetPackedUpdate(result.second->process_id, |
| 115 | + std::move(update_raw)); |
| 116 | + } |
| 117 | + |
| 118 | + is_loaded = true; |
| 119 | + return result; |
| 120 | +} |
| 121 | + |
| 122 | +LoaderResultStatus AppLoader_NSP::VerifyIntegrity(std::function<bool(size_t, size_t)> progress_callback) { |
| 123 | + // Extracted-type NSPs can't be verified. |
| 124 | + if (nsp->IsExtractedType()) { |
| 125 | + return LoaderResultStatus::ErrorIntegrityVerificationNotImplemented; |
| 126 | + } |
| 127 | + |
| 128 | + // Get list of all NCAs. |
| 129 | + const auto ncas = nsp->GetNCAsCollapsed(); |
| 130 | + |
| 131 | + size_t total_size = 0; |
| 132 | + size_t processed_size = 0; |
| 133 | + |
| 134 | + // Loop over NCAs, collecting the total size to verify. |
| 135 | + for (const auto& nca : ncas) { |
| 136 | + total_size += nca->GetBaseFile()->GetSize(); |
| 137 | + } |
| 138 | + |
| 139 | + // Loop over NCAs again, verifying each. |
| 140 | + for (const auto& nca : ncas) { |
| 141 | + AppLoader_NCA loader_nca(nca->GetBaseFile()); |
| 142 | + |
| 143 | + const auto NcaProgressCallback = [&](size_t nca_processed_size, size_t nca_total_size) { |
| 144 | + return progress_callback(processed_size + nca_processed_size, total_size); |
| 145 | + }; |
| 146 | + |
| 147 | + const auto verification_result = loader_nca.VerifyIntegrity(NcaProgressCallback); |
| 148 | + if (verification_result != LoaderResultStatus::Success) { |
| 149 | + return verification_result; |
| 150 | + } |
| 151 | + |
| 152 | + processed_size += nca->GetBaseFile()->GetSize(); |
| 153 | + } |
| 154 | + |
| 155 | + return LoaderResultStatus::Success; |
| 156 | +} |
| 157 | + |
| 158 | +LoaderResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& out_file) { |
| 159 | + return secondary_loader->ReadRomFS(out_file); |
| 160 | +} |
| 161 | + |
| 162 | +LoaderResultStatus AppLoader_NSP::ReadUpdateRaw(FileSys::VirtualFile& out_file) { |
| 163 | + if (nsp->IsExtractedType()) { |
| 164 | + return LoaderResultStatus::ErrorNoPackedUpdate; |
| 165 | + } |
| 166 | + |
| 167 | + const auto read = nsp->GetNCAFile(FileSys::GetUpdateTitleID(nsp->GetProgramTitleID()), |
| 168 | + LoaderContentRecordType::Program); |
| 169 | + |
| 170 | + if (read == nullptr) { |
| 171 | + return LoaderResultStatus::ErrorNoPackedUpdate; |
| 172 | + } |
| 173 | + |
| 174 | + const auto nca_test = std::make_shared<FileSys::NCA>(read); |
| 175 | + if (nca_test->GetStatus() != LoaderResultStatus::ErrorMissingBKTRBaseRomFS) { |
| 176 | + return nca_test->GetStatus(); |
| 177 | + } |
| 178 | + |
| 179 | + out_file = read; |
| 180 | + return LoaderResultStatus::Success; |
| 181 | +} |
| 182 | + |
| 183 | +LoaderResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) { |
| 184 | + out_program_id = nsp->GetProgramTitleID(); |
| 185 | + if (out_program_id == 0) { |
| 186 | + return LoaderResultStatus::ErrorNotInitialized; |
| 187 | + } |
| 188 | + return LoaderResultStatus::Success; |
| 189 | +} |
| 190 | + |
| 191 | +LoaderResultStatus AppLoader_NSP::ReadProgramIds(std::vector<u64>& out_program_ids) { |
| 192 | + out_program_ids = nsp->GetProgramTitleIDs(); |
| 193 | + return LoaderResultStatus::Success; |
| 194 | +} |
| 195 | + |
| 196 | +LoaderResultStatus AppLoader_NSP::ReadIcon(std::vector<u8>& buffer) { |
| 197 | + if (icon_file == nullptr) { |
| 198 | + return LoaderResultStatus::ErrorNoControl; |
| 199 | + } |
| 200 | + |
| 201 | + buffer = icon_file->ReadAllBytes(); |
| 202 | + return LoaderResultStatus::Success; |
| 203 | +} |
| 204 | + |
| 205 | +LoaderResultStatus AppLoader_NSP::ReadTitle(std::string& title) { |
| 206 | + if (nacp_file == nullptr) { |
| 207 | + return LoaderResultStatus::ErrorNoControl; |
| 208 | + } |
| 209 | + |
| 210 | + title = nacp_file->GetApplicationName(); |
| 211 | + return LoaderResultStatus::Success; |
| 212 | +} |
| 213 | + |
| 214 | +LoaderResultStatus AppLoader_NSP::ReadControlData(FileSys::NACP& nacp) { |
| 215 | + if (nacp_file == nullptr) { |
| 216 | + return LoaderResultStatus::ErrorNoControl; |
| 217 | + } |
| 218 | + |
| 219 | + nacp = *nacp_file; |
| 220 | + return LoaderResultStatus::Success; |
| 221 | +} |
| 222 | + |
| 223 | +LoaderResultStatus AppLoader_NSP::ReadManualRomFS(FileSys::VirtualFile& out_file) { |
| 224 | + UNIMPLEMENTED(); |
| 225 | + return LoaderResultStatus::ErrorNoRomFS; |
| 226 | +} |
| 227 | + |
| 228 | +LoaderResultStatus AppLoader_NSP::ReadBanner(std::vector<u8>& buffer) { |
| 229 | + return secondary_loader->ReadBanner(buffer); |
| 230 | +} |
| 231 | + |
| 232 | +LoaderResultStatus AppLoader_NSP::ReadLogo(std::vector<u8>& buffer) { |
| 233 | + return secondary_loader->ReadLogo(buffer); |
| 234 | +} |
| 235 | + |
| 236 | +LoaderResultStatus AppLoader_NSP::ReadNSOModules(Modules& modules) { |
| 237 | + return secondary_loader->ReadNSOModules(modules); |
| 238 | +} |
| 239 | + |
| 240 | +} // namespace Loader |
0 commit comments