From 7fdfa63ce3283b2c47aff2be5a6abb011bcaedc7 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 9 Aug 2018 20:51:52 -0400 Subject: file_sys: Add support for parsing NCA metadata (CNMT) --- src/core/file_sys/nca_metadata.cpp | 125 +++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/core/file_sys/nca_metadata.cpp (limited to 'src/core/file_sys/nca_metadata.cpp') diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp new file mode 100644 index 0000000000..fa06897b7a --- /dev/null +++ b/src/core/file_sys/nca_metadata.cpp @@ -0,0 +1,125 @@ +// Copyright 2018 yuzu emulator team +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include "common/common_funcs.h" +#include "common/swap.h" +#include "content_archive.h" +#include "core/file_sys/nca_metadata.h" + +namespace FileSys { + +CNMT::CNMT(VirtualFile file_) : file(std::move(file_)), header(std::make_unique()) { + if (file->ReadObject(header.get()) != sizeof(CNMTHeader)) + return; + + // If type is {Application, Update, AOC} has opt-header. + if (static_cast(header->type) >= 0x80 && static_cast(header->type) <= 0x82) { + opt_header = std::make_unique(); + if (file->ReadObject(opt_header.get(), sizeof(CNMTHeader)) != sizeof(OptionalHeader)) { + opt_header = nullptr; + } + } + + for (u16 i = 0; i < header->number_content_entries; ++i) { + auto& next = content_records.emplace_back(ContentRecord{}); + if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(ContentRecord) + + header->table_offset) != sizeof(ContentRecord)) { + content_records.erase(content_records.end() - 1); + } + } + + for (u16 i = 0; i < header->number_meta_entries; ++i) { + auto& next = meta_records.emplace_back(MetaRecord{}); + if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(MetaRecord) + + header->table_offset) != sizeof(MetaRecord)) { + meta_records.erase(meta_records.end() - 1); + } + } +} + +CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector content_records, + std::vector meta_records) + : file(nullptr), header(std::make_unique(std::move(header))), + opt_header(std::make_unique(std::move(opt_header))), + content_records(std::move(content_records)), meta_records(std::move(meta_records)) {} + +u64 CNMT::GetTitleID() const { + return header->title_id; +} + +u32 CNMT::GetTitleVersion() const { + return header->title_version; +} + +TitleType CNMT::GetType() const { + return header->type; +} + +const std::vector& CNMT::GetContentRecords() const { + return content_records; +} + +const std::vector& CNMT::GetMetaRecords() const { + return meta_records; +} + +bool CNMT::UnionRecords(const CNMT& other) { + bool change = false; + for (const auto& rec : other.content_records) { + const auto iter = std::find_if( + content_records.begin(), content_records.end(), + [rec](const ContentRecord& r) { return r.nca_id == rec.nca_id && r.type == rec.type; }); + if (iter == content_records.end()) { + content_records.emplace_back(rec); + ++header->number_content_entries; + change = true; + } + } + for (const auto& rec : other.meta_records) { + const auto iter = + std::find_if(meta_records.begin(), meta_records.end(), [rec](const MetaRecord& r) { + return r.title_id == rec.title_id && r.title_version == rec.title_version && + r.type == rec.type; + }); + if (iter == meta_records.end()) { + meta_records.emplace_back(rec); + ++header->number_meta_entries; + change = true; + } + } + return change; +} + +std::vector CNMT::Serialize() const { + if (header == nullptr) + return {}; + std::vector out(sizeof(CNMTHeader)); + out.reserve(0x100); // Avoid resizing -- average size. + memcpy(out.data(), header.get(), sizeof(CNMTHeader)); + if (opt_header != nullptr) { + out.resize(out.size() + sizeof(OptionalHeader)); + memcpy(out.data() + sizeof(CNMTHeader), opt_header.get(), sizeof(OptionalHeader)); + } + + auto offset = header->table_offset; + + const auto dead_zone = offset + sizeof(CNMTHeader) - out.size(); + if (dead_zone > 0) + out.resize(offset + sizeof(CNMTHeader)); + + for (const auto& rec : content_records) { + out.resize(out.size() + sizeof(ContentRecord)); + memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(ContentRecord)); + offset += sizeof(ContentRecord); + } + + for (const auto& rec : meta_records) { + out.resize(out.size() + sizeof(MetaRecord)); + memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(MetaRecord)); + offset += sizeof(MetaRecord); + } + + return out; +} +} // namespace FileSys -- cgit v1.2.3 From b0531d6ea09600a3313e5cc6e73d58e06fd20f97 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Thu, 9 Aug 2018 23:10:32 -0400 Subject: file_sys: Comply to style guidelines --- src/common/hex_util.h | 2 ++ src/core/file_sys/nca_metadata.cpp | 10 +++++---- src/core/file_sys/nca_metadata.h | 4 ++++ src/core/file_sys/registered_cache.cpp | 24 ++++++++++---------- src/core/file_sys/registered_cache.h | 13 ++++++----- src/core/file_sys/vfs_concat.cpp | 8 +++---- src/core/file_sys/vfs_concat.h | 6 ++--- src/yuzu/main.cpp | 40 +++++++++++++++++----------------- 8 files changed, 60 insertions(+), 47 deletions(-) (limited to 'src/core/file_sys/nca_metadata.cpp') diff --git a/src/common/hex_util.h b/src/common/hex_util.h index f16c58aab1..13d5860153 100644 --- a/src/common/hex_util.h +++ b/src/common/hex_util.h @@ -5,6 +5,8 @@ #pragma once #include +#include +#include #include #include "common/common_types.h" diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index fa06897b7a..118a0c2874 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp @@ -2,6 +2,7 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include #include "common/common_funcs.h" #include "common/swap.h" #include "content_archive.h" @@ -67,9 +68,10 @@ const std::vector& CNMT::GetMetaRecords() const { bool CNMT::UnionRecords(const CNMT& other) { bool change = false; for (const auto& rec : other.content_records) { - const auto iter = std::find_if( - content_records.begin(), content_records.end(), - [rec](const ContentRecord& r) { return r.nca_id == rec.nca_id && r.type == rec.type; }); + const auto iter = std::find_if(content_records.begin(), content_records.end(), + [&rec](const ContentRecord& r) { + return r.nca_id == rec.nca_id && r.type == rec.type; + }); if (iter == content_records.end()) { content_records.emplace_back(rec); ++header->number_content_entries; @@ -78,7 +80,7 @@ bool CNMT::UnionRecords(const CNMT& other) { } for (const auto& rec : other.meta_records) { const auto iter = - std::find_if(meta_records.begin(), meta_records.end(), [rec](const MetaRecord& r) { + std::find_if(meta_records.begin(), meta_records.end(), [&rec](const MetaRecord& r) { return r.title_id == rec.title_id && r.title_version == rec.title_version && r.type == rec.type; }); diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index 7b0725f36c..6cd919e54d 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -4,7 +4,11 @@ #pragma once +#include #include +#include +#include "common/common_types.h" +#include "common/swap.h" #include "core/file_sys/vfs.h" namespace FileSys { diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index 766fef2541..3e77061710 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -23,13 +23,13 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) } static bool FollowsTwoDigitDirFormat(std::string_view name) { - const static std::regex two_digit_regex( + static const std::regex two_digit_regex( "000000[0123456789abcdefABCDEF][0123456789abcdefABCDEF]"); return std::regex_match(name.begin(), name.end(), two_digit_regex); } static bool FollowsNcaIdFormat(std::string_view name) { - const static std::regex nca_id_regex("[0123456789abcdefABCDEF]+.nca"); + static const std::regex nca_id_regex("[0123456789abcdefABCDEF]+.nca"); return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex); } @@ -56,7 +56,7 @@ static std::string GetCNMTName(TitleType type, u64 title_id) { "" ///< Currently unknown 'DeltaTitle' }; - size_t index = static_cast(type); + auto index = static_cast(type); if (index >= 0x80) index -= 0x80; return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); @@ -90,15 +90,15 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, VirtualFile file = nullptr; const auto files = nca_dir->GetFiles(); - if (files.size() == 1 && files[0]->GetName() == "00") + if (files.size() == 1 && files[0]->GetName() == "00") { file = files[0]; - else { + } else { std::vector concat; for (u8 i = 0; i < 0x10; ++i) { auto next = nca_dir->GetFile(fmt::format("{:02X}", i)); - if (next != nullptr) + if (next != nullptr) { concat.push_back(std::move(next)); - else { + } else { next = nca_dir->GetFile(fmt::format("{:02x}", i)); if (next != nullptr) concat.push_back(std::move(next)); @@ -146,7 +146,8 @@ boost::optional RegisteredCache::GetNcaIDFromMetadata(u64 title_id, return boost::make_optional(iter->nca_id); } -void RegisteredCache::AccumulateFiles(std::vector& ids) const { +std::vector RegisteredCache::AccumulateFiles() const { + std::vector ids; for (const auto& d2_dir : dir->GetSubdirectories()) { if (FollowsNcaIdFormat(d2_dir->GetName())) { ids.push_back(HexStringToArray<0x10, true>(d2_dir->GetName().substr(0, 0x20))); @@ -175,6 +176,7 @@ void RegisteredCache::AccumulateFiles(std::vector& ids) const { if (FollowsNcaIdFormat(d2_file->GetName())) ids.push_back(HexStringToArray<0x10, true>(d2_file->GetName().substr(0, 0x20))); } + return ids; } void RegisteredCache::ProcessFiles(const std::vector& ids) { @@ -185,8 +187,9 @@ void RegisteredCache::ProcessFiles(const std::vector& ids) { continue; const auto nca = std::make_shared(parser(file, id)); if (nca->GetStatus() != Loader::ResultStatus::Success || - nca->GetType() != NCAContentType::Meta) + nca->GetType() != NCAContentType::Meta) { continue; + } const auto section0 = nca->GetSubdirectories()[0]; @@ -218,8 +221,7 @@ void RegisteredCache::AccumulateYuzuMeta() { void RegisteredCache::Refresh() { if (dir == nullptr) return; - std::vector ids; - AccumulateFiles(ids); + const auto ids = AccumulateFiles(); ProcessFiles(ids); AccumulateYuzuMeta(); } diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h index ba2e3403f1..baaed02dda 100644 --- a/src/core/file_sys/registered_cache.h +++ b/src/core/file_sys/registered_cache.h @@ -5,14 +5,17 @@ #pragma once #include +#include #include #include #include +#include #include #include "common/common_funcs.h" +#include "common/common_types.h" #include "content_archive.h" +#include "core/file_sys/nca_metadata.h" #include "core/file_sys/vfs.h" -#include "nca_metadata.h" namespace FileSys { class XCI; @@ -49,9 +52,9 @@ public: // Parsing function defines the conversion from raw file to NCA. If there are other steps // besides creating the NCA from the file (e.g. NAX0 on SD Card), that should go in a custom // parsing function. - RegisteredCache(VirtualDir dir, - RegisteredCacheParsingFunction parsing_function = - [](const VirtualFile& file, const NcaID& id) { return file; }); + explicit RegisteredCache(VirtualDir dir, + RegisteredCacheParsingFunction parsing_function = + [](const VirtualFile& file, const NcaID& id) { return file; }); void Refresh(); @@ -86,7 +89,7 @@ private: void IterateAllMetadata(std::vector& out, std::function proc, std::function filter) const; - void AccumulateFiles(std::vector& ids) const; + std::vector AccumulateFiles() const; void ProcessFiles(const std::vector& ids); void AccumulateYuzuMeta(); boost::optional GetNcaIDFromMetadata(u64 title_id, ContentRecordType type) const; diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp index 1d439e0a4f..88a9a52593 100644 --- a/src/core/file_sys/vfs_concat.cpp +++ b/src/core/file_sys/vfs_concat.cpp @@ -9,17 +9,17 @@ namespace FileSys { -VirtualFile ConcatenateFiles(std::vector files, std::string_view name) { +VirtualFile ConcatenateFiles(std::vector files, std::string name) { if (files.empty()) return nullptr; if (files.size() == 1) return files[0]; - return std::shared_ptr(new ConcatenatedVfsFile(std::move(files), name)); + return std::shared_ptr(new ConcatenatedVfsFile(std::move(files), std::move(name))); } -ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector files_, std::string_view name) - : name(name) { +ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector files_, std::string name) + : name(std::move(name)) { size_t next_offset = 0; for (const auto& file : files_) { files[next_offset] = file; diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h index d319c5786e..686d325152 100644 --- a/src/core/file_sys/vfs_concat.h +++ b/src/core/file_sys/vfs_concat.h @@ -12,14 +12,14 @@ namespace FileSys { // Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases. -VirtualFile ConcatenateFiles(std::vector files, std::string_view name = ""); +VirtualFile ConcatenateFiles(std::vector files, std::string name = ""); // Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently // read-only. class ConcatenatedVfsFile : public VfsFile { - friend VirtualFile ConcatenateFiles(std::vector files, std::string_view name); + friend VirtualFile ConcatenateFiles(std::vector files, std::string name); - ConcatenatedVfsFile(std::vector files, std::string_view name); + ConcatenatedVfsFile(std::vector files, std::string name); public: std::string GetName() const override; diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp index c481914868..fd237df43d 100644 --- a/src/yuzu/main.cpp +++ b/src/yuzu/main.cpp @@ -619,7 +619,7 @@ void GMainWindow::OnMenuLoadFolder() { } void GMainWindow::OnMenuInstallToNAND() { - const static QString file_filter = + const QString file_filter = tr("Installable Switch File (*.nca *.xci);;Nintendo Content Archive (*.nca);;NX Cartridge " "Image (*.xci)"); QString filename = QFileDialog::getOpenFileName(this, tr("Install File"), @@ -629,36 +629,36 @@ void GMainWindow::OnMenuInstallToNAND() { const auto xci = std::make_shared( vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); if (xci->GetStatus() != Loader::ResultStatus::Success) { - QMessageBox::critical( + QMessageBox::warning( this, tr("Failed to Install XCI"), tr("The XCI file you provided is invalid. Please double-check your encryption " "keys and the file and try again.")); return; } - if (!Service::FileSystem::GetUserNANDContents()->InstallEntry(xci)) { - QMessageBox::critical( - this, tr("Failed to Install XCI"), - tr("There was an error while attempting to install the provided XCI file. It " - "could have an incorrect format or be missing a metadata entry. Please " - "double-check your file and try again.")); - } else { + if (Service::FileSystem::GetUserNANDContents()->InstallEntry(xci)) { QMessageBox::information(this, tr("Successfully Installed XCI"), tr("The file was successfully installed.")); game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + } else { + QMessageBox::warning( + this, tr("Failed to Install XCI"), + tr("There was an error while attempting to install the provided XCI file. It " + "could have an incorrect format or be missing a metadata entry. Please " + "double-check your file and try again.")); } } else { const auto nca = std::make_shared( vfs->OpenFile(filename.toStdString(), FileSys::Mode::Read)); if (nca->GetStatus() != Loader::ResultStatus::Success) { - QMessageBox::critical( + QMessageBox::warning( this, tr("Failed to Install NCA"), tr("The NCA file you provided is invalid. Please double-check your encryption " "keys and the file and try again.")); return; } - const static QStringList tt_options{"System Application", + static const QStringList tt_options{"System Application", "System Archive", "System Application Update", "Firmware Package (Type A)", @@ -676,26 +676,26 @@ void GMainWindow::OnMenuInstallToNAND() { auto index = tt_options.indexOf(item); if (!ok || index == -1) { - QMessageBox::critical(this, tr("Failed to Install NCA"), - tr("The title type you selected for the NCA is invalid.")); + QMessageBox::warning(this, tr("Failed to Install NCA"), + tr("The title type you selected for the NCA is invalid.")); return; } if (index >= 5) index += 0x80; - if (!Service::FileSystem::GetUserNANDContents()->InstallEntry( + if (Service::FileSystem::GetUserNANDContents()->InstallEntry( nca, static_cast(index))) { - QMessageBox::critical(this, tr("Failed to Install NCA"), - tr("There was an error while attempting to install the " - "provided NCA file. An error might have occured creating " - "the metadata file or parsing the NCA. Please " - "double-check your file and try again.")); - } else { QMessageBox::information(this, tr("Successfully Installed NCA"), tr("The file was successfully installed.")); game_list->PopulateAsync(UISettings::values.gamedir, UISettings::values.gamedir_deepscan); + } else { + QMessageBox::warning(this, tr("Failed to Install NCA"), + tr("There was an error while attempting to install the " + "provided NCA file. An error might have occured creating " + "the metadata file or parsing the NCA. Please " + "double-check your file and try again.")); } } } -- cgit v1.2.3 From 50ff8ec3647a25ba860be224367a2f54e91e5dfd Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Fri, 10 Aug 2018 20:47:51 -0400 Subject: nca_metadata: Remove unnecessary reference to base file --- src/core/file_sys/nca_metadata.cpp | 4 ++-- src/core/file_sys/nca_metadata.h | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'src/core/file_sys/nca_metadata.cpp') diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index 118a0c2874..38b5eae80e 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp @@ -10,7 +10,7 @@ namespace FileSys { -CNMT::CNMT(VirtualFile file_) : file(std::move(file_)), header(std::make_unique()) { +CNMT::CNMT(VirtualFile file) : header(std::make_unique()) { if (file->ReadObject(header.get()) != sizeof(CNMTHeader)) return; @@ -41,7 +41,7 @@ CNMT::CNMT(VirtualFile file_) : file(std::move(file_)), header(std::make_unique< CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector content_records, std::vector meta_records) - : file(nullptr), header(std::make_unique(std::move(header))), + : header(std::make_unique(std::move(header))), opt_header(std::make_unique(std::move(opt_header))), content_records(std::move(content_records)), meta_records(std::move(meta_records)) {} diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index 6cd919e54d..c1ca9b061a 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -96,7 +96,6 @@ public: std::vector Serialize() const; private: - VirtualFile file; std::unique_ptr header; std::unique_ptr opt_header; std::vector content_records; -- cgit v1.2.3 From 83db3b452a5ad9eee6d966a40f82a3ff2c7468ad Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sat, 11 Aug 2018 15:39:09 -0400 Subject: registration: Update documentation and style --- src/core/file_sys/nca_metadata.cpp | 63 +++++++++++++++++++--------------- src/core/file_sys/nca_metadata.h | 7 ++-- src/core/file_sys/registered_cache.cpp | 23 +++++++++---- src/core/file_sys/vfs_concat.cpp | 1 + src/core/file_sys/vfs_real.cpp | 17 ++++++--- 5 files changed, 69 insertions(+), 42 deletions(-) (limited to 'src/core/file_sys/nca_metadata.cpp') diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index 38b5eae80e..234d701990 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp @@ -4,36 +4,44 @@ #include #include "common/common_funcs.h" +#include "common/logging/log.h" #include "common/swap.h" #include "content_archive.h" #include "core/file_sys/nca_metadata.h" namespace FileSys { -CNMT::CNMT(VirtualFile file) : header(std::make_unique()) { - if (file->ReadObject(header.get()) != sizeof(CNMTHeader)) +bool operator>=(TitleType lhs, TitleType rhs) { + return static_cast(lhs) >= static_cast(rhs); +} + +bool operator<=(TitleType lhs, TitleType rhs) { + return static_cast(lhs) <= static_cast(rhs); +} + +CNMT::CNMT(VirtualFile file) { + if (file->ReadObject(&header) != sizeof(CNMTHeader)) return; // If type is {Application, Update, AOC} has opt-header. - if (static_cast(header->type) >= 0x80 && static_cast(header->type) <= 0x82) { - opt_header = std::make_unique(); - if (file->ReadObject(opt_header.get(), sizeof(CNMTHeader)) != sizeof(OptionalHeader)) { - opt_header = nullptr; + if (header.type >= TitleType::Application && header.type <= TitleType::AOC) { + if (file->ReadObject(&opt_header, sizeof(CNMTHeader)) != sizeof(OptionalHeader)) { + LOG_WARNING(Loader, "Failed to read optional header."); } } - for (u16 i = 0; i < header->number_content_entries; ++i) { + for (u16 i = 0; i < header.number_content_entries; ++i) { auto& next = content_records.emplace_back(ContentRecord{}); if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(ContentRecord) + - header->table_offset) != sizeof(ContentRecord)) { + header.table_offset) != sizeof(ContentRecord)) { content_records.erase(content_records.end() - 1); } } - for (u16 i = 0; i < header->number_meta_entries; ++i) { + for (u16 i = 0; i < header.number_meta_entries; ++i) { auto& next = meta_records.emplace_back(MetaRecord{}); if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(MetaRecord) + - header->table_offset) != sizeof(MetaRecord)) { + header.table_offset) != sizeof(MetaRecord)) { meta_records.erase(meta_records.end() - 1); } } @@ -41,20 +49,19 @@ CNMT::CNMT(VirtualFile file) : header(std::make_unique()) { CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector content_records, std::vector meta_records) - : header(std::make_unique(std::move(header))), - opt_header(std::make_unique(std::move(opt_header))), + : header(std::move(header)), opt_header(std::move(opt_header)), content_records(std::move(content_records)), meta_records(std::move(meta_records)) {} u64 CNMT::GetTitleID() const { - return header->title_id; + return header.title_id; } u32 CNMT::GetTitleVersion() const { - return header->title_version; + return header.title_version; } TitleType CNMT::GetType() const { - return header->type; + return header.type; } const std::vector& CNMT::GetContentRecords() const { @@ -74,7 +81,7 @@ bool CNMT::UnionRecords(const CNMT& other) { }); if (iter == content_records.end()) { content_records.emplace_back(rec); - ++header->number_content_entries; + ++header.number_content_entries; change = true; } } @@ -86,7 +93,7 @@ bool CNMT::UnionRecords(const CNMT& other) { }); if (iter == meta_records.end()) { meta_records.emplace_back(rec); - ++header->number_meta_entries; + ++header.number_meta_entries; change = true; } } @@ -94,30 +101,30 @@ bool CNMT::UnionRecords(const CNMT& other) { } std::vector CNMT::Serialize() const { - if (header == nullptr) - return {}; - std::vector out(sizeof(CNMTHeader)); - out.reserve(0x100); // Avoid resizing -- average size. - memcpy(out.data(), header.get(), sizeof(CNMTHeader)); - if (opt_header != nullptr) { - out.resize(out.size() + sizeof(OptionalHeader)); - memcpy(out.data() + sizeof(CNMTHeader), opt_header.get(), sizeof(OptionalHeader)); + const bool has_opt_header = + header.type >= TitleType::Application && header.type <= TitleType::AOC; + std::vector out(sizeof(CNMTHeader) + (has_opt_header ? sizeof(OptionalHeader) : 0)); + memcpy(out.data(), &header, sizeof(CNMTHeader)); + + // Optional Header + if (has_opt_header) { + memcpy(out.data() + sizeof(CNMTHeader), &opt_header, sizeof(OptionalHeader)); } - auto offset = header->table_offset; + auto offset = header.table_offset; const auto dead_zone = offset + sizeof(CNMTHeader) - out.size(); if (dead_zone > 0) out.resize(offset + sizeof(CNMTHeader)); + out.resize(out.size() + content_records.size() * sizeof(ContentRecord)); for (const auto& rec : content_records) { - out.resize(out.size() + sizeof(ContentRecord)); memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(ContentRecord)); offset += sizeof(ContentRecord); } + out.resize(out.size() + content_records.size() * sizeof(MetaRecord)); for (const auto& rec : meta_records) { - out.resize(out.size() + sizeof(MetaRecord)); memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(MetaRecord)); offset += sizeof(MetaRecord); } diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h index c1ca9b061a..88e66d4da1 100644 --- a/src/core/file_sys/nca_metadata.h +++ b/src/core/file_sys/nca_metadata.h @@ -29,6 +29,9 @@ enum class TitleType : u8 { DeltaTitle = 0x83, }; +bool operator>=(TitleType lhs, TitleType rhs); +bool operator<=(TitleType lhs, TitleType rhs); + enum class ContentRecordType : u8 { Meta = 0, Program = 1, @@ -96,8 +99,8 @@ public: std::vector Serialize() const; private: - std::unique_ptr header; - std::unique_ptr opt_header; + CNMTHeader header; + OptionalHeader opt_header; std::vector content_records; std::vector meta_records; diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index aaadb7463f..20fec2391c 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -23,13 +23,14 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs) } static bool FollowsTwoDigitDirFormat(std::string_view name) { - static const std::regex two_digit_regex( - "000000[0123456789abcdefABCDEF][0123456789abcdefABCDEF]"); + static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript | + std::regex_constants::icase); return std::regex_match(name.begin(), name.end(), two_digit_regex); } static bool FollowsNcaIdFormat(std::string_view name) { - static const std::regex nca_id_regex("[0123456789abcdefABCDEF]+.nca"); + static const std::regex nca_id_regex("[0-9A-F]{32}.nca", std::regex_constants::ECMAScript | + std::regex_constants::icase); return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex); } @@ -57,8 +58,9 @@ static std::string GetCNMTName(TitleType type, u64 title_id) { }; auto index = static_cast(type); - if (index >= 0x80) - index -= 0x80; + // If the index is after the jump in TitleType, subtract it out. + if (index >= static_cast(TitleType::Application)) + index -= static_cast(TitleType::Application); return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); } @@ -120,9 +122,15 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, VirtualFile RegisteredCache::GetFileAtID(NcaID id) const { VirtualFile file; + // Try all four modes of file storage: + // (bit 1 = uppercase/lower, bit 0 = within a two-digit dir) + // 00: /000000**/{:032X}.nca + // 01: /{:032X}.nca + // 10: /000000**/{:032x}.nca + // 11: /{:032x}.nca for (u8 i = 0; i < 4; ++i) { - file = OpenFileOrDirectoryConcat( - dir, GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0)); + const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0); + file = OpenFileOrDirectoryConcat(dir, path); if (file != nullptr) return file; } @@ -420,6 +428,7 @@ bool RegisteredCache::RawInstallNCA(std::shared_ptr nca, const VfsCopyFunct } bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) { + // Reasoning behind this method can be found in the comment for InstallEntry, NCA overload. const auto dir = this->dir->CreateDirectoryRelative("yuzu_meta"); const auto filename = GetCNMTName(cnmt.GetType(), cnmt.GetTitleID()); if (dir->GetFile(filename) == nullptr) { diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp index 88a9a52593..e6bf586a30 100644 --- a/src/core/file_sys/vfs_concat.cpp +++ b/src/core/file_sys/vfs_concat.cpp @@ -68,6 +68,7 @@ size_t ConcatenatedVfsFile::Read(u8* data, size_t length, size_t offset) const { } } + // Check if the entry should be the last one. The loop above will make it end(). if (entry == files.end() && offset < files.rbegin()->first + files.rbegin()->second->GetSize()) --entry; diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index fa682153cf..33ab35fcde 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -83,10 +83,12 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); - if (!FileUtil::Exists(path) && - !FileUtil::CreateFullPath( - FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash)) && - !FileUtil::CreateEmptyFile(path)) + if (!FileUtil::Exists(path)) + return nullptr; + if (!FileUtil::CreateFullPath( + FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash))) + return nullptr; + if (!FileUtil::CreateEmptyFile(path)) return nullptr; return OpenFile(path, perms); } @@ -143,7 +145,12 @@ VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); - if (!FileUtil::Exists(path) && !FileUtil::CreateDir(path)) + if (!FileUtil::Exists(path)) + return nullptr; + if (!FileUtil::CreateFullPath( + FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash))) + return nullptr; + if (!FileUtil::CreateDir(path)) return nullptr; // Cannot use make_shared as RealVfsDirectory constructor is private return std::shared_ptr(new RealVfsDirectory(*this, path, perms)); -- cgit v1.2.3 From 3781654c6d5f0932351f020b884ac6ff32cbc7e9 Mon Sep 17 00:00:00 2001 From: Zach Hilman Date: Sun, 12 Aug 2018 15:55:44 -0400 Subject: registration: Various style and documentation improvements Fix logic in RealVfsFilesystem Create methods Remove magic numbers Fix regex errors --- src/core/file_sys/nca_metadata.cpp | 11 ++++------- src/core/file_sys/registered_cache.cpp | 13 ++++++++----- src/core/file_sys/vfs_real.cpp | 16 ++++++++++------ 3 files changed, 22 insertions(+), 18 deletions(-) (limited to 'src/core/file_sys/nca_metadata.cpp') diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp index 234d701990..4492444449 100644 --- a/src/core/file_sys/nca_metadata.cpp +++ b/src/core/file_sys/nca_metadata.cpp @@ -103,7 +103,10 @@ bool CNMT::UnionRecords(const CNMT& other) { std::vector CNMT::Serialize() const { const bool has_opt_header = header.type >= TitleType::Application && header.type <= TitleType::AOC; - std::vector out(sizeof(CNMTHeader) + (has_opt_header ? sizeof(OptionalHeader) : 0)); + const auto dead_zone = header.table_offset + sizeof(CNMTHeader); + std::vector out( + std::max(sizeof(CNMTHeader) + (has_opt_header ? sizeof(OptionalHeader) : 0), dead_zone) + + content_records.size() * sizeof(ContentRecord) + meta_records.size() * sizeof(MetaRecord)); memcpy(out.data(), &header, sizeof(CNMTHeader)); // Optional Header @@ -113,17 +116,11 @@ std::vector CNMT::Serialize() const { auto offset = header.table_offset; - const auto dead_zone = offset + sizeof(CNMTHeader) - out.size(); - if (dead_zone > 0) - out.resize(offset + sizeof(CNMTHeader)); - - out.resize(out.size() + content_records.size() * sizeof(ContentRecord)); for (const auto& rec : content_records) { memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(ContentRecord)); offset += sizeof(ContentRecord); } - out.resize(out.size() + content_records.size() * sizeof(MetaRecord)); for (const auto& rec : meta_records) { memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(MetaRecord)); offset += sizeof(MetaRecord); diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index e916d56109..a5e935f64f 100644 --- a/src/core/file_sys/registered_cache.cpp +++ b/src/core/file_sys/registered_cache.cpp @@ -29,8 +29,8 @@ static bool FollowsTwoDigitDirFormat(std::string_view name) { } static bool FollowsNcaIdFormat(std::string_view name) { - static const std::regex nca_id_regex("[0-9A-F]{32}.nca", std::regex_constants::ECMAScript | - std::regex_constants::icase); + static const std::regex nca_id_regex("[0-9A-F]{32}\\.nca", std::regex_constants::ECMAScript | + std::regex_constants::icase); return name.size() == 36 && std::regex_match(name.begin(), name.end(), nca_id_regex); } @@ -59,8 +59,10 @@ static std::string GetCNMTName(TitleType type, u64 title_id) { auto index = static_cast(type); // If the index is after the jump in TitleType, subtract it out. - if (index >= static_cast(TitleType::Application)) - index -= 0x7B; + if (index >= static_cast(TitleType::Application)) { + index -= static_cast(TitleType::Application) - + static_cast(TitleType::FirmwarePackageB); + } return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id); } @@ -96,7 +98,8 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir, file = files[0]; } else { std::vector concat; - for (u8 i = 0; i < 0x10; ++i) { + // Since the files are a two-digit hex number, max is FF. + for (size_t i = 0; i < 0x100; ++i) { auto next = nca_dir->GetFile(fmt::format("{:02X}", i)); if (next != nullptr) { concat.push_back(std::move(next)); diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp index 02cdb039ab..0afe515f07 100644 --- a/src/core/file_sys/vfs_real.cpp +++ b/src/core/file_sys/vfs_real.cpp @@ -84,9 +84,11 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) { VirtualFile RealVfsFilesystem::CreateFile(std::string_view path_, Mode perms) { const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash); - if (!FileUtil::Exists(path) && !FileUtil::CreateFullPath(path_fwd) && - !FileUtil::CreateEmptyFile(path)) - return nullptr; + if (!FileUtil::Exists(path)) { + FileUtil::CreateFullPath(path_fwd); + if (!FileUtil::CreateEmptyFile(path)) + return nullptr; + } return OpenFile(path, perms); } @@ -143,9 +145,11 @@ VirtualDir RealVfsFilesystem::OpenDirectory(std::string_view path_, Mode perms) VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) { const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault); const auto path_fwd = FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash); - if (!FileUtil::Exists(path) && !FileUtil::CreateFullPath(path_fwd) && - !FileUtil::CreateEmptyFile(path)) - return nullptr; + if (!FileUtil::Exists(path)) { + FileUtil::CreateFullPath(path_fwd); + if (!FileUtil::CreateDir(path)) + return nullptr; + } // Cannot use make_shared as RealVfsDirectory constructor is private return std::shared_ptr(new RealVfsDirectory(*this, path, perms)); } -- cgit v1.2.3