registration: Update documentation and style
This commit is contained in:
parent
22bdddd6f0
commit
893447b6b0
@ -4,36 +4,44 @@
|
|||||||
|
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include "common/common_funcs.h"
|
#include "common/common_funcs.h"
|
||||||
|
#include "common/logging/log.h"
|
||||||
#include "common/swap.h"
|
#include "common/swap.h"
|
||||||
#include "content_archive.h"
|
#include "content_archive.h"
|
||||||
#include "core/file_sys/nca_metadata.h"
|
#include "core/file_sys/nca_metadata.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
CNMT::CNMT(VirtualFile file) : header(std::make_unique<CNMTHeader>()) {
|
bool operator>=(TitleType lhs, TitleType rhs) {
|
||||||
if (file->ReadObject(header.get()) != sizeof(CNMTHeader))
|
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;
|
return;
|
||||||
|
|
||||||
// If type is {Application, Update, AOC} has opt-header.
|
// If type is {Application, Update, AOC} has opt-header.
|
||||||
if (static_cast<u8>(header->type) >= 0x80 && static_cast<u8>(header->type) <= 0x82) {
|
if (header.type >= TitleType::Application && header.type <= TitleType::AOC) {
|
||||||
opt_header = std::make_unique<OptionalHeader>();
|
if (file->ReadObject(&opt_header, sizeof(CNMTHeader)) != sizeof(OptionalHeader)) {
|
||||||
if (file->ReadObject(opt_header.get(), sizeof(CNMTHeader)) != sizeof(OptionalHeader)) {
|
LOG_WARNING(Loader, "Failed to read optional header.");
|
||||||
opt_header = nullptr;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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{});
|
auto& next = content_records.emplace_back(ContentRecord{});
|
||||||
if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(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);
|
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{});
|
auto& next = meta_records.emplace_back(MetaRecord{});
|
||||||
if (file->ReadObject(&next, sizeof(CNMTHeader) + i * sizeof(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);
|
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,
|
CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
|
||||||
std::vector<MetaRecord> meta_records)
|
std::vector<MetaRecord> meta_records)
|
||||||
: header(std::make_unique<CNMTHeader>(std::move(header))),
|
: header(std::move(header)), opt_header(std::move(opt_header)),
|
||||||
opt_header(std::make_unique<OptionalHeader>(std::move(opt_header))),
|
|
||||||
content_records(std::move(content_records)), meta_records(std::move(meta_records)) {}
|
content_records(std::move(content_records)), meta_records(std::move(meta_records)) {}
|
||||||
|
|
||||||
u64 CNMT::GetTitleID() const {
|
u64 CNMT::GetTitleID() const {
|
||||||
return header->title_id;
|
return header.title_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 CNMT::GetTitleVersion() const {
|
u32 CNMT::GetTitleVersion() const {
|
||||||
return header->title_version;
|
return header.title_version;
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleType CNMT::GetType() const {
|
TitleType CNMT::GetType() const {
|
||||||
return header->type;
|
return header.type;
|
||||||
}
|
}
|
||||||
|
|
||||||
const std::vector<ContentRecord>& CNMT::GetContentRecords() const {
|
const std::vector<ContentRecord>& CNMT::GetContentRecords() const {
|
||||||
@ -74,7 +81,7 @@ bool CNMT::UnionRecords(const CNMT& other) {
|
|||||||
});
|
});
|
||||||
if (iter == content_records.end()) {
|
if (iter == content_records.end()) {
|
||||||
content_records.emplace_back(rec);
|
content_records.emplace_back(rec);
|
||||||
++header->number_content_entries;
|
++header.number_content_entries;
|
||||||
change = true;
|
change = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,7 +93,7 @@ bool CNMT::UnionRecords(const CNMT& other) {
|
|||||||
});
|
});
|
||||||
if (iter == meta_records.end()) {
|
if (iter == meta_records.end()) {
|
||||||
meta_records.emplace_back(rec);
|
meta_records.emplace_back(rec);
|
||||||
++header->number_meta_entries;
|
++header.number_meta_entries;
|
||||||
change = true;
|
change = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -94,30 +101,30 @@ bool CNMT::UnionRecords(const CNMT& other) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> CNMT::Serialize() const {
|
std::vector<u8> CNMT::Serialize() const {
|
||||||
if (header == nullptr)
|
const bool has_opt_header =
|
||||||
return {};
|
header.type >= TitleType::Application && header.type <= TitleType::AOC;
|
||||||
std::vector<u8> out(sizeof(CNMTHeader));
|
std::vector<u8> out(sizeof(CNMTHeader) + (has_opt_header ? sizeof(OptionalHeader) : 0));
|
||||||
out.reserve(0x100); // Avoid resizing -- average size.
|
memcpy(out.data(), &header, sizeof(CNMTHeader));
|
||||||
memcpy(out.data(), header.get(), sizeof(CNMTHeader));
|
|
||||||
if (opt_header != nullptr) {
|
// Optional Header
|
||||||
out.resize(out.size() + sizeof(OptionalHeader));
|
if (has_opt_header) {
|
||||||
memcpy(out.data() + sizeof(CNMTHeader), opt_header.get(), sizeof(OptionalHeader));
|
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();
|
const auto dead_zone = offset + sizeof(CNMTHeader) - out.size();
|
||||||
if (dead_zone > 0)
|
if (dead_zone > 0)
|
||||||
out.resize(offset + sizeof(CNMTHeader));
|
out.resize(offset + sizeof(CNMTHeader));
|
||||||
|
|
||||||
|
out.resize(out.size() + content_records.size() * sizeof(ContentRecord));
|
||||||
for (const auto& rec : content_records) {
|
for (const auto& rec : content_records) {
|
||||||
out.resize(out.size() + sizeof(ContentRecord));
|
|
||||||
memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(ContentRecord));
|
memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(ContentRecord));
|
||||||
offset += sizeof(ContentRecord);
|
offset += sizeof(ContentRecord);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
out.resize(out.size() + content_records.size() * sizeof(MetaRecord));
|
||||||
for (const auto& rec : meta_records) {
|
for (const auto& rec : meta_records) {
|
||||||
out.resize(out.size() + sizeof(MetaRecord));
|
|
||||||
memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(MetaRecord));
|
memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(MetaRecord));
|
||||||
offset += sizeof(MetaRecord);
|
offset += sizeof(MetaRecord);
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,9 @@ enum class TitleType : u8 {
|
|||||||
DeltaTitle = 0x83,
|
DeltaTitle = 0x83,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool operator>=(TitleType lhs, TitleType rhs);
|
||||||
|
bool operator<=(TitleType lhs, TitleType rhs);
|
||||||
|
|
||||||
enum class ContentRecordType : u8 {
|
enum class ContentRecordType : u8 {
|
||||||
Meta = 0,
|
Meta = 0,
|
||||||
Program = 1,
|
Program = 1,
|
||||||
@ -96,8 +99,8 @@ public:
|
|||||||
std::vector<u8> Serialize() const;
|
std::vector<u8> Serialize() const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<CNMTHeader> header;
|
CNMTHeader header;
|
||||||
std::unique_ptr<OptionalHeader> opt_header;
|
OptionalHeader opt_header;
|
||||||
std::vector<ContentRecord> content_records;
|
std::vector<ContentRecord> content_records;
|
||||||
std::vector<MetaRecord> meta_records;
|
std::vector<MetaRecord> meta_records;
|
||||||
|
|
||||||
|
@ -23,13 +23,14 @@ bool operator<(const RegisteredCacheEntry& lhs, const RegisteredCacheEntry& rhs)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool FollowsTwoDigitDirFormat(std::string_view name) {
|
static bool FollowsTwoDigitDirFormat(std::string_view name) {
|
||||||
static const std::regex two_digit_regex(
|
static const std::regex two_digit_regex("000000[0-9A-F]{2}", std::regex_constants::ECMAScript |
|
||||||
"000000[0123456789abcdefABCDEF][0123456789abcdefABCDEF]");
|
std::regex_constants::icase);
|
||||||
return std::regex_match(name.begin(), name.end(), two_digit_regex);
|
return std::regex_match(name.begin(), name.end(), two_digit_regex);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool FollowsNcaIdFormat(std::string_view name) {
|
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);
|
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);
|
auto index = static_cast<size_t>(type);
|
||||||
if (index >= 0x80)
|
// If the index is after the jump in TitleType, subtract it out.
|
||||||
index -= 0x80;
|
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);
|
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 RegisteredCache::GetFileAtID(NcaID id) const {
|
||||||
VirtualFile file;
|
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) {
|
for (u8 i = 0; i < 4; ++i) {
|
||||||
file = OpenFileOrDirectoryConcat(
|
const auto path = GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0);
|
||||||
dir, GetRelativePathFromNcaID(id, (i & 0b10) == 0, (i & 0b01) == 0));
|
file = OpenFileOrDirectoryConcat(dir, path);
|
||||||
if (file != nullptr)
|
if (file != nullptr)
|
||||||
return file;
|
return file;
|
||||||
}
|
}
|
||||||
@ -420,6 +428,7 @@ bool RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const VfsCopyFunct
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
|
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 dir = this->dir->CreateDirectoryRelative("yuzu_meta");
|
||||||
const auto filename = GetCNMTName(cnmt.GetType(), cnmt.GetTitleID());
|
const auto filename = GetCNMTName(cnmt.GetType(), cnmt.GetTitleID());
|
||||||
if (dir->GetFile(filename) == nullptr) {
|
if (dir->GetFile(filename) == nullptr) {
|
||||||
|
@ -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())
|
if (entry == files.end() && offset < files.rbegin()->first + files.rbegin()->second->GetSize())
|
||||||
--entry;
|
--entry;
|
||||||
|
|
||||||
|
@ -83,10 +83,12 @@ VirtualFile RealVfsFilesystem::OpenFile(std::string_view path_, Mode perms) {
|
|||||||
|
|
||||||
VirtualFile RealVfsFilesystem::CreateFile(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 = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
|
||||||
if (!FileUtil::Exists(path) &&
|
if (!FileUtil::Exists(path))
|
||||||
!FileUtil::CreateFullPath(
|
return nullptr;
|
||||||
FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash)) &&
|
if (!FileUtil::CreateFullPath(
|
||||||
!FileUtil::CreateEmptyFile(path))
|
FileUtil::SanitizePath(path, FileUtil::DirectorySeparator::ForwardSlash)))
|
||||||
|
return nullptr;
|
||||||
|
if (!FileUtil::CreateEmptyFile(path))
|
||||||
return nullptr;
|
return nullptr;
|
||||||
return OpenFile(path, perms);
|
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) {
|
VirtualDir RealVfsFilesystem::CreateDirectory(std::string_view path_, Mode perms) {
|
||||||
const auto path = FileUtil::SanitizePath(path_, FileUtil::DirectorySeparator::PlatformDefault);
|
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;
|
return nullptr;
|
||||||
// Cannot use make_shared as RealVfsDirectory constructor is private
|
// Cannot use make_shared as RealVfsDirectory constructor is private
|
||||||
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
return std::shared_ptr<RealVfsDirectory>(new RealVfsDirectory(*this, path, perms));
|
||||||
|
Loading…
Reference in New Issue
Block a user