diff options
| author | 2018-09-04 16:20:40 -0400 | |
|---|---|---|
| committer | 2018-09-04 16:20:40 -0400 | |
| commit | faa9e066aba320bcd38fd023ee58c6f9e1d3efdd (patch) | |
| tree | c369b13af5a30698564ee54acbae639be4576482 /src/core/file_sys | |
| parent | Merge pull request #1238 from lioncash/explicit (diff) | |
| parent | main: Only show DRD deprecation warning once (diff) | |
| download | yuzu-faa9e066aba320bcd38fd023ee58c6f9e1d3efdd.tar.gz yuzu-faa9e066aba320bcd38fd023ee58c6f9e1d3efdd.tar.xz yuzu-faa9e066aba320bcd38fd023ee58c6f9e1d3efdd.zip | |
Merge pull request #1178 from DarkLordZach/nsp
file_sys: Add Nintendo Submissions Package (NSP) file format
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 @@ | |||
| 10 | #include "common/logging/log.h" | 10 | #include "common/logging/log.h" |
| 11 | #include "core/file_sys/card_image.h" | 11 | #include "core/file_sys/card_image.h" |
| 12 | #include "core/file_sys/content_archive.h" | 12 | #include "core/file_sys/content_archive.h" |
| 13 | #include "core/file_sys/nca_metadata.h" | ||
| 13 | #include "core/file_sys/partition_filesystem.h" | 14 | #include "core/file_sys/partition_filesystem.h" |
| 15 | #include "core/file_sys/submission_package.h" | ||
| 14 | #include "core/file_sys/vfs_offset.h" | 16 | #include "core/file_sys/vfs_offset.h" |
| 15 | #include "core/loader/loader.h" | 17 | #include "core/loader/loader.h" |
| 16 | 18 | ||
| @@ -44,15 +46,19 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) { | |||
| 44 | partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw); | 46 | partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw); |
| 45 | } | 47 | } |
| 46 | 48 | ||
| 47 | program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; | 49 | secure_partition = std::make_shared<NSP>( |
| 50 | main_hfs.GetFile(partition_names[static_cast<size_t>(XCIPartition::Secure)])); | ||
| 48 | 51 | ||
| 49 | auto result = AddNCAFromPartition(XCIPartition::Secure); | 52 | const auto secure_ncas = secure_partition->GetNCAsCollapsed(); |
| 50 | if (result != Loader::ResultStatus::Success) { | 53 | std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas)); |
| 51 | status = result; | 54 | |
| 52 | return; | 55 | program_nca_status = Loader::ResultStatus::ErrorXCIMissingProgramNCA; |
| 53 | } | 56 | program = |
| 57 | secure_partition->GetNCA(secure_partition->GetProgramTitleID(), ContentRecordType::Program); | ||
| 58 | if (program != nullptr) | ||
| 59 | program_nca_status = program->GetStatus(); | ||
| 54 | 60 | ||
| 55 | result = AddNCAFromPartition(XCIPartition::Update); | 61 | auto result = AddNCAFromPartition(XCIPartition::Update); |
| 56 | if (result != Loader::ResultStatus::Success) { | 62 | if (result != Loader::ResultStatus::Success) { |
| 57 | status = result; | 63 | status = result; |
| 58 | return; | 64 | return; |
| @@ -89,6 +95,10 @@ VirtualDir XCI::GetPartition(XCIPartition partition) const { | |||
| 89 | return partitions[static_cast<size_t>(partition)]; | 95 | return partitions[static_cast<size_t>(partition)]; |
| 90 | } | 96 | } |
| 91 | 97 | ||
| 98 | std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const { | ||
| 99 | return secure_partition; | ||
| 100 | } | ||
| 101 | |||
| 92 | VirtualDir XCI::GetSecurePartition() const { | 102 | VirtualDir XCI::GetSecurePartition() const { |
| 93 | return GetPartition(XCIPartition::Secure); | 103 | return GetPartition(XCIPartition::Secure); |
| 94 | } | 104 | } |
| @@ -105,6 +115,20 @@ VirtualDir XCI::GetLogoPartition() const { | |||
| 105 | return GetPartition(XCIPartition::Logo); | 115 | return GetPartition(XCIPartition::Logo); |
| 106 | } | 116 | } |
| 107 | 117 | ||
| 118 | u64 XCI::GetProgramTitleID() const { | ||
| 119 | return secure_partition->GetProgramTitleID(); | ||
| 120 | } | ||
| 121 | |||
| 122 | std::shared_ptr<NCA> XCI::GetProgramNCA() const { | ||
| 123 | return program; | ||
| 124 | } | ||
| 125 | |||
| 126 | VirtualFile XCI::GetProgramNCAFile() const { | ||
| 127 | if (GetProgramNCA() == nullptr) | ||
| 128 | return nullptr; | ||
| 129 | return GetProgramNCA()->GetBaseFile(); | ||
| 130 | } | ||
| 131 | |||
| 108 | const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const { | 132 | const std::vector<std::shared_ptr<NCA>>& XCI::GetNCAs() const { |
| 109 | return ncas; | 133 | return ncas; |
| 110 | } | 134 | } |
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 { | |||
| 19 | 19 | ||
| 20 | class NCA; | 20 | class NCA; |
| 21 | enum class NCAContentType : u8; | 21 | enum class NCAContentType : u8; |
| 22 | class NSP; | ||
| 22 | 23 | ||
| 23 | enum class GamecardSize : u8 { | 24 | enum class GamecardSize : u8 { |
| 24 | S_1GB = 0xFA, | 25 | S_1GB = 0xFA, |
| @@ -71,11 +72,16 @@ public: | |||
| 71 | u8 GetFormatVersion() const; | 72 | u8 GetFormatVersion() const; |
| 72 | 73 | ||
| 73 | VirtualDir GetPartition(XCIPartition partition) const; | 74 | VirtualDir GetPartition(XCIPartition partition) const; |
| 75 | std::shared_ptr<NSP> GetSecurePartitionNSP() const; | ||
| 74 | VirtualDir GetSecurePartition() const; | 76 | VirtualDir GetSecurePartition() const; |
| 75 | VirtualDir GetNormalPartition() const; | 77 | VirtualDir GetNormalPartition() const; |
| 76 | VirtualDir GetUpdatePartition() const; | 78 | VirtualDir GetUpdatePartition() const; |
| 77 | VirtualDir GetLogoPartition() const; | 79 | VirtualDir GetLogoPartition() const; |
| 78 | 80 | ||
| 81 | u64 GetProgramTitleID() const; | ||
| 82 | |||
| 83 | std::shared_ptr<NCA> GetProgramNCA() const; | ||
| 84 | VirtualFile GetProgramNCAFile() const; | ||
| 79 | const std::vector<std::shared_ptr<NCA>>& GetNCAs() const; | 85 | const std::vector<std::shared_ptr<NCA>>& GetNCAs() const; |
| 80 | std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; | 86 | std::shared_ptr<NCA> GetNCAByType(NCAContentType type) const; |
| 81 | VirtualFile GetNCAFileByType(NCAContentType type) const; | 87 | VirtualFile GetNCAFileByType(NCAContentType type) const; |
| @@ -101,6 +107,8 @@ private: | |||
| 101 | Loader::ResultStatus program_nca_status; | 107 | Loader::ResultStatus program_nca_status; |
| 102 | 108 | ||
| 103 | std::vector<VirtualDir> partitions; | 109 | std::vector<VirtualDir> partitions; |
| 110 | std::shared_ptr<NSP> secure_partition; | ||
| 111 | std::shared_ptr<NCA> program; | ||
| 104 | std::vector<std::shared_ptr<NCA>> ncas; | 112 | std::vector<std::shared_ptr<NCA>> ncas; |
| 105 | }; | 113 | }; |
| 106 | } // namespace FileSys | 114 | } // 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>()) { | |||
| 21 | } | 21 | } |
| 22 | 22 | ||
| 23 | const LanguageEntry& NACP::GetLanguageEntry(Language language) const { | 23 | const LanguageEntry& NACP::GetLanguageEntry(Language language) const { |
| 24 | return raw->language_entries.at(static_cast<u8>(language)); | 24 | if (language != Language::Default) { |
| 25 | return raw->language_entries.at(static_cast<u8>(language)); | ||
| 26 | } else { | ||
| 27 | for (const auto& language_entry : raw->language_entries) { | ||
| 28 | if (!language_entry.GetApplicationName().empty()) | ||
| 29 | return language_entry; | ||
| 30 | } | ||
| 31 | |||
| 32 | // Fallback to English | ||
| 33 | return GetLanguageEntry(Language::AmericanEnglish); | ||
| 34 | } | ||
| 25 | } | 35 | } |
| 26 | 36 | ||
| 27 | std::string NACP::GetApplicationName(Language language) const { | 37 | 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 @@ | |||
| 9 | #include <string> | 9 | #include <string> |
| 10 | #include "common/common_funcs.h" | 10 | #include "common/common_funcs.h" |
| 11 | #include "common/common_types.h" | 11 | #include "common/common_types.h" |
| 12 | #include "common/swap.h" | ||
| 12 | #include "core/file_sys/vfs.h" | 13 | #include "core/file_sys/vfs.h" |
| 13 | 14 | ||
| 14 | namespace FileSys { | 15 | namespace FileSys { |
| @@ -61,6 +62,8 @@ enum class Language : u8 { | |||
| 61 | Korean = 12, | 62 | Korean = 12, |
| 62 | Taiwanese = 13, | 63 | Taiwanese = 13, |
| 63 | Chinese = 14, | 64 | Chinese = 14, |
| 65 | |||
| 66 | Default = 255, | ||
| 64 | }; | 67 | }; |
| 65 | 68 | ||
| 66 | static constexpr std::array<const char*, 15> LANGUAGE_NAMES = { | 69 | static constexpr std::array<const char*, 15> LANGUAGE_NAMES = { |
| @@ -75,9 +78,9 @@ static constexpr std::array<const char*, 15> LANGUAGE_NAMES = { | |||
| 75 | class NACP { | 78 | class NACP { |
| 76 | public: | 79 | public: |
| 77 | explicit NACP(VirtualFile file); | 80 | explicit NACP(VirtualFile file); |
| 78 | const LanguageEntry& GetLanguageEntry(Language language = Language::AmericanEnglish) const; | 81 | const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const; |
| 79 | std::string GetApplicationName(Language language = Language::AmericanEnglish) const; | 82 | std::string GetApplicationName(Language language = Language::Default) const; |
| 80 | std::string GetDeveloperName(Language language = Language::AmericanEnglish) const; | 83 | std::string GetDeveloperName(Language language = Language::Default) const; |
| 81 | u64 GetTitleId() const; | 84 | u64 GetTitleId() const; |
| 82 | std::string GetVersionString() const; | 85 | std::string GetVersionString() const; |
| 83 | 86 | ||
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 @@ | |||
| 13 | #include "core/file_sys/content_archive.h" | 13 | #include "core/file_sys/content_archive.h" |
| 14 | #include "core/file_sys/nca_metadata.h" | 14 | #include "core/file_sys/nca_metadata.h" |
| 15 | #include "core/file_sys/registered_cache.h" | 15 | #include "core/file_sys/registered_cache.h" |
| 16 | #include "core/file_sys/submission_package.h" | ||
| 16 | #include "core/file_sys/vfs_concat.h" | 17 | #include "core/file_sys/vfs_concat.h" |
| 17 | #include "core/loader/loader.h" | 18 | #include "core/loader/loader.h" |
| 18 | 19 | ||
| @@ -358,17 +359,21 @@ std::vector<RegisteredCacheEntry> RegisteredCache::ListEntriesFilter( | |||
| 358 | return out; | 359 | return out; |
| 359 | } | 360 | } |
| 360 | 361 | ||
| 361 | static std::shared_ptr<NCA> GetNCAFromXCIForID(std::shared_ptr<XCI> xci, const NcaID& id) { | 362 | static std::shared_ptr<NCA> GetNCAFromNSPForID(std::shared_ptr<NSP> nsp, const NcaID& id) { |
| 362 | const auto filename = fmt::format("{}.nca", Common::HexArrayToString(id, false)); | 363 | const auto file = nsp->GetFile(fmt::format("{}.nca", Common::HexArrayToString(id, false))); |
| 363 | const auto iter = | 364 | if (file == nullptr) |
| 364 | std::find_if(xci->GetNCAs().begin(), xci->GetNCAs().end(), | 365 | return nullptr; |
| 365 | [&filename](std::shared_ptr<NCA> nca) { return nca->GetName() == filename; }); | 366 | return std::make_shared<NCA>(file); |
| 366 | return iter == xci->GetNCAs().end() ? nullptr : *iter; | ||
| 367 | } | 367 | } |
| 368 | 368 | ||
| 369 | InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists, | 369 | InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists, |
| 370 | const VfsCopyFunction& copy) { | 370 | const VfsCopyFunction& copy) { |
| 371 | const auto& ncas = xci->GetNCAs(); | 371 | return InstallEntry(xci->GetSecurePartitionNSP(), overwrite_if_exists, copy); |
| 372 | } | ||
| 373 | |||
| 374 | InstallResult RegisteredCache::InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists, | ||
| 375 | const VfsCopyFunction& copy) { | ||
| 376 | const auto& ncas = nsp->GetNCAsCollapsed(); | ||
| 372 | const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) { | 377 | const auto& meta_iter = std::find_if(ncas.begin(), ncas.end(), [](std::shared_ptr<NCA> nca) { |
| 373 | return nca->GetType() == NCAContentType::Meta; | 378 | return nca->GetType() == NCAContentType::Meta; |
| 374 | }); | 379 | }); |
| @@ -392,7 +397,7 @@ InstallResult RegisteredCache::InstallEntry(std::shared_ptr<XCI> xci, bool overw | |||
| 392 | const auto cnmt_file = section0->GetFiles()[0]; | 397 | const auto cnmt_file = section0->GetFiles()[0]; |
| 393 | const CNMT cnmt(cnmt_file); | 398 | const CNMT cnmt(cnmt_file); |
| 394 | for (const auto& record : cnmt.GetContentRecords()) { | 399 | for (const auto& record : cnmt.GetContentRecords()) { |
| 395 | const auto nca = GetNCAFromXCIForID(xci, record.nca_id); | 400 | const auto nca = GetNCAFromNSPForID(nsp, record.nca_id); |
| 396 | if (nca == nullptr) | 401 | if (nca == nullptr) |
| 397 | return InstallResult::ErrorCopyFailed; | 402 | return InstallResult::ErrorCopyFailed; |
| 398 | const auto res2 = RawInstallNCA(nca, copy, overwrite_if_exists, record.nca_id); | 403 | 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 @@ | |||
| 17 | namespace FileSys { | 17 | namespace FileSys { |
| 18 | class CNMT; | 18 | class CNMT; |
| 19 | class NCA; | 19 | class NCA; |
| 20 | class NSP; | ||
| 20 | class XCI; | 21 | class XCI; |
| 21 | 22 | ||
| 22 | enum class ContentRecordType : u8; | 23 | enum class ContentRecordType : u8; |
| @@ -89,10 +90,12 @@ public: | |||
| 89 | boost::optional<ContentRecordType> record_type = boost::none, | 90 | boost::optional<ContentRecordType> record_type = boost::none, |
| 90 | boost::optional<u64> title_id = boost::none) const; | 91 | boost::optional<u64> title_id = boost::none) const; |
| 91 | 92 | ||
| 92 | // Raw copies all the ncas from the xci to the csache. Does some quick checks to make sure there | 93 | // Raw copies all the ncas from the xci/nsp to the csache. Does some quick checks to make sure |
| 93 | // is a meta NCA and all of them are accessible. | 94 | // there is a meta NCA and all of them are accessible. |
| 94 | InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false, | 95 | InstallResult InstallEntry(std::shared_ptr<XCI> xci, bool overwrite_if_exists = false, |
| 95 | const VfsCopyFunction& copy = &VfsRawCopy); | 96 | const VfsCopyFunction& copy = &VfsRawCopy); |
| 97 | InstallResult InstallEntry(std::shared_ptr<NSP> nsp, bool overwrite_if_exists = false, | ||
| 98 | const VfsCopyFunction& copy = &VfsRawCopy); | ||
| 96 | 99 | ||
| 97 | // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this | 100 | // Due to the fact that we must use Meta-type NCAs to determine the existance of files, this |
| 98 | // poses quite a challenge. Instead of creating a new meta NCA for this file, yuzu will create a | 101 | // 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 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <fmt/ostream.h> | ||
| 6 | #include "common/assert.h" | ||
| 7 | #include "common/hex_util.h" | ||
| 8 | #include "core/file_sys/content_archive.h" | ||
| 9 | #include "core/file_sys/nca_metadata.h" | ||
| 10 | #include "core/file_sys/partition_filesystem.h" | ||
| 11 | #include "core/file_sys/submission_package.h" | ||
| 12 | #include "core/loader/loader.h" | ||
| 13 | |||
| 14 | namespace FileSys { | ||
| 15 | NSP::NSP(VirtualFile file_) | ||
| 16 | : file(std::move(file_)), | ||
| 17 | pfs(std::make_shared<PartitionFilesystem>(file)), status{Loader::ResultStatus::Success} { | ||
| 18 | if (pfs->GetStatus() != Loader::ResultStatus::Success) { | ||
| 19 | status = pfs->GetStatus(); | ||
| 20 | return; | ||
| 21 | } | ||
| 22 | |||
| 23 | if (IsDirectoryExeFS(pfs)) { | ||
| 24 | extracted = true; | ||
| 25 | exefs = pfs; | ||
| 26 | |||
| 27 | const auto& files = pfs->GetFiles(); | ||
| 28 | const auto romfs_iter = | ||
| 29 | std::find_if(files.begin(), files.end(), [](const FileSys::VirtualFile& file) { | ||
| 30 | return file->GetName().find(".romfs") != std::string::npos; | ||
| 31 | }); | ||
| 32 | if (romfs_iter != files.end()) | ||
| 33 | romfs = *romfs_iter; | ||
| 34 | return; | ||
| 35 | } | ||
| 36 | |||
| 37 | extracted = false; | ||
| 38 | const auto files = pfs->GetFiles(); | ||
| 39 | |||
| 40 | Core::Crypto::KeyManager keys; | ||
| 41 | for (const auto& ticket_file : files) { | ||
| 42 | if (ticket_file->GetExtension() == "tik") { | ||
| 43 | if (ticket_file == nullptr || | ||
| 44 | ticket_file->GetSize() < | ||
| 45 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { | ||
| 46 | continue; | ||
| 47 | } | ||
| 48 | |||
| 49 | Core::Crypto::Key128 key{}; | ||
| 50 | ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); | ||
| 51 | std::string_view name_only(ticket_file->GetName()); | ||
| 52 | name_only.remove_suffix(4); | ||
| 53 | const auto rights_id_raw = Common::HexStringToArray<16>(name_only); | ||
| 54 | u128 rights_id; | ||
| 55 | std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128)); | ||
| 56 | keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]); | ||
| 57 | } | ||
| 58 | } | ||
| 59 | |||
| 60 | for (const auto& outer_file : files) { | ||
| 61 | if (outer_file->GetName().substr(outer_file->GetName().size() - 9) == ".cnmt.nca") { | ||
| 62 | const auto nca = std::make_shared<NCA>(outer_file); | ||
| 63 | if (nca->GetStatus() != Loader::ResultStatus::Success) | ||
| 64 | continue; | ||
| 65 | const auto section0 = nca->GetSubdirectories()[0]; | ||
| 66 | |||
| 67 | for (const auto& inner_file : section0->GetFiles()) { | ||
| 68 | if (inner_file->GetExtension() != "cnmt") | ||
| 69 | continue; | ||
| 70 | |||
| 71 | const CNMT cnmt(inner_file); | ||
| 72 | auto& ncas_title = ncas[cnmt.GetTitleID()]; | ||
| 73 | |||
| 74 | ncas_title[ContentRecordType::Meta] = nca; | ||
| 75 | for (const auto& rec : cnmt.GetContentRecords()) { | ||
| 76 | const auto id_string = Common::HexArrayToString(rec.nca_id, false); | ||
| 77 | const auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string)); | ||
| 78 | if (next_file == nullptr) { | ||
| 79 | LOG_WARNING(Service_FS, | ||
| 80 | "NCA with ID {}.nca is listed in content metadata, but cannot " | ||
| 81 | "be found in PFS. NSP appears to be corrupted.", | ||
| 82 | id_string); | ||
| 83 | continue; | ||
| 84 | } | ||
| 85 | |||
| 86 | auto next_nca = std::make_shared<NCA>(next_file); | ||
| 87 | if (next_nca->GetType() == NCAContentType::Program) | ||
| 88 | program_status[cnmt.GetTitleID()] = next_nca->GetStatus(); | ||
| 89 | if (next_nca->GetStatus() == Loader::ResultStatus::Success) | ||
| 90 | ncas_title[rec.type] = std::move(next_nca); | ||
| 91 | } | ||
| 92 | |||
| 93 | break; | ||
| 94 | } | ||
| 95 | } | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | NSP::~NSP() = default; | ||
| 100 | |||
| 101 | Loader::ResultStatus NSP::GetStatus() const { | ||
| 102 | return status; | ||
| 103 | } | ||
| 104 | |||
| 105 | Loader::ResultStatus NSP::GetProgramStatus(u64 title_id) const { | ||
| 106 | const auto iter = program_status.find(title_id); | ||
| 107 | if (iter == program_status.end()) | ||
| 108 | return Loader::ResultStatus::ErrorNSPMissingProgramNCA; | ||
| 109 | return iter->second; | ||
| 110 | } | ||
| 111 | |||
| 112 | u64 NSP::GetFirstTitleID() const { | ||
| 113 | if (program_status.empty()) | ||
| 114 | return 0; | ||
| 115 | return program_status.begin()->first; | ||
| 116 | } | ||
| 117 | |||
| 118 | u64 NSP::GetProgramTitleID() const { | ||
| 119 | const auto out = GetFirstTitleID(); | ||
| 120 | if ((out & 0x800) == 0) | ||
| 121 | return out; | ||
| 122 | |||
| 123 | const auto ids = GetTitleIDs(); | ||
| 124 | const auto iter = | ||
| 125 | std::find_if(ids.begin(), ids.end(), [](u64 tid) { return (tid & 0x800) == 0; }); | ||
| 126 | return iter == ids.end() ? out : *iter; | ||
| 127 | } | ||
| 128 | |||
| 129 | std::vector<u64> NSP::GetTitleIDs() const { | ||
| 130 | std::vector<u64> out; | ||
| 131 | out.reserve(ncas.size()); | ||
| 132 | for (const auto& kv : ncas) | ||
| 133 | out.push_back(kv.first); | ||
| 134 | return out; | ||
| 135 | } | ||
| 136 | |||
| 137 | bool NSP::IsExtractedType() const { | ||
| 138 | return extracted; | ||
| 139 | } | ||
| 140 | |||
| 141 | VirtualFile NSP::GetRomFS() const { | ||
| 142 | return romfs; | ||
| 143 | } | ||
| 144 | |||
| 145 | VirtualDir NSP::GetExeFS() const { | ||
| 146 | return exefs; | ||
| 147 | } | ||
| 148 | |||
| 149 | std::vector<std::shared_ptr<NCA>> NSP::GetNCAsCollapsed() const { | ||
| 150 | if (extracted) | ||
| 151 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | ||
| 152 | std::vector<std::shared_ptr<NCA>> out; | ||
| 153 | for (const auto& map : ncas) { | ||
| 154 | for (const auto& inner_map : map.second) | ||
| 155 | out.push_back(inner_map.second); | ||
| 156 | } | ||
| 157 | return out; | ||
| 158 | } | ||
| 159 | |||
| 160 | std::multimap<u64, std::shared_ptr<NCA>> NSP::GetNCAsByTitleID() const { | ||
| 161 | if (extracted) | ||
| 162 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | ||
| 163 | std::multimap<u64, std::shared_ptr<NCA>> out; | ||
| 164 | for (const auto& map : ncas) { | ||
| 165 | for (const auto& inner_map : map.second) | ||
| 166 | out.emplace(map.first, inner_map.second); | ||
| 167 | } | ||
| 168 | return out; | ||
| 169 | } | ||
| 170 | |||
| 171 | std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> NSP::GetNCAs() const { | ||
| 172 | return ncas; | ||
| 173 | } | ||
| 174 | |||
| 175 | std::shared_ptr<NCA> NSP::GetNCA(u64 title_id, ContentRecordType type) const { | ||
| 176 | if (extracted) | ||
| 177 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | ||
| 178 | |||
| 179 | const auto title_id_iter = ncas.find(title_id); | ||
| 180 | if (title_id_iter == ncas.end()) | ||
| 181 | return nullptr; | ||
| 182 | |||
| 183 | const auto type_iter = title_id_iter->second.find(type); | ||
| 184 | if (type_iter == title_id_iter->second.end()) | ||
| 185 | return nullptr; | ||
| 186 | |||
| 187 | return type_iter->second; | ||
| 188 | } | ||
| 189 | |||
| 190 | VirtualFile NSP::GetNCAFile(u64 title_id, ContentRecordType type) const { | ||
| 191 | if (extracted) | ||
| 192 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | ||
| 193 | const auto nca = GetNCA(title_id, type); | ||
| 194 | if (nca != nullptr) | ||
| 195 | return nca->GetBaseFile(); | ||
| 196 | return nullptr; | ||
| 197 | } | ||
| 198 | |||
| 199 | std::vector<Core::Crypto::Key128> NSP::GetTitlekey() const { | ||
| 200 | if (extracted) | ||
| 201 | LOG_WARNING(Service_FS, "called on an NSP that is of type extracted."); | ||
| 202 | std::vector<Core::Crypto::Key128> out; | ||
| 203 | for (const auto& ticket_file : ticket_files) { | ||
| 204 | if (ticket_file == nullptr || | ||
| 205 | ticket_file->GetSize() < | ||
| 206 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) { | ||
| 207 | continue; | ||
| 208 | } | ||
| 209 | |||
| 210 | out.emplace_back(); | ||
| 211 | ticket_file->Read(out.back().data(), out.back().size(), | ||
| 212 | Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET); | ||
| 213 | } | ||
| 214 | return out; | ||
| 215 | } | ||
| 216 | |||
| 217 | std::vector<VirtualFile> NSP::GetFiles() const { | ||
| 218 | return pfs->GetFiles(); | ||
| 219 | } | ||
| 220 | |||
| 221 | std::vector<VirtualDir> NSP::GetSubdirectories() const { | ||
| 222 | return pfs->GetSubdirectories(); | ||
| 223 | } | ||
| 224 | |||
| 225 | std::string NSP::GetName() const { | ||
| 226 | return file->GetName(); | ||
| 227 | } | ||
| 228 | |||
| 229 | VirtualDir NSP::GetParentDirectory() const { | ||
| 230 | return file->GetContainingDirectory(); | ||
| 231 | } | ||
| 232 | |||
| 233 | bool NSP::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) { | ||
| 234 | return false; | ||
| 235 | } | ||
| 236 | } // 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 @@ | |||
| 1 | // Copyright 2018 yuzu emulator team | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include <array> | ||
| 8 | #include <map> | ||
| 9 | #include <vector> | ||
| 10 | #include "common/common_types.h" | ||
| 11 | #include "common/swap.h" | ||
| 12 | #include "core/file_sys/content_archive.h" | ||
| 13 | #include "core/file_sys/romfs_factory.h" | ||
| 14 | #include "core/file_sys/vfs.h" | ||
| 15 | #include "core/loader/loader.h" | ||
| 16 | |||
| 17 | namespace FileSys { | ||
| 18 | |||
| 19 | class PartitionFilesystem; | ||
| 20 | |||
| 21 | class NSP : public ReadOnlyVfsDirectory { | ||
| 22 | public: | ||
| 23 | explicit NSP(VirtualFile file); | ||
| 24 | ~NSP(); | ||
| 25 | |||
| 26 | Loader::ResultStatus GetStatus() const; | ||
| 27 | Loader::ResultStatus GetProgramStatus(u64 title_id) const; | ||
| 28 | // Should only be used when one title id can be assured. | ||
| 29 | u64 GetFirstTitleID() const; | ||
| 30 | u64 GetProgramTitleID() const; | ||
| 31 | std::vector<u64> GetTitleIDs() const; | ||
| 32 | |||
| 33 | bool IsExtractedType() const; | ||
| 34 | |||
| 35 | // Common (Can be safely called on both types) | ||
| 36 | VirtualFile GetRomFS() const; | ||
| 37 | VirtualDir GetExeFS() const; | ||
| 38 | |||
| 39 | // Type 0 Only (Collection of NCAs + Certificate + Ticket + Meta XML) | ||
| 40 | std::vector<std::shared_ptr<NCA>> GetNCAsCollapsed() const; | ||
| 41 | std::multimap<u64, std::shared_ptr<NCA>> GetNCAsByTitleID() const; | ||
| 42 | std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> GetNCAs() const; | ||
| 43 | std::shared_ptr<NCA> GetNCA(u64 title_id, ContentRecordType type) const; | ||
| 44 | VirtualFile GetNCAFile(u64 title_id, ContentRecordType type) const; | ||
| 45 | std::vector<Core::Crypto::Key128> GetTitlekey() const; | ||
| 46 | |||
| 47 | std::vector<VirtualFile> GetFiles() const override; | ||
| 48 | |||
| 49 | std::vector<VirtualDir> GetSubdirectories() const override; | ||
| 50 | |||
| 51 | std::string GetName() const override; | ||
| 52 | |||
| 53 | VirtualDir GetParentDirectory() const override; | ||
| 54 | |||
| 55 | protected: | ||
| 56 | bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override; | ||
| 57 | |||
| 58 | private: | ||
| 59 | VirtualFile file; | ||
| 60 | |||
| 61 | bool extracted; | ||
| 62 | Loader::ResultStatus status; | ||
| 63 | std::map<u64, Loader::ResultStatus> program_status; | ||
| 64 | |||
| 65 | std::shared_ptr<PartitionFilesystem> pfs; | ||
| 66 | // Map title id -> {map type -> NCA} | ||
| 67 | std::map<u64, std::map<ContentRecordType, std::shared_ptr<NCA>>> ncas; | ||
| 68 | std::vector<VirtualFile> ticket_files; | ||
| 69 | |||
| 70 | VirtualFile romfs; | ||
| 71 | VirtualDir exefs; | ||
| 72 | }; | ||
| 73 | } // namespace FileSys | ||