From d770c602052a9016f4577ed8c993a8f78f8e5507 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 11:43:27 -0400 Subject: [PATCH 01/14] key_manager: Avoid autogeneration if key exists --- src/core/crypto/key_manager.cpp | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index f768533da..bd4b3d7c7 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -231,18 +231,28 @@ void KeyManager::WriteKeyToFile(bool title_key, std::string_view keyname, } void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { - const auto iter = std::find_if( + if (s128_keys.find({id, field1, field2}) != s128_keys.end()) + return; + if (id == S128KeyType::Titlekey) { + Key128 rights_id; + std::memcpy(rights_id.data(), &field2, sizeof(u64)); + std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64)); + WriteKeyToFile(true, fmt::format("{}", Common::HexArrayToString(rights_id)), key); + } + const auto iter2 = std::find_if( s128_file_id.begin(), s128_file_id.end(), [&id, &field1, &field2](const std::pair> elem) { return std::tie(elem.second.type, elem.second.field1, elem.second.field2) == std::tie(id, field1, field2); }); - if (iter != s128_file_id.end()) - WriteKeyToFile(id == S128KeyType::Titlekey, iter->first, key); + if (iter2 != s128_file_id.end()) + WriteKeyToFile(false, iter2->first, key); s128_keys[{id, field1, field2}] = key; } void KeyManager::SetKey(S256KeyType id, Key256 key, u64 field1, u64 field2) { + if (s256_keys.find({id, field1, field2}) != s256_keys.end()) + return; const auto iter = std::find_if( s256_file_id.begin(), s256_file_id.end(), [&id, &field1, &field2](const std::pair> elem) { From b555311438ae1e2ad1e9caea55cc9f77c9ed4661 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 11:44:14 -0400 Subject: [PATCH 02/14] loader: Add NSP file type and NSP-specific errors --- src/core/loader/loader.cpp | 14 ++++++++++++-- src/core/loader/loader.h | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp index 5980cdb25..446adf557 100644 --- a/src/core/loader/loader.cpp +++ b/src/core/loader/loader.cpp @@ -15,6 +15,7 @@ #include "core/loader/nca.h" #include "core/loader/nro.h" #include "core/loader/nso.h" +#include "core/loader/nsp.h" #include "core/loader/xci.h" namespace Loader { @@ -34,6 +35,7 @@ FileType IdentifyFile(FileSys::VirtualFile file) { CHECK_TYPE(NCA) CHECK_TYPE(XCI) CHECK_TYPE(NAX) + CHECK_TYPE(NSP) #undef CHECK_TYPE @@ -59,6 +61,8 @@ FileType GuessFromFilename(const std::string& name) { return FileType::NCA; if (extension == "xci") return FileType::XCI; + if (extension == "nsp") + return FileType::NSP; return FileType::Unknown; } @@ -77,6 +81,8 @@ std::string GetFileTypeString(FileType type) { return "XCI"; case FileType::NAX: return "NAX"; + case FileType::NSP: + return "NSP"; case FileType::DeconstructedRomDirectory: return "Directory"; case FileType::Error: @@ -87,7 +93,7 @@ std::string GetFileTypeString(FileType type) { return "unknown"; } -constexpr std::array RESULT_MESSAGES{ +constexpr std::array RESULT_MESSAGES{ "The operation completed successfully.", "The loader requested to load is already loaded.", "The operation is not implemented.", @@ -137,7 +143,7 @@ constexpr std::array RESULT_MESSAGES{ "The AES Key Generation Source could not be found.", "The SD Save Key Source could not be found.", "The SD NCA Key Source could not be found.", -}; + "The NSP file is missing a Program-type NCA."}; std::ostream& operator<<(std::ostream& os, ResultStatus status) { os << RESULT_MESSAGES.at(static_cast(status)); @@ -182,6 +188,10 @@ static std::unique_ptr GetFileLoader(FileSys::VirtualFile file, FileT case FileType::NAX: return std::make_unique(std::move(file)); + // NX NSP (Nintendo Submission Package) file format + case FileType::NSP: + return std::make_unique(std::move(file)); + // NX deconstructed ROM directory. case FileType::DeconstructedRomDirectory: return std::make_unique(std::move(file)); diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h index 5a8540b0e..be66b2257 100644 --- a/src/core/loader/loader.h +++ b/src/core/loader/loader.h @@ -29,6 +29,7 @@ enum class FileType { NSO, NRO, NCA, + NSP, XCI, NAX, DeconstructedRomDirectory, @@ -105,6 +106,7 @@ enum class ResultStatus : u16 { ErrorMissingAESKeyGenerationSource, ErrorMissingSDSaveKeySource, ErrorMissingSDNCAKeySource, + ErrorNSPMissingProgramNCA, }; std::ostream& operator<<(std::ostream& os, ResultStatus status); From a040929c90055b520d52fc062b297578ab119800 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 11:44:52 -0400 Subject: [PATCH 03/14] drd: Load title ID from program metadata Previously only loaded from control metadata --- src/core/loader/deconstructed_rom_directory.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp index 921b899e2..1ae4bb656 100644 --- a/src/core/loader/deconstructed_rom_directory.cpp +++ b/src/core/loader/deconstructed_rom_directory.cpp @@ -61,7 +61,6 @@ AppLoader_DeconstructedRomDirectory::AppLoader_DeconstructedRomDirectory(FileSys if (nacp_file != nullptr) { FileSys::NACP nacp(nacp_file); - title_id = nacp.GetTitleId(); name = nacp.GetApplicationName(); } } @@ -120,6 +119,7 @@ ResultStatus AppLoader_DeconstructedRomDirectory::Load( } auto& kernel = Core::System::GetInstance().Kernel(); + title_id = metadata.GetTitleID(); process->program_id = metadata.GetTitleID(); process->svc_access_mask.set(); process->resource_limit = @@ -159,8 +159,6 @@ ResultStatus AppLoader_DeconstructedRomDirectory::ReadIcon(std::vector& buff } ResultStatus AppLoader_DeconstructedRomDirectory::ReadProgramId(u64& out_program_id) { - if (name.empty()) - return ResultStatus::ErrorNoControl; out_program_id = title_id; return ResultStatus::Success; } From 93703431e2d5318ac4a901b81d31230c40942043 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 11:45:26 -0400 Subject: [PATCH 04/14] file_sys: Add Nintendo Submission Package (NSP) --- src/core/file_sys/submission_package.cpp | 226 +++++++++++++++++++++++ src/core/file_sys/submission_package.h | 70 +++++++ 2 files changed, 296 insertions(+) create mode 100644 src/core/file_sys/submission_package.cpp create mode 100644 src/core/file_sys/submission_package.h diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp new file mode 100644 index 000000000..660771cf8 --- /dev/null +++ b/src/core/file_sys/submission_package.cpp @@ -0,0 +1,226 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include "common/assert.h" +#include "common/hex_util.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/nca_metadata.h" +#include "core/file_sys/submission_package.h" +#include "core/loader/loader.h" + +namespace FileSys { +NSP::NSP(VirtualFile file_) + : file(std::move(file_)), + pfs(std::make_shared(file)), status{Loader::ResultStatus::Success} { + if (pfs->GetStatus() != Loader::ResultStatus::Success) { + status = pfs->GetStatus(); + return; + } + + if (IsDirectoryExeFS(pfs)) { + extracted = true; + exefs = pfs; + + const auto& files = pfs->GetFiles(); + const auto romfs_iter = + std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { + return file->GetName().find(".romfs") != std::string::npos; + }); + if (romfs_iter != files.end()) + romfs = *romfs_iter; + return; + } + + extracted = false; + const auto files = pfs->GetFiles(); + + Core::Crypto::KeyManager keys; + for (const auto& ticket_file : files) { + if (ticket_file->GetExtension() == "tik") { + if (ticket_file == nullptr || + 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); + std::string_view name_only(ticket_file->GetName()); + name_only.remove_suffix(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]); + } + } + + for (const auto& outer_file : files) { + if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") { + const auto nca = std::make_shared(outer_file); + if (nca->GetStatus() != Loader::ResultStatus::Success) + continue; + const auto section0 = nca->GetSubdirectories()[0]; + + for (const auto& inner_file : section0->GetFiles()) { + if (inner_file->GetExtension() != "cnmt") + continue; + + const CNMT cnmt(inner_file); + auto& ncas_title = ncas[cnmt.GetTitleID()]; + + ncas_title[ContentRecordType::Meta] = nca; + for (const auto& rec : cnmt.GetContentRecords()) { + const auto next_file = pfs->GetFile( + fmt::format("{}.nca", Common::HexArrayToString(rec.nca_id, false))); + if (next_file == nullptr) { + LOG_WARNING(Service_FS, + "NCA with ID {}.nca is listed in content metadata, but cannot " + "be found in PFS. NSP appears to be corrupted.", + Common::HexArrayToString(rec.nca_id, false)); + continue; + } + + const auto next_nca = std::make_shared(next_file); + if (next_nca->GetType() == NCAContentType::Program) + program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); + if (next_nca->GetStatus() == Loader::ResultStatus::Success) + ncas_title[rec.type] = next_nca; + } + + break; + } + } + } +} + +Loader::ResultStatus NSP::GetStatus() const { + return status; +} + +Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { + if (program_status.find(title_id) != program_status.end()) + return program_status.at(title_id); + return Loader::ResultStatus::ErrorNSPMissingProgramNCA; +} + +u64 NSP::GetFirstTitleID() const { + if (program_status.empty()) + return 0; + return program_status.begin()->first; +} + +u64 NSP::GetProgramTitleID() const { + auto out = GetFirstTitleID(); + for (const auto other_tid : GetTitleIDs()) { + if ((out & 0x800) != 0) + out = other_tid; + } + return out; +} + +std::vector NSP::GetTitleIDs() const { + std::vector out; + 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> NSP::GetNCAsCollapsed() const { + if (extracted) + LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); + std::vector> out; + for (const auto& map : ncas) { + for (const auto& inner_map : map.second) + out.push_back(inner_map.second); + } + return out; +} + +std::multimap> NSP::GetNCAsByTitleID() const { + if (extracted) + LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); + std::multimap> out; + for (const auto& map : ncas) { + for (const auto& inner_map : map.second) + out.insert({map.first, inner_map.second}); + } + return out; +} + +std::map>> NSP::GetNCAs() const { + return ncas; +} + +std::shared_ptr NSP::GetNCA(u64 title_id, ContentRecordType type) const { + if (extracted) + LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); + if (ncas.find(title_id) != ncas.end()) { + const auto& inner_map = ncas.at(title_id); + if (inner_map.find(type) != inner_map.end()) + return inner_map.at(type); + } + + return nullptr; +} + +VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const { + 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 NSP::GetTitlekey() const { + if (extracted) + LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); + std::vector 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; + } + + Core::Crypto::Key128 key{}; + ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); + out.push_back(key); + } + return out; +} + +std::vector NSP::GetFiles() const { + return pfs->GetFiles(); +} + +std::vector NSP::GetSubdirectories() const { + return pfs->GetSubdirectories(); +} + +std::string NSP::GetName() const { + return file->GetName(); +} + +VirtualDir NSP::GetParentDirectory() const { + return file->GetContainingDirectory(); +} + +bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { + return false; +} +} // namespace FileSys diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h new file mode 100644 index 000000000..7b520df57 --- /dev/null +++ b/src/core/file_sys/submission_package.h @@ -0,0 +1,70 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include +#include "common/common_types.h" +#include "common/swap.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/vfs.h" +#include "core/loader/loader.h" +#include "romfs_factory.h" + +namespace FileSys { + +class NSP : public ReadOnlyVfsDirectory { +public: + explicit NSP(VirtualFile file); + + Loader::ResultStatus GetStatus() const; + Loader::ResultStatus GetProgramStatus(u64 title_id) const; + // Should only be used when one title id can be assured. + u64 GetFirstTitleID() const; + u64 GetProgramTitleID() const; + std::vector GetTitleIDs() const; + + bool IsExtractedType() const; + + // Common (Can be safely called on both types) + VirtualFile GetRomFS() const; + VirtualDir GetExeFS() const; + + // Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML) + std::vector> GetNCAsCollapsed() const; + std::multimap> GetNCAsByTitleID() const; + std::map>> GetNCAs() const; + std::shared_ptr GetNCA(u64 title_id, ContentRecordType type) const; + VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const; + std::vector GetTitlekey() const; + + std::vector GetFiles() const override; + + std::vector GetSubdirectories() const override; + + std::string GetName() const override; + + VirtualDir GetParentDirectory() const override; + +protected: + bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; + +private: + VirtualFile file; + + bool extracted; + Loader::ResultStatus status; + std::map program_status; + + std::shared_ptr pfs; + // Map title id -> {map type -> NCA} + std::map>> ncas; + std::vector ticket_files; + + VirtualFile romfs; + VirtualDir exefs; +}; +} // namespace FileSys From 5c8aff984e47c0f471e9eafd071031bc49ad8efc Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 11:48:23 -0400 Subject: [PATCH 05/14] card_image: Parse XCI secure partition with NSP Eliminated duplicate code and adds support for Rev1+ carts --- src/core/crypto/key_manager.h | 2 ++ src/core/file_sys/card_image.cpp | 33 +++++++++++++++++++++++++------- src/core/file_sys/card_image.h | 7 +++++++ src/core/loader/xci.cpp | 7 +++---- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h index bf51bf31f..ce67913bb 100644 --- a/src/core/crypto/key_manager.h +++ b/src/core/crypto/key_manager.h @@ -17,6 +17,8 @@ enum class ResultStatus : u16; namespace Core::Crypto { +constexpr u64 TICKET_FILE_TITLEKEY_OFFSET = 0x180; + using Key128 = std::array; using Key256 = std::array; using SHA256Hash = std::array; diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index ce4423fa6..d0f1afac0 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -10,6 +10,7 @@ #include "common/logging/log.h" #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" +#include "core/file_sys/nca_metadata.h" #include "core/file_sys/partition_filesystem.h" #include "core/file_sys/vfs_offset.h" #include "core/loader/loader.h" @@ -44,15 +45,19 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { partitions[static_cast(partition)] = std::make_shared(raw); } + secure_partition = std::make_shared( + main_hfs.GetFile(partition_names[static_cast(XCIPartition::Secure)])); + + const auto secure_ncas = secure_partition->GetNCAsCollapsed(); + std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas)); + program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; + program = + secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program); + if (program != nullptr) + program_nca_status = program->GetStatus(); - auto result = AddNCAFromPartition(XCIPartition::Secure); - if (result != Loader::ResultStatus::Success) { - status = result; - return; - } - - result = AddNCAFromPartition(XCIPartition::Update); + auto result = AddNCAFromPartition(XCIPartition::Update); if (result != Loader::ResultStatus::Success) { status = result; return; @@ -89,6 +94,10 @@ VirtualDir XCI::GetPartition(XCIPartition partition) const { return partitions[static_cast(partition)]; } +std::shared_ptr XCI::GetSecurePartitionNSP() const { + return secure_partition; +} + VirtualDir XCI::GetSecurePartition() const { return GetPartition(XCIPartition::Secure); } @@ -105,6 +114,16 @@ VirtualDir XCI::GetLogoPartition() const { return GetPartition(XCIPartition::Logo); } +std::shared_ptr XCI::GetProgramNCA() const { + return program; +} + +VirtualFile XCI::GetProgramNCAFile() const { + if (GetProgramNCA() == nullptr) + return nullptr; + return GetProgramNCA()->GetBaseFile(); +} + const std::vector>& XCI::GetNCAs() const { return ncas; } diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index 4f104d18a..b73f1d900 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -10,6 +10,8 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/file_sys/vfs.h" +#include "core/loader/loader.h" +#include "submission_package.h" namespace Loader { enum class ResultStatus : u16; @@ -71,11 +73,14 @@ public: u8 GetFormatVersion() const; VirtualDir GetPartition(XCIPartition partition) const; + std::shared_ptr GetSecurePartitionNSP() const; VirtualDir GetSecurePartition() const; VirtualDir GetNormalPartition() const; VirtualDir GetUpdatePartition() const; VirtualDir GetLogoPartition() const; + std::shared_ptr GetProgramNCA() const; + VirtualFile GetProgramNCAFile() const; const std::vector>& GetNCAs() const; std::shared_ptr GetNCAByType(NCAContentType type) const; VirtualFile GetNCAFileByType(NCAContentType type) const; @@ -101,6 +106,8 @@ private: Loader::ResultStatus program_nca_status; std::vector partitions; + std::shared_ptr secure_partition; + std::shared_ptr program; std::vector> ncas; }; } // namespace FileSys diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp index 9dc4d1f35..75b998faa 100644 --- a/src/core/loader/xci.cpp +++ b/src/core/loader/xci.cpp @@ -17,8 +17,7 @@ namespace Loader { AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file) : AppLoader(file), xci(std::make_unique(file)), - nca_loader(std::make_unique( - xci->GetNCAFileByType(FileSys::NCAContentType::Program))) { + nca_loader(std::make_unique(xci->GetProgramNCAFile())) { if (xci->GetStatus() != ResultStatus::Success) return; const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control); @@ -64,11 +63,11 @@ ResultStatus AppLoader_XCI::Load(Kernel::SharedPtr& process) { if (xci->GetProgramNCAStatus() != ResultStatus::Success) return xci->GetProgramNCAStatus(); - const auto nca = xci->GetNCAFileByType(FileSys::NCAContentType::Program); + const auto nca = xci->GetProgramNCA(); if (nca == nullptr && !Core::Crypto::KeyManager::KeyFileExists(false)) return ResultStatus::ErrorMissingProductionKeyFile; - auto result = nca_loader->Load(process); + const auto result = nca_loader->Load(process); if (result != ResultStatus::Success) return result; From d7518cf6e03184a25bffeef5d38257549012b98b Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 11:49:31 -0400 Subject: [PATCH 06/14] loader: Add AppLoader for NSP files --- src/core/loader/nsp.cpp | 128 ++++++++++++++++++++++++++++++++++++++++ src/core/loader/nsp.h | 54 +++++++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 src/core/loader/nsp.cpp create mode 100644 src/core/loader/nsp.h diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp new file mode 100644 index 000000000..75d9fc1bc --- /dev/null +++ b/src/core/loader/nsp.cpp @@ -0,0 +1,128 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include + +#include "common/common_types.h" +#include "core/file_sys/card_image.h" +#include "core/file_sys/content_archive.h" +#include "core/file_sys/control_metadata.h" +#include "core/file_sys/registered_cache.h" +#include "core/file_sys/romfs.h" +#include "core/file_sys/submission_package.h" +#include "core/hle/kernel/process.h" +#include "core/loader/deconstructed_rom_directory.h" +#include "core/loader/nca.h" +#include "core/loader/nsp.h" + +namespace Loader { + +AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file) + : AppLoader(file), nsp(std::make_unique(file)), + title_id(nsp->GetProgramTitleID()) { + if (nsp->GetStatus() != ResultStatus::Success) + return; + if (nsp->IsExtractedType()) + return; + const auto control_nca = + nsp->GetNCA(nsp->GetFirstTitleID(), FileSys::ContentRecordType::Control); + if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) + return; + const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS()); + if (romfs == nullptr) + return; + for (const auto& language : FileSys::LANGUAGE_NAMES) { + icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat"); + if (icon_file != nullptr) + break; + } + const auto nacp_raw = romfs->GetFile("control.nacp"); + if (nacp_raw == nullptr) + return; + nacp_file = std::make_shared(nacp_raw); +} + +AppLoader_NSP::~AppLoader_NSP() = default; + +FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) { + FileSys::NSP nsp(file); + + if (nsp.GetStatus() == ResultStatus::Success) { + // Extracted Type case + if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr && + FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) + return FileType::NSP; + + // Non-Ectracted Type case + if (!nsp.IsExtractedType() && + nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr && + AppLoader_NCA::IdentifyType(nsp.GetNCAFile( + nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) + return FileType::NSP; + } + + return FileType::Error; +} + +ResultStatus AppLoader_NSP::Load(Kernel::SharedPtr& process) { + if (is_loaded) { + return ResultStatus::ErrorAlreadyLoaded; + } + + if (nsp->IsExtractedType()) { + secondary_loader = std::make_unique(nsp->GetExeFS()); + } else { + if (title_id == 0) + return ResultStatus::ErrorNSPMissingProgramNCA; + + secondary_loader = std::make_unique( + nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program)); + + if (nsp->GetStatus() != ResultStatus::Success) + return nsp->GetStatus(); + + if (nsp->GetProgramStatus(title_id) != ResultStatus::Success) + return nsp->GetProgramStatus(title_id); + + if (nsp->GetNCA(title_id, FileSys::ContentRecordType::Program) == nullptr) { + if (!Core::Crypto::KeyManager::KeyFileExists(false)) + return ResultStatus::ErrorMissingProductionKeyFile; + return ResultStatus::ErrorNSPMissingProgramNCA; + } + } + + const auto result = secondary_loader->Load(process); + if (result != ResultStatus::Success) + return result; + + is_loaded = true; + + return ResultStatus::Success; +} + +ResultStatus AppLoader_NSP::ReadRomFS(FileSys::VirtualFile& dir) { + return secondary_loader->ReadRomFS(dir); +} + +ResultStatus AppLoader_NSP::ReadProgramId(u64& out_program_id) { + if (title_id == 0) + return ResultStatus::ErrorNotInitialized; + out_program_id = title_id; + return ResultStatus::Success; +} + +ResultStatus AppLoader_NSP::ReadIcon(std::vector& buffer) { + if (icon_file == nullptr) + return ResultStatus::ErrorNoControl; + buffer = icon_file->ReadAllBytes(); + return ResultStatus::Success; +} + +ResultStatus AppLoader_NSP::ReadTitle(std::string& title) { + if (nacp_file == nullptr) + return ResultStatus::ErrorNoControl; + title = nacp_file->GetApplicationName(); + return ResultStatus::Success; +} +} // namespace Loader diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h new file mode 100644 index 000000000..785feaf37 --- /dev/null +++ b/src/core/loader/nsp.h @@ -0,0 +1,54 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include +#include "common/common_types.h" +#include "core/file_sys/vfs.h" +#include "core/loader/loader.h" + +namespace FileSys { +class NACP; +class NSP; +} // namespace FileSys + +namespace Loader { + +class AppLoader_NCA; + +/// Loads an XCI file +class AppLoader_NSP final : public AppLoader { +public: + explicit AppLoader_NSP(FileSys::VirtualFile file); + ~AppLoader_NSP(); + + /** + * Returns the type of the file + * @param file std::shared_ptr open file + * @return FileType found, or FileType::Error if this loader doesn't know it + */ + static FileType IdentifyType(const FileSys::VirtualFile& file); + + FileType GetFileType() override { + return IdentifyType(file); + } + + ResultStatus Load(Kernel::SharedPtr& process) override; + + ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override; + ResultStatus ReadProgramId(u64& out_program_id) override; + ResultStatus ReadIcon(std::vector& buffer) override; + ResultStatus ReadTitle(std::string& title) override; + +private: + std::unique_ptr nsp; + std::unique_ptr secondary_loader; + + FileSys::VirtualFile icon_file; + std::shared_ptr nacp_file; + u64 title_id; +}; + +} // namespace Loader From f7eaea424d07e971d0279257d20d408b64ef05b6 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 11:50:04 -0400 Subject: [PATCH 07/14] registration: Add support for installing NSP files --- src/core/file_sys/registered_cache.cpp | 20 ++++++++++++-------- src/core/file_sys/registered_cache.h | 6 ++++-- src/yuzu/main.cpp | 24 ++++++++++++++++++------ 3 files changed, 34 insertions(+), 16 deletions(-) diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index d9decc104..94268d127 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -358,17 +358,21 @@ std::vector RegisteredCache::ListEntriesFilter( return out; } -static std::shared_ptr GetNCAFromXCIForID(std::shared_ptr xci, const NcaID& id) { - const auto filename = fmt::format("{}.nca", Common::HexArrayToString(id, false)); - const auto iter = - std::find_if(xci->GetNCAs().begin(), xci->GetNCAs().end(), - [&filename](std::shared_ptr nca) { return nca->GetName() == filename; }); - return iter == xci->GetNCAs().end() ? nullptr : *iter; +static std::shared_ptr GetNCAFromNSPForID(std::shared_ptr nsp, const NcaID& id) { + const auto file = nsp->GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false))); + if (file == nullptr) + return nullptr; + return std::make_shared(file); } InstallResult RegisteredCache::InstallEntry(std::shared_ptr xci, bool overwrite_if_exists, const VfsCopyFunction& copy) { - const auto& ncas = xci->GetNCAs(); + return InstallEntry(xci->GetSecurePartitionNSP(), overwrite_if_exists, copy); +} + +InstallResult RegisteredCache::InstallEntry(std::shared_ptr nsp, bool overwrite_if_exists, + const VfsCopyFunction& copy) { + const auto& ncas = nsp->GetNCAsCollapsed(); const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr nca) { return nca->GetType() == NCAContentType::Meta; }); @@ -392,7 +396,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr xci, bool overw const auto cnmt_file = section0->GetFiles()[0]; const CNMT cnmt(cnmt_file); for (const auto& record : cnmt.GetContentRecords()) { - const auto nca = GetNCAFromXCIForID(xci, record.nca_id); + const auto nca = GetNCAFromNSPForID(nsp, record.nca_id); if (nca == nullptr) return InstallResult::ErrorCopyFailed; const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id); diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index fe2cdc3d9..50e26f8fb 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -89,10 +89,12 @@ public: boost::optional record_type = boost::none, boost::optional title_id = boost::none) const; - // Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there - // is a meta NCA and all of them are accessible. + // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure + // there is a meta NCA and all of them are accessible. InstallResult InstallEntry(std::shared_ptr xci, bool overwrite_if_exists = false, const VfsCopyFunction& copy = &VfsRawCopy); + InstallResult InstallEntry(std::shared_ptr nsp, bool overwrite_if_exists = false, + const VfsCopyFunction& copy = &VfsRawCopy); // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 262e33487..c4eda4bab 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -806,22 +806,34 @@ void GMainWindow::OnMenuInstallToNAND() { QMessageBox::Yes; }; - if (filename.endsWith("xci", Qt::CaseInsensitive)) { - const auto xci = std::make_shared( - vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); - if (xci->GetStatus() != Loader::ResultStatus::Success) { + if (filename.endsWith("xci", Qt::CaseInsensitive) || + filename.endsWith("nsp", Qt::CaseInsensitive)) { + + std::shared_ptr nsp; + if (filename.endsWith("nsp", Qt::CaseInsensitive)) { + nsp = std::make_shared( + vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); + if (!nsp->IsExtractedType()) + failed(); + } else { + const auto xci = std::make_shared( + vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); + nsp = xci->GetSecurePartitionNSP(); + } + + if (nsp->GetStatus() != Loader::ResultStatus::Success) { failed(); return; } const auto res = - Service::FileSystem::GetUserNANDContents()->InstallEntry(xci, false, qt_raw_copy); + Service::FileSystem::GetUserNANDContents()->InstallEntry(nsp, false, qt_raw_copy); if (res == FileSys::InstallResult::Success) { success(); } else { if (res == FileSys::InstallResult::ErrorAlreadyExists) { if (overwrite()) { const auto res2 = Service::FileSystem::GetUserNANDContents()->InstallEntry( - xci, true, qt_raw_copy); + nsp, true, qt_raw_copy); if (res2 == FileSys::InstallResult::Success) { success(); } else { From 58473309a08979d657dc09d5594833791e5c920c Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 11:50:15 -0400 Subject: [PATCH 08/14] qt: Add UI support for NSP files --- src/core/CMakeLists.txt | 4 ++++ src/yuzu/game_list.cpp | 2 +- src/yuzu/main.cpp | 3 ++- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index a74270a0f..54afa6a87 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -49,6 +49,8 @@ add_library(core STATIC file_sys/savedata_factory.h file_sys/sdmc_factory.cpp file_sys/sdmc_factory.h + file_sys/submission_package.cpp + file_sys/submission_package.h file_sys/vfs.cpp file_sys/vfs.h file_sys/vfs_concat.cpp @@ -359,6 +361,8 @@ add_library(core STATIC loader/nro.h loader/nso.cpp loader/nso.h + loader/nsp.cpp + loader/nsp.h loader/xci.cpp loader/xci.h memory.cpp diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp index 71953cee3..3e2a5976b 100644 --- a/src/yuzu/game_list.cpp +++ b/src/yuzu/game_list.cpp @@ -432,7 +432,7 @@ void GameList::LoadInterfaceLayout() { item_model->sort(header->sortIndicatorSection(), header->sortIndicatorOrder()); } -const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci"}; +const QStringList GameList::supported_file_extensions = {"nso", "nro", "nca", "xci", "nsp"}; static bool HasSupportedFileExtension(const std::string& file_name) { const QFileInfo file = QFileInfo(QString::fromStdString(file_name)); diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c4eda4bab..e7722cf95 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -746,7 +746,8 @@ void GMainWindow::OnMenuLoadFolder() { void GMainWindow::OnMenuInstallToNAND() { const QString file_filter = - tr("Installable Switch File (*.nca *.xci);;Nintendo Content Archive (*.nca);;NX Cartridge " + tr("Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive " + "(*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge " "Image (*.xci)"); QString filename = QFileDialog::getOpenFileName(this, tr("Install File"), UISettings::values.roms_path, file_filter); From e4e55d064edd71fbf359dec9d6b5efad4f0d6c91 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 25 Aug 2018 22:42:54 -0400 Subject: [PATCH 09/14] nsp: Comply with style and performance guidelines --- src/core/crypto/key_manager.cpp | 2 +- src/core/file_sys/card_image.cpp | 1 + src/core/file_sys/card_image.h | 2 +- src/core/file_sys/submission_package.cpp | 57 ++++++++++++++---------- src/core/file_sys/submission_package.h | 2 + src/core/loader/nsp.cpp | 11 ++++- src/core/loader/nsp.h | 2 +- 7 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp index bd4b3d7c7..6f27f990b 100644 --- a/src/core/crypto/key_manager.cpp +++ b/src/core/crypto/key_manager.cpp @@ -237,7 +237,7 @@ void KeyManager::SetKey(S128KeyType id, Key128 key, u64 field1, u64 field2) { Key128 rights_id; std::memcpy(rights_id.data(), &field2, sizeof(u64)); std::memcpy(rights_id.data() + sizeof(u64), &field1, sizeof(u64)); - WriteKeyToFile(true, fmt::format("{}", Common::HexArrayToString(rights_id)), key); + WriteKeyToFile(true, Common::HexArrayToString(rights_id), key); } const auto iter2 = std::find_if( s128_file_id.begin(), s128_file_id.end(), diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index d0f1afac0..e07ac8503 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -12,6 +12,7 @@ #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/partition_filesystem.h" +#include "core/file_sys/submission_package.h" #include "core/file_sys/vfs_offset.h" #include "core/loader/loader.h" diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index b73f1d900..4d07d3d05 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -11,7 +11,6 @@ #include "common/swap.h" #include "core/file_sys/vfs.h" #include "core/loader/loader.h" -#include "submission_package.h" namespace Loader { enum class ResultStatus : u16; @@ -21,6 +20,7 @@ namespace FileSys { class NCA; enum class NCAContentType : u8; +class NSP; enum class GamecardSize : u8 { S_1GB = 0xFA, diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index 660771cf8..ce05a5845 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -72,21 +72,21 @@ NSP::NSP(VirtualFile file_) ncas_title[ContentRecordType::Meta] = nca; for (const auto& rec : cnmt.GetContentRecords()) { - const auto next_file = pfs->GetFile( - fmt::format("{}.nca", Common::HexArrayToString(rec.nca_id, false))); + const auto id_string = Common::HexArrayToString(rec.nca_id, false); + const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); if (next_file == nullptr) { LOG_WARNING(Service_FS, "NCA with ID {}.nca is listed in content metadata, but cannot " "be found in PFS. NSP appears to be corrupted.", - Common::HexArrayToString(rec.nca_id, false)); + id_string); continue; } - const auto next_nca = std::make_shared(next_file); + auto next_nca = std::make_shared(next_file); if (next_nca->GetType() == NCAContentType::Program) program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); if (next_nca->GetStatus() == Loader::ResultStatus::Success) - ncas_title[rec.type] = next_nca; + ncas_title[rec.type] = std::move(next_nca); } break; @@ -95,14 +95,17 @@ NSP::NSP(VirtualFile file_) } } +NSP::~NSP() = default; + Loader::ResultStatus NSP::GetStatus() const { return status; } Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { - if (program_status.find(title_id) != program_status.end()) - return program_status.at(title_id); - return Loader::ResultStatus::ErrorNSPMissingProgramNCA; + const auto iter = program_status.find(title_id); + if (iter == program_status.end()) + return Loader::ResultStatus::ErrorNSPMissingProgramNCA; + return iter->second; } u64 NSP::GetFirstTitleID() const { @@ -112,16 +115,19 @@ u64 NSP::GetFirstTitleID() const { } u64 NSP::GetProgramTitleID() const { - auto out = GetFirstTitleID(); - for (const auto other_tid : GetTitleIDs()) { - if ((out & 0x800) != 0) - out = other_tid; - } - return out; + 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; } std::vector NSP::GetTitleIDs() const { std::vector out; + out.reserve(ncas.size()); for (const auto& kv : ncas) out.push_back(kv.first); return out; @@ -156,7 +162,7 @@ std::multimap> NSP::GetNCAsByTitleID() const { std::multimap> out; for (const auto& map : ncas) { for (const auto& inner_map : map.second) - out.insert({map.first, inner_map.second}); + out.emplace(map.first, inner_map.second); } return out; } @@ -168,13 +174,16 @@ std::map>> NSP::GetNCAs() std::shared_ptr NSP::GetNCA(u64 title_id, ContentRecordType type) const { if (extracted) LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); - if (ncas.find(title_id) != ncas.end()) { - const auto& inner_map = ncas.at(title_id); - if (inner_map.find(type) != inner_map.end()) - return inner_map.at(type); - } - return nullptr; + const auto title_id_iter = ncas.find(title_id); + if (title_id_iter == ncas.end()) + return nullptr; + + const auto type_iter = title_id_iter->second.find(type); + if (type_iter == title_id_iter->second.end()) + return nullptr; + + return type_iter->second; } VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const { @@ -197,9 +206,9 @@ std::vector NSP::GetTitlekey() const { continue; } - Core::Crypto::Key128 key{}; - ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); - out.push_back(key); + out.emplace_back(); + ticket_file->Read(out.back().data(), out.back().size(), + Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); } return out; } diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index 7b520df57..482a8b71f 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -10,6 +10,7 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/file_sys/content_archive.h" +#include "core/file_sys/romfs_factory.h" #include "core/file_sys/vfs.h" #include "core/loader/loader.h" #include "romfs_factory.h" @@ -19,6 +20,7 @@ namespace FileSys { class NSP : public ReadOnlyVfsDirectory { public: explicit NSP(VirtualFile file); + ~NSP(); Loader::ResultStatus GetStatus() const; Loader::ResultStatus GetProgramStatus(u64 title_id) const; diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index 75d9fc1bc..b59d40052 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -21,22 +21,27 @@ namespace Loader { AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file) : AppLoader(file), nsp(std::make_unique(file)), title_id(nsp->GetProgramTitleID()) { + if (nsp->GetStatus() != ResultStatus::Success) return; if (nsp->IsExtractedType()) return; + const auto control_nca = nsp->GetNCA(nsp->GetFirstTitleID(), FileSys::ContentRecordType::Control); if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) return; + const auto romfs = FileSys::ExtractRomFS(control_nca->GetRomFS()); if (romfs == nullptr) return; + for (const auto& language : FileSys::LANGUAGE_NAMES) { icon_file = romfs->GetFile("icon_" + std::string(language) + ".dat"); if (icon_file != nullptr) break; } + const auto nacp_raw = romfs->GetFile("control.nacp"); if (nacp_raw == nullptr) return; @@ -51,15 +56,17 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) { if (nsp.GetStatus() == ResultStatus::Success) { // Extracted Type case if (nsp.IsExtractedType() && nsp.GetExeFS() != nullptr && - FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) + FileSys::IsDirectoryExeFS(nsp.GetExeFS()) && nsp.GetRomFS() != nullptr) { return FileType::NSP; + } // Non-Ectracted Type case if (!nsp.IsExtractedType() && nsp.GetNCA(nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program) != nullptr && AppLoader_NCA::IdentifyType(nsp.GetNCAFile( - nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) + nsp.GetFirstTitleID(), FileSys::ContentRecordType::Program)) == FileType::NCA) { return FileType::NSP; + } } return FileType::Error; diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h index 785feaf37..7ef810499 100644 --- a/src/core/loader/nsp.h +++ b/src/core/loader/nsp.h @@ -22,7 +22,7 @@ class AppLoader_NCA; class AppLoader_NSP final : public AppLoader { public: explicit AppLoader_NSP(FileSys::VirtualFile file); - ~AppLoader_NSP(); + ~AppLoader_NSP() override; /** * Returns the type of the file From 8974771334aceaaba0912887dacfd3eb1eb0bee6 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 30 Aug 2018 16:59:30 -0400 Subject: [PATCH 10/14] registration: Fix NSP installation errors --- src/yuzu/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index e7722cf95..d7c5d813f 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -814,7 +814,7 @@ void GMainWindow::OnMenuInstallToNAND() { if (filename.endsWith("nsp", Qt::CaseInsensitive)) { nsp = std::make_shared( vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); - if (!nsp->IsExtractedType()) + if (nsp->IsExtractedType()) failed(); } else { const auto xci = std::make_shared( From 128006172578390d7c83575f591dbd8df9361e84 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 30 Aug 2018 16:59:49 -0400 Subject: [PATCH 11/14] qt: Add deprecation warnings for DRD format --- src/yuzu/main.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index d7c5d813f..037bf2aef 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -488,6 +488,16 @@ bool GMainWindow::LoadROM(const QString& filename) { const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; + if (system.GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory) { + QMessageBox::warning( + this, tr("Warning Outdated Game Format"), + tr("You are using the deconstructed ROM directory format for this game, which is an " + "outdated format that has been superseded by others such as NCA, NAX, XCI, or " + "NSP.

For an explanation of the various Switch formats yuzu supports, check out our " + "wiki.")); + } + render_window->DoneCurrent(); if (result != Core::System::ResultStatus::Success) { From 23d2c504795a3efadfa046cfe4b5faf95649f454 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 3 Sep 2018 18:47:23 -0400 Subject: [PATCH 12/14] card_image: Add program title ID getter --- src/core/file_sys/card_image.cpp | 4 ++++ src/core/file_sys/card_image.h | 2 ++ 2 files changed, 6 insertions(+) diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index e07ac8503..1bd3353e4 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -115,6 +115,10 @@ VirtualDir XCI::GetLogoPartition() const { return GetPartition(XCIPartition::Logo); } +u64 XCI::GetProgramTitleID() const { + return secure_partition->GetProgramTitleID(); +} + std::shared_ptr XCI::GetProgramNCA() const { return program; } diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index 4d07d3d05..bd8c0fcbf 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -79,6 +79,8 @@ public: VirtualDir GetUpdatePartition() const; VirtualDir GetLogoPartition() const; + u64 GetProgramTitleID() const; + std::shared_ptr GetProgramNCA() const; VirtualFile GetProgramNCAFile() const; const std::vector>& GetNCAs() const; From e973cceaddf060b3738417504856db1baa4a04fa Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Mon, 3 Sep 2018 19:00:14 -0400 Subject: [PATCH 13/14] control_metadata: Use alternate language names if AmericanEnglish isn't available --- src/core/file_sys/control_metadata.cpp | 12 +++++++++++- src/core/file_sys/control_metadata.h | 9 ++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp index ae21ad5b9..e76bf77bf 100644 --- a/src/core/file_sys/control_metadata.cpp +++ b/src/core/file_sys/control_metadata.cpp @@ -21,7 +21,17 @@ NACP::NACP(VirtualFile file) : raw(std::make_unique()) { } const LanguageEntry& NACP::GetLanguageEntry(Language language) const { - return raw->language_entries.at(static_cast(language)); + if (language != Language::Default) { + return raw->language_entries.at(static_cast(language)); + } else { + for (const auto& language_entry : raw->language_entries) { + if (!language_entry.GetApplicationName().empty()) + return language_entry; + } + + // Fallback to English + return GetLanguageEntry(Language::AmericanEnglish); + } } std::string NACP::GetApplicationName(Language language) const { diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h index 1568046f1..8a510bf46 100644 --- a/src/core/file_sys/control_metadata.h +++ b/src/core/file_sys/control_metadata.h @@ -9,6 +9,7 @@ #include #include "common/common_funcs.h" #include "common/common_types.h" +#include "common/swap.h" #include "core/file_sys/vfs.h" namespace FileSys { @@ -61,6 +62,8 @@ enum class Language : u8 { Korean = 12, Taiwanese = 13, Chinese = 14, + + Default = 255, }; static constexpr std::array LANGUAGE_NAMES = { @@ -75,9 +78,9 @@ static constexpr std::array LANGUAGE_NAMES = { class NACP { public: explicit NACP(VirtualFile file); - const LanguageEntry& GetLanguageEntry(Language language = Language::AmericanEnglish) const; - std::string GetApplicationName(Language language = Language::AmericanEnglish) const; - std::string GetDeveloperName(Language language = Language::AmericanEnglish) const; + const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const; + std::string GetApplicationName(Language language = Language::Default) const; + std::string GetDeveloperName(Language language = Language::Default) const; u64 GetTitleId() const; std::string GetVersionString() const; From 87be4bc283eee72a51b5e8391147c60671351b80 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Tue, 4 Sep 2018 14:44:40 -0400 Subject: [PATCH 14/14] main: Only show DRD deprecation warning once --- src/core/file_sys/card_image.h | 1 - src/core/file_sys/registered_cache.cpp | 1 + src/core/file_sys/registered_cache.h | 1 + src/core/file_sys/submission_package.cpp | 1 + src/core/file_sys/submission_package.h | 3 ++- src/core/loader/nsp.cpp | 2 +- src/yuzu/main.cpp | 16 +++++++++++++--- 7 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h index bd8c0fcbf..ce514dfa0 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -10,7 +10,6 @@ #include "common/common_types.h" #include "common/swap.h" #include "core/file_sys/vfs.h" -#include "core/loader/loader.h" namespace Loader { enum class ResultStatus : u16; diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 94268d127..cf6f77401 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -13,6 +13,7 @@ #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" #include "core/file_sys/registered_cache.h" +#include "core/file_sys/submission_package.h" #include "core/file_sys/vfs_concat.h" #include "core/loader/loader.h" diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index 50e26f8fb..467ceeef1 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -17,6 +17,7 @@ namespace FileSys { class CNMT; class NCA; +class NSP; class XCI; enum class ContentRecordType : u8; diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp index ce05a5845..bde879861 100644 --- a/src/core/file_sys/submission_package.cpp +++ b/src/core/file_sys/submission_package.cpp @@ -7,6 +7,7 @@ #include "common/hex_util.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/nca_metadata.h" +#include "core/file_sys/partition_filesystem.h" #include "core/file_sys/submission_package.h" #include "core/loader/loader.h" diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h index 482a8b71f..0292164f9 100644 --- a/src/core/file_sys/submission_package.h +++ b/src/core/file_sys/submission_package.h @@ -13,10 +13,11 @@ #include "core/file_sys/romfs_factory.h" #include "core/file_sys/vfs.h" #include "core/loader/loader.h" -#include "romfs_factory.h" namespace FileSys { +class PartitionFilesystem; + class NSP : public ReadOnlyVfsDirectory { public: explicit NSP(VirtualFile file); diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp index b59d40052..7c06239f2 100644 --- a/src/core/loader/nsp.cpp +++ b/src/core/loader/nsp.cpp @@ -8,7 +8,7 @@ #include "core/file_sys/card_image.h" #include "core/file_sys/content_archive.h" #include "core/file_sys/control_metadata.h" -#include "core/file_sys/registered_cache.h" +#include "core/file_sys/nca_metadata.h" #include "core/file_sys/romfs.h" #include "core/file_sys/submission_package.h" #include "core/hle/kernel/process.h" diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index 037bf2aef..56bd3ee2e 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -34,7 +34,9 @@ #include "core/file_sys/content_archive.h" #include "core/file_sys/registered_cache.h" #include "core/file_sys/savedata_factory.h" +#include "core/file_sys/submission_package.h" #include "core/file_sys/vfs_real.h" +#include "core/hle/kernel/process.h" #include "core/hle/service/filesystem/filesystem.h" #include "core/loader/loader.h" #include "core/perf_stats.h" @@ -76,6 +78,7 @@ __declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; */ enum class CalloutFlag : uint32_t { Telemetry = 0x1, + DRDDeprecation = 0x2, }; static void ShowCalloutMessage(const QString& message, CalloutFlag flag) { @@ -488,14 +491,21 @@ bool GMainWindow::LoadROM(const QString& filename) { const Core::System::ResultStatus result{system.Load(*render_window, filename.toStdString())}; - if (system.GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory) { + const auto drd_callout = + (UISettings::values.callout_flags & static_cast(CalloutFlag::DRDDeprecation)) == 0; + + if (result == Core::System::ResultStatus::Success && + system.GetAppLoader().GetFileType() == Loader::FileType::DeconstructedRomDirectory && + drd_callout) { + UISettings::values.callout_flags |= static_cast(CalloutFlag::DRDDeprecation); QMessageBox::warning( this, tr("Warning Outdated Game Format"), tr("You are using the deconstructed ROM directory format for this game, which is an " "outdated format that has been superseded by others such as NCA, NAX, XCI, or " - "NSP.

For an explanation of the various Switch formats yuzu supports,
For an explanation of the various Switch formats yuzu supports,
check out our " - "wiki.")); + "wiki. This message will not be shown again.")); } render_window->DoneCurrent();