diff options
Diffstat (limited to 'src/core/file_sys')
-rw-r--r-- | src/core/file_sys/card_image.cpp | 38 | ||||
-rw-r--r-- | src/core/file_sys/card_image.h | 8 | ||||
-rw-r--r-- | src/core/file_sys/control_metadata.cpp | 12 | ||||
-rw-r--r-- | src/core/file_sys/control_metadata.h | 9 | ||||
-rw-r--r-- | src/core/file_sys/registered_cache.cpp | 21 | ||||
-rw-r--r-- | src/core/file_sys/registered_cache.h | 7 | ||||
-rw-r--r-- | src/core/file_sys/submission_package.cpp | 236 | ||||
-rw-r--r-- | src/core/file_sys/submission_package.h | 73 |
8 files changed, 383 insertions, 21 deletions
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp index ce4423fa6..1bd3353e4 100644 --- a/src/core/file_sys/card_image.cpp +++ b/src/core/file_sys/card_image.cpp @@ -10,7 +10,9 @@ #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/submission_package.h" #include "core/file_sys/vfs_offset.h" #include "core/loader/loader.h" @@ -44,15 +46,19 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw); } - program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; + secure_partition = std::make_shared<NSP>( + main_hfs.GetFile(partition_names[static_cast<size_t>(XCIPartition::Secure)])); - auto result = AddNCAFromPartition(XCIPartition::Secure); - if (result != Loader::ResultStatus::Success) { - status = result; - return; - } + 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(); - result = AddNCAFromPartition(XCIPartition::Update); + auto result = AddNCAFromPartition(XCIPartition::Update); if (result != Loader::ResultStatus::Success) { status = result; return; @@ -89,6 +95,10 @@ VirtualDir XCI::GetPartition(XCIPartition partition) const { return partitions[static_cast<size_t>(partition)]; } +std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const { + return secure_partition; +} + VirtualDir XCI::GetSecurePartition() const { return GetPartition(XCIPartition::Secure); } @@ -105,6 +115,20 @@ VirtualDir XCI::GetLogoPartition() const { return GetPartition(XCIPartition::Logo); } +u64 XCI::GetProgramTitleID() const { + return secure_partition->GetProgramTitleID(); +} + +std::shared_ptr<NCA> XCI::GetProgramNCA() const { + return program; +} + +VirtualFile XCI::GetProgramNCAFile() const { + if (GetProgramNCA() == nullptr) + return nullptr; + return GetProgramNCA()->GetBaseFile(); +} + const std::vector<std::shared_ptr<NCA>>& 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..ce514dfa0 100644 --- a/src/core/file_sys/card_image.h +++ b/src/core/file_sys/card_image.h @@ -19,6 +19,7 @@ namespace FileSys { class NCA; enum class NCAContentType : u8; +class NSP; enum class GamecardSize : u8 { S_1GB = 0xFA, @@ -71,11 +72,16 @@ public: u8 GetFormatVersion() const; VirtualDir GetPartition(XCIPartition partition) const; + std::shared_ptr<NSP> GetSecurePartitionNSP() const; VirtualDir GetSecurePartition() const; VirtualDir GetNormalPartition() const; VirtualDir GetUpdatePartition() const; VirtualDir GetLogoPartition() const; + u64 GetProgramTitleID() const; + + std::shared_ptr<NCA> GetProgramNCA() const; + VirtualFile GetProgramNCAFile() const; const std::vector<std::shared_ptr<NCA>>& GetNCAs() const; std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; VirtualFile GetNCAFileByType(NCAContentType type) const; @@ -101,6 +107,8 @@ private: Loader::ResultStatus program_nca_status; std::vector<VirtualDir> partitions; + std::shared_ptr<NSP> secure_partition; + std::shared_ptr<NCA> program; std::vector<std::shared_ptr<NCA>> ncas; }; } // namespace FileSys 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<RawNACP>()) { } const LanguageEntry& NACP::GetLanguageEntry(Language language) const { - return raw->language_entries.at(static_cast<u8>(language)); + if (language != Language::Default) { + return raw->language_entries.at(static_cast<u8>(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 <string> #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<const char*, 15> LANGUAGE_NAMES = { @@ -75,9 +78,9 @@ static constexpr std::array<const char*, 15> 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; diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index d9decc104..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" @@ -358,17 +359,21 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter( return out; } -static std::shared_ptr<NCA> GetNCAFromXCIForID(std::shared_ptr<XCI> 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> nca) { return nca->GetName() == filename; }); - return iter == xci->GetNCAs().end() ? nullptr : *iter; +static std::shared_ptr<NCA> GetNCAFromNSPForID(std::shared_ptr<NSP> 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<NCA>(file); } InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> 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> 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> nca) { return nca->GetType() == NCAContentType::Meta; }); @@ -392,7 +397,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> 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..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; @@ -89,10 +90,12 @@ public: boost::optional<ContentRecordType> record_type = boost::none, boost::optional<u64> 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> xci, bool overwrite_if_exists = false, const VfsCopyFunction& copy = &VfsRawCopy); + InstallResult InstallEntry(std::shared_ptr<NSP> 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/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp new file mode 100644 index 000000000..bde879861 --- /dev/null +++ b/src/core/file_sys/submission_package.cpp @@ -0,0 +1,236 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include <fmt/ostream.h> +#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/partition_filesystem.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<PartitionFilesystem>(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<NCA>(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 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.", + id_string); + continue; + } + + auto next_nca = std::make_shared<NCA>(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] = std::move(next_nca); + } + + break; + } + } + } +} + +NSP::~NSP() = default; + +Loader::ResultStatus NSP::GetStatus() const { + return status; +} + +Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { + const auto iter = program_status.find(title_id); + if (iter == program_status.end()) + return Loader::ResultStatus::ErrorNSPMissingProgramNCA; + return iter->second; +} + +u64 NSP::GetFirstTitleID() const { + if (program_status.empty()) + return 0; + return program_status.begin()->first; +} + +u64 NSP::GetProgramTitleID() const { + 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<u64> NSP::GetTitleIDs() const { + std::vector<u64> out; + out.reserve(ncas.size()); + 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) + out.emplace(map.first, inner_map.second); + } + return out; +} + +std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const { + return ncas; +} + +std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const { + if (extracted) + LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); + + 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 { + 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; + } + + out.emplace_back(); + ticket_file->Read(out.back().data(), out.back().size(), + Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); + } + 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(); +} + +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..0292164f9 --- /dev/null +++ b/src/core/file_sys/submission_package.h @@ -0,0 +1,73 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <array> +#include <map> +#include <vector> +#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" + +namespace FileSys { + +class PartitionFilesystem; + +class NSP : public ReadOnlyVfsDirectory { +public: + explicit NSP(VirtualFile file); + ~NSP(); + + 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<u64> 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<std::shared_ptr<NCA>> GetNCAsCollapsed() const; + std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const; + std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const; + std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const; + VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const; + std::vector<Core::Crypto::Key128> GetTitlekey() const; + + std::vector<VirtualFile> GetFiles() const override; + + std::vector<VirtualDir> 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<u64, Loader::ResultStatus> program_status; + + std::shared_ptr<PartitionFilesystem> pfs; + // Map title id -> {map type -> NCA} + std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas; + std::vector<VirtualFile> ticket_files; + + VirtualFile romfs; + VirtualDir exefs; +}; +} // namespace FileSys |