diff options
-rw-r--r-- | src/core/file_sys/nca_metadata.cpp | 63 | ||||
-rw-r--r-- | src/core/file_sys/nca_metadata.h | 7 | ||||
-rw-r--r-- | src/core/file_sys/registered_cache.cpp | 23 | ||||
-rw-r--r-- | src/core/file_sys/vfs_concat.cpp | 1 | ||||
-rw-r--r-- | src/core/file_sys/vfs_real.cpp | 17 |
5 files changed, 69 insertions, 42 deletions
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 <cstring> #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<CNMTHeader>()) { - if (file->ReadObject(header.get()) != sizeof(CNMTHeader)) +bool operator>=(TitleType lhs, TitleType rhs) { + return static_cast<size_t>(lhs) >= static_cast<size_t>(rhs); +} + +bool operator<=(TitleType lhs, TitleType rhs) { + return static_cast<size_t>(lhs) <= static_cast<size_t>(rhs); +} + +CNMT::CNMT(VirtualFile file) { + if (file->ReadObject(&header) != sizeof(CNMTHeader)) return; // If type is {Application, Update, AOC} has opt-header. - if (static_cast<u8>(header->type) >= 0x80 && static_cast<u8>(header->type) <= 0x82) { - opt_header = std::make_unique<OptionalHeader>(); - 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<CNMTHeader>()) { CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records, std::vector<MetaRecord> meta_records) - : header(std::make_unique<CNMTHeader>(std::move(header))), - opt_header(std::make_unique<OptionalHeader>(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<ContentRecord>& 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<u8> CNMT::Serialize() const { - if (header == nullptr) - return {}; - std::vector<u8> 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<u8> 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 c1ca9b061..88e66d4da 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<u8> Serialize() const; private: - std::unique_ptr<CNMTHeader> header; - std::unique_ptr<OptionalHeader> opt_header; + CNMTHeader header; + OptionalHeader opt_header; std::vector<ContentRecord> content_records; std::vector<MetaRecord> meta_records; diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp index aaadb7463..20fec2391 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<size_t>(type); - if (index >= 0x80) - index -= 0x80; + // If the index is after the jump in TitleType, subtract it out. + if (index >= static_cast<size_t>(TitleType::Application)) + index -= static_cast<size_t>(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> 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 88a9a5259..e6bf586a3 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 fa682153c..33ab35fcd 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<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms)); |