From 9aab7871222ca86bdf817cc6c96956b25aa76674 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 000000000..fa06897b7 --- /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 167bfddafadb843236c0fa683cf97eaffaa5ea1a 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/core/file_sys/nca_metadata.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 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 fa06897b7..118a0c287 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; }); -- cgit v1.2.3 From 22bdddd6f01e1976590e6df55fd3bcb29cb8aeef 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 ++-- 1 file changed, 2 insertions(+), 2 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 118a0c287..38b5eae80 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)) {} -- cgit v1.2.3 From 893447b6b0f5068f3cc2111b5f21c3cff68002e2 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 +++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 28 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 38b5eae80..234d70199 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); } -- cgit v1.2.3 From 35e4a47be0c4ef25f860d51851d2c02c02dff53d 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 ++++------- 1 file changed, 4 insertions(+), 7 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 234d70199..449244444 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); -- cgit v1.2.3