aboutsummaryrefslogtreecommitdiff
path: root/src/core/file_sys
diff options
context:
space:
mode:
Diffstat (limited to 'src/core/file_sys')
-rw-r--r--src/core/file_sys/bis_factory.cpp12
-rw-r--r--src/core/file_sys/bis_factory.h5
-rw-r--r--src/core/file_sys/card_image.cpp15
-rw-r--r--src/core/file_sys/content_archive.cpp8
-rw-r--r--src/core/file_sys/content_archive.h2
-rw-r--r--src/core/file_sys/control_metadata.cpp26
-rw-r--r--src/core/file_sys/control_metadata.h9
-rw-r--r--src/core/file_sys/directory.h2
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp366
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.h70
-rw-r--r--src/core/file_sys/nca_metadata.cpp6
-rw-r--r--src/core/file_sys/nca_metadata.h1
-rw-r--r--src/core/file_sys/nca_patch.cpp34
-rw-r--r--src/core/file_sys/nca_patch.h12
-rw-r--r--src/core/file_sys/partition_filesystem.cpp12
-rw-r--r--src/core/file_sys/partition_filesystem.h4
-rw-r--r--src/core/file_sys/patch_manager.cpp55
-rw-r--r--src/core/file_sys/patch_manager.h3
-rw-r--r--src/core/file_sys/program_metadata.cpp10
-rw-r--r--src/core/file_sys/program_metadata.h9
-rw-r--r--src/core/file_sys/registered_cache.cpp19
-rw-r--r--src/core/file_sys/registered_cache.h2
-rw-r--r--src/core/file_sys/romfs.cpp32
-rw-r--r--src/core/file_sys/romfs.h15
-rw-r--r--src/core/file_sys/romfs_factory.cpp4
-rw-r--r--src/core/file_sys/romfs_factory.h1
-rw-r--r--src/core/file_sys/savedata_factory.cpp15
-rw-r--r--src/core/file_sys/savedata_factory.h1
-rw-r--r--src/core/file_sys/submission_package.h2
-rw-r--r--src/core/file_sys/vfs.cpp67
-rw-r--r--src/core/file_sys/vfs.h47
-rw-r--r--src/core/file_sys/vfs_concat.cpp89
-rw-r--r--src/core/file_sys/vfs_concat.h28
-rw-r--r--src/core/file_sys/vfs_layered.cpp132
-rw-r--r--src/core/file_sys/vfs_layered.h50
-rw-r--r--src/core/file_sys/vfs_offset.cpp26
-rw-r--r--src/core/file_sys/vfs_offset.h27
-rw-r--r--src/core/file_sys/vfs_real.cpp25
-rw-r--r--src/core/file_sys/vfs_real.h9
-rw-r--r--src/core/file_sys/vfs_static.h79
-rw-r--r--src/core/file_sys/vfs_vector.cpp56
-rw-r--r--src/core/file_sys/vfs_vector.h26
-rw-r--r--src/core/file_sys/xts_archive.cpp21
-rw-r--r--src/core/file_sys/xts_archive.h5
44 files changed, 1237 insertions, 202 deletions
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 205492897..6102ef476 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -2,13 +2,14 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <fmt/format.h>
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/registered_cache.h"
namespace FileSys {
-BISFactory::BISFactory(VirtualDir nand_root_)
- : nand_root(std::move(nand_root_)),
+BISFactory::BISFactory(VirtualDir nand_root_, VirtualDir load_root_)
+ : nand_root(std::move(nand_root_)), load_root(std::move(load_root_)),
sysnand_cache(std::make_shared<RegisteredCache>(
GetOrCreateDirectoryRelative(nand_root, "/system/Contents/registered"))),
usrnand_cache(std::make_shared<RegisteredCache>(
@@ -24,4 +25,11 @@ std::shared_ptr<RegisteredCache> BISFactory::GetUserNANDContents() const {
return usrnand_cache;
}
+VirtualDir BISFactory::GetModificationLoadRoot(u64 title_id) const {
+ // LayeredFS doesn't work on updates and title id-less homebrew
+ if (title_id == 0 || (title_id & 0x800) > 0)
+ return nullptr;
+ return GetOrCreateDirectoryRelative(load_root, fmt::format("/{:016X}", title_id));
+}
+
} // namespace FileSys
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 9523dd864..c352e0925 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -17,14 +17,17 @@ class RegisteredCache;
/// registered caches.
class BISFactory {
public:
- explicit BISFactory(VirtualDir nand_root);
+ explicit BISFactory(VirtualDir nand_root, VirtualDir load_root);
~BISFactory();
std::shared_ptr<RegisteredCache> GetSystemNANDContents() const;
std::shared_ptr<RegisteredCache> GetUserNANDContents() const;
+ VirtualDir GetModificationLoadRoot(u64 title_id) const;
+
private:
VirtualDir nand_root;
+ VirtualDir load_root;
std::shared_ptr<RegisteredCache> sysnand_cache;
std::shared_ptr<RegisteredCache> usrnand_cache;
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 8218893b2..edfc1bbd4 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -41,13 +41,14 @@ XCI::XCI(VirtualFile file_) : file(std::move(file_)), partitions(0x4) {
for (XCIPartition partition :
{XCIPartition::Update, XCIPartition::Normal, XCIPartition::Secure, XCIPartition::Logo}) {
- auto raw = main_hfs.GetFile(partition_names[static_cast<size_t>(partition)]);
+ auto raw = main_hfs.GetFile(partition_names[static_cast<std::size_t>(partition)]);
if (raw != nullptr)
- partitions[static_cast<size_t>(partition)] = std::make_shared<PartitionFilesystem>(raw);
+ partitions[static_cast<std::size_t>(partition)] =
+ std::make_shared<PartitionFilesystem>(raw);
}
secure_partition = std::make_shared<NSP>(
- main_hfs.GetFile(partition_names[static_cast<size_t>(XCIPartition::Secure)]));
+ main_hfs.GetFile(partition_names[static_cast<std::size_t>(XCIPartition::Secure)]));
const auto secure_ncas = secure_partition->GetNCAsCollapsed();
std::copy(secure_ncas.begin(), secure_ncas.end(), std::back_inserter(ncas));
@@ -92,7 +93,7 @@ Loader::ResultStatus XCI::GetProgramNCAStatus() const {
}
VirtualDir XCI::GetPartition(XCIPartition partition) const {
- return partitions[static_cast<size_t>(partition)];
+ return partitions[static_cast<std::size_t>(partition)];
}
std::shared_ptr<NSP> XCI::GetSecurePartitionNSP() const {
@@ -168,11 +169,11 @@ bool XCI::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
}
Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
- if (partitions[static_cast<size_t>(part)] == nullptr) {
+ if (partitions[static_cast<std::size_t>(part)] == nullptr) {
return Loader::ResultStatus::ErrorXCIMissingPartition;
}
- for (const VirtualFile& file : partitions[static_cast<size_t>(part)]->GetFiles()) {
+ for (const VirtualFile& file : partitions[static_cast<std::size_t>(part)]->GetFiles()) {
if (file->GetExtension() != "nca")
continue;
auto nca = std::make_shared<NCA>(file);
@@ -187,7 +188,7 @@ Loader::ResultStatus XCI::AddNCAFromPartition(XCIPartition part) {
} else {
const u16 error_id = static_cast<u16>(nca->GetStatus());
LOG_CRITICAL(Loader, "Could not load NCA {}/{}, failed with error code {:04X} ({})",
- partition_names[static_cast<size_t>(part)], nca->GetName(), error_id,
+ partition_names[static_cast<std::size_t>(part)], nca->GetName(), error_id,
nca->GetStatus());
}
}
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 79bfb6fec..aa1b3c17d 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -298,11 +298,11 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
auto section = sections[i];
if (section.raw.header.filesystem_type == NCASectionFilesystemType::ROMFS) {
- const size_t base_offset =
+ const std::size_t base_offset =
header.section_tables[i].media_offset * MEDIA_OFFSET_MULTIPLIER;
ivfc_offset = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].offset;
- const size_t romfs_offset = base_offset + ivfc_offset;
- const size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
+ const std::size_t romfs_offset = base_offset + ivfc_offset;
+ const std::size_t romfs_size = section.romfs.ivfc.levels[IVFC_MAX_LEVEL - 1].size;
auto raw = std::make_shared<OffsetVfsFile>(file, romfs_size, romfs_offset);
auto dec = Decrypt(section, raw, romfs_offset);
@@ -463,6 +463,8 @@ NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_off
status = Loader::ResultStatus::Success;
}
+NCA::~NCA() = default;
+
Loader::ResultStatus NCA::GetStatus() const {
return status;
}
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index 00eca52da..f9f66cae9 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -81,6 +81,8 @@ class NCA : public ReadOnlyVfsDirectory {
public:
explicit NCA(VirtualFile file, VirtualFile bktr_base_romfs = nullptr,
u64 bktr_base_ivfc_offset = 0);
+ ~NCA() override;
+
Loader::ResultStatus GetStatus() const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index e76bf77bf..5b1177a03 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -8,6 +8,14 @@
namespace FileSys {
+const std::array<const char*, 15> LANGUAGE_NAMES = {
+ "AmericanEnglish", "BritishEnglish", "Japanese",
+ "French", "German", "LatinAmericanSpanish",
+ "Spanish", "Italian", "Dutch",
+ "CanadianFrench", "Portugese", "Russian",
+ "Korean", "Taiwanese", "Chinese",
+};
+
std::string LanguageEntry::GetApplicationName() const {
return Common::StringFromFixedZeroTerminatedBuffer(application_name.data(), 0x200);
}
@@ -20,18 +28,20 @@ NACP::NACP(VirtualFile file) : raw(std::make_unique<RawNACP>()) {
file->ReadObject(raw.get());
}
+NACP::~NACP() = default;
+
const LanguageEntry& NACP::GetLanguageEntry(Language language) const {
if (language != Language::Default) {
return raw->language_entries.at(static_cast<u8>(language));
- } else {
- for (const auto& language_entry : raw->language_entries) {
- if (!language_entry.GetApplicationName().empty())
- return language_entry;
- }
-
- // Fallback to English
- return GetLanguageEntry(Language::AmericanEnglish);
}
+
+ for (const auto& language_entry : raw->language_entries) {
+ if (!language_entry.GetApplicationName().empty())
+ return language_entry;
+ }
+
+ // Fallback to English
+ return GetLanguageEntry(Language::AmericanEnglish);
}
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 8a510bf46..43d6f0719 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -66,18 +66,15 @@ enum class Language : u8 {
Default = 255,
};
-static constexpr std::array<const char*, 15> LANGUAGE_NAMES = {
- "AmericanEnglish", "BritishEnglish", "Japanese",
- "French", "German", "LatinAmericanSpanish",
- "Spanish", "Italian", "Dutch",
- "CanadianFrench", "Portugese", "Russian",
- "Korean", "Taiwanese", "Chinese"};
+extern const std::array<const char*, 15> LANGUAGE_NAMES;
// A class representing the format used by NX metadata files, typically named Control.nacp.
// These store application name, dev name, title id, and other miscellaneous data.
class NACP {
public:
explicit NACP(VirtualFile file);
+ ~NACP();
+
const LanguageEntry& GetLanguageEntry(Language language = Language::Default) const;
std::string GetApplicationName(Language language = Language::Default) const;
std::string GetDeveloperName(Language language = Language::Default) const;
diff --git a/src/core/file_sys/directory.h b/src/core/file_sys/directory.h
index 3759e743a..12bb90ec8 100644
--- a/src/core/file_sys/directory.h
+++ b/src/core/file_sys/directory.h
@@ -25,7 +25,7 @@ enum EntryType : u8 {
struct Entry {
Entry(std::string_view view, EntryType entry_type, u64 entry_size)
: type{entry_type}, file_size{entry_size} {
- const size_t copy_size = view.copy(filename, std::size(filename) - 1);
+ const std::size_t copy_size = view.copy(filename, std::size(filename) - 1);
filename[copy_size] = '\0';
}
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
new file mode 100644
index 000000000..2a913ce82
--- /dev/null
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 2018 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Adapted by DarkLordZach for use/interaction with yuzu
+ *
+ * Modifications Copyright 2018 yuzu emulator team
+ * Licensed under GPLv2 or any later version
+ * Refer to the license.txt file included.
+ */
+
+#include <cstring>
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "core/file_sys/fsmitm_romfsbuild.h"
+#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs_vector.h"
+
+namespace FileSys {
+
+constexpr u64 FS_MAX_PATH = 0x301;
+
+constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
+constexpr u32 ROMFS_FILEPARTITION_OFS = 0x200;
+
+// Types for building a RomFS.
+struct RomFSHeader {
+ u64 header_size;
+ u64 dir_hash_table_ofs;
+ u64 dir_hash_table_size;
+ u64 dir_table_ofs;
+ u64 dir_table_size;
+ u64 file_hash_table_ofs;
+ u64 file_hash_table_size;
+ u64 file_table_ofs;
+ u64 file_table_size;
+ u64 file_partition_ofs;
+};
+static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
+
+struct RomFSDirectoryEntry {
+ u32 parent;
+ u32 sibling;
+ u32 child;
+ u32 file;
+ u32 hash;
+ u32 name_size;
+};
+static_assert(sizeof(RomFSDirectoryEntry) == 0x18, "RomFSDirectoryEntry has incorrect size.");
+
+struct RomFSFileEntry {
+ u32 parent;
+ u32 sibling;
+ u64 offset;
+ u64 size;
+ u32 hash;
+ u32 name_size;
+};
+static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size.");
+
+struct RomFSBuildFileContext;
+
+struct RomFSBuildDirectoryContext {
+ std::string path;
+ u32 cur_path_ofs = 0;
+ u32 path_len = 0;
+ u32 entry_offset = 0;
+ std::shared_ptr<RomFSBuildDirectoryContext> parent;
+ std::shared_ptr<RomFSBuildDirectoryContext> child;
+ std::shared_ptr<RomFSBuildDirectoryContext> sibling;
+ std::shared_ptr<RomFSBuildFileContext> file;
+};
+
+struct RomFSBuildFileContext {
+ std::string path;
+ u32 cur_path_ofs = 0;
+ u32 path_len = 0;
+ u32 entry_offset = 0;
+ u64 offset = 0;
+ u64 size = 0;
+ std::shared_ptr<RomFSBuildDirectoryContext> parent;
+ std::shared_ptr<RomFSBuildFileContext> sibling;
+ VirtualFile source;
+};
+
+static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) {
+ u32 hash = parent ^ 123456789;
+ for (u32 i = 0; i < path_len; i++) {
+ hash = (hash >> 5) | (hash << 27);
+ hash ^= path[start + i];
+ }
+
+ return hash;
+}
+
+static u64 romfs_get_hash_table_count(u64 num_entries) {
+ if (num_entries < 3) {
+ return 3;
+ }
+
+ if (num_entries < 19) {
+ return num_entries | 1;
+ }
+
+ u64 count = num_entries;
+ while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 ||
+ count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
+ count++;
+ }
+ return count;
+}
+
+void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
+ std::shared_ptr<RomFSBuildDirectoryContext> parent) {
+ std::vector<std::shared_ptr<RomFSBuildDirectoryContext>> child_dirs;
+
+ VirtualDir dir;
+
+ if (parent->path_len == 0)
+ dir = root_romfs;
+ else
+ dir = root_romfs->GetDirectoryRelative(parent->path);
+
+ const auto entries = dir->GetEntries();
+
+ for (const auto& kv : entries) {
+ if (kv.second == VfsEntryType::Directory) {
+ const auto child = std::make_shared<RomFSBuildDirectoryContext>();
+ // Set child's path.
+ child->cur_path_ofs = parent->path_len + 1;
+ child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
+ child->path = parent->path + "/" + kv.first;
+
+ // Sanity check on path_len
+ ASSERT(child->path_len < FS_MAX_PATH);
+
+ if (AddDirectory(parent, child)) {
+ child_dirs.push_back(child);
+ }
+ } else {
+ const auto child = std::make_shared<RomFSBuildFileContext>();
+ // Set child's path.
+ child->cur_path_ofs = parent->path_len + 1;
+ child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
+ child->path = parent->path + "/" + kv.first;
+
+ // Sanity check on path_len
+ ASSERT(child->path_len < FS_MAX_PATH);
+
+ child->source = root_romfs->GetFileRelative(child->path);
+
+ child->size = child->source->GetSize();
+
+ AddFile(parent, child);
+ }
+ }
+
+ for (auto& child : child_dirs) {
+ this->VisitDirectory(root_romfs, child);
+ }
+}
+
+bool RomFSBuildContext::AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
+ std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx) {
+ // Check whether it's already in the known directories.
+ const auto existing = directories.find(dir_ctx->path);
+ if (existing != directories.end())
+ return false;
+
+ // Add a new directory.
+ num_dirs++;
+ dir_table_size +=
+ sizeof(RomFSDirectoryEntry) + Common::AlignUp(dir_ctx->path_len - dir_ctx->cur_path_ofs, 4);
+ dir_ctx->parent = parent_dir_ctx;
+ directories.emplace(dir_ctx->path, dir_ctx);
+
+ return true;
+}
+
+bool RomFSBuildContext::AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
+ std::shared_ptr<RomFSBuildFileContext> file_ctx) {
+ // Check whether it's already in the known files.
+ const auto existing = files.find(file_ctx->path);
+ if (existing != files.end()) {
+ return false;
+ }
+
+ // Add a new file.
+ num_files++;
+ file_table_size +=
+ sizeof(RomFSFileEntry) + Common::AlignUp(file_ctx->path_len - file_ctx->cur_path_ofs, 4);
+ file_ctx->parent = parent_dir_ctx;
+ files.emplace(file_ctx->path, file_ctx);
+
+ return true;
+}
+
+RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_)) {
+ root = std::make_shared<RomFSBuildDirectoryContext>();
+ root->path = "\0";
+ directories.emplace(root->path, root);
+ num_dirs = 1;
+ dir_table_size = 0x18;
+
+ VisitDirectory(base, root);
+}
+
+RomFSBuildContext::~RomFSBuildContext() = default;
+
+std::map<u64, VirtualFile> RomFSBuildContext::Build() {
+ const u64 dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
+ const u64 file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
+ dir_hash_table_size = 4 * dir_hash_table_entry_count;
+ file_hash_table_size = 4 * file_hash_table_entry_count;
+
+ // Assign metadata pointers
+ RomFSHeader header{};
+
+ std::vector<u32> dir_hash_table(dir_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
+ std::vector<u32> file_hash_table(file_hash_table_entry_count, ROMFS_ENTRY_EMPTY);
+
+ std::vector<u8> dir_table(dir_table_size);
+ std::vector<u8> file_table(file_table_size);
+
+ std::shared_ptr<RomFSBuildFileContext> cur_file;
+
+ // Determine file offsets.
+ u32 entry_offset = 0;
+ std::shared_ptr<RomFSBuildFileContext> prev_file = nullptr;
+ for (const auto& it : files) {
+ cur_file = it.second;
+ file_partition_size = Common::AlignUp(file_partition_size, 16);
+ cur_file->offset = file_partition_size;
+ file_partition_size += cur_file->size;
+ cur_file->entry_offset = entry_offset;
+ entry_offset += sizeof(RomFSFileEntry) +
+ Common::AlignUp(cur_file->path_len - cur_file->cur_path_ofs, 4);
+ prev_file = cur_file;
+ }
+ // Assign deferred parent/sibling ownership.
+ for (auto it = files.rbegin(); it != files.rend(); ++it) {
+ cur_file = it->second;
+ cur_file->sibling = cur_file->parent->file;
+ cur_file->parent->file = cur_file;
+ }
+
+ std::shared_ptr<RomFSBuildDirectoryContext> cur_dir;
+
+ // Determine directory offsets.
+ entry_offset = 0;
+ for (const auto& it : directories) {
+ cur_dir = it.second;
+ cur_dir->entry_offset = entry_offset;
+ entry_offset += sizeof(RomFSDirectoryEntry) +
+ Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4);
+ }
+ // Assign deferred parent/sibling ownership.
+ for (auto it = directories.rbegin(); it->second != root; ++it) {
+ cur_dir = it->second;
+ cur_dir->sibling = cur_dir->parent->child;
+ cur_dir->parent->child = cur_dir;
+ }
+
+ std::map<u64, VirtualFile> out;
+
+ // Populate file tables.
+ for (const auto& it : files) {
+ cur_file = it.second;
+ RomFSFileEntry cur_entry{};
+
+ cur_entry.parent = cur_file->parent->entry_offset;
+ cur_entry.sibling =
+ cur_file->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_file->sibling->entry_offset;
+ cur_entry.offset = cur_file->offset;
+ cur_entry.size = cur_file->size;
+
+ const auto name_size = cur_file->path_len - cur_file->cur_path_ofs;
+ const auto hash = romfs_calc_path_hash(cur_file->parent->entry_offset, cur_file->path,
+ cur_file->cur_path_ofs, name_size);
+ cur_entry.hash = file_hash_table[hash % file_hash_table_entry_count];
+ file_hash_table[hash % file_hash_table_entry_count] = cur_file->entry_offset;
+
+ cur_entry.name_size = name_size;
+
+ out.emplace(cur_file->offset + ROMFS_FILEPARTITION_OFS, cur_file->source);
+ std::memcpy(file_table.data() + cur_file->entry_offset, &cur_entry, sizeof(RomFSFileEntry));
+ std::memset(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry), 0,
+ Common::AlignUp(cur_entry.name_size, 4));
+ std::memcpy(file_table.data() + cur_file->entry_offset + sizeof(RomFSFileEntry),
+ cur_file->path.data() + cur_file->cur_path_ofs, name_size);
+ }
+
+ // Populate dir tables.
+ for (const auto& it : directories) {
+ cur_dir = it.second;
+ RomFSDirectoryEntry cur_entry{};
+
+ cur_entry.parent = cur_dir == root ? 0 : cur_dir->parent->entry_offset;
+ cur_entry.sibling =
+ cur_dir->sibling == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->sibling->entry_offset;
+ cur_entry.child =
+ cur_dir->child == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->child->entry_offset;
+ cur_entry.file = cur_dir->file == nullptr ? ROMFS_ENTRY_EMPTY : cur_dir->file->entry_offset;
+
+ const auto name_size = cur_dir->path_len - cur_dir->cur_path_ofs;
+ const auto hash = romfs_calc_path_hash(cur_dir == root ? 0 : cur_dir->parent->entry_offset,
+ cur_dir->path, cur_dir->cur_path_ofs, name_size);
+ cur_entry.hash = dir_hash_table[hash % dir_hash_table_entry_count];
+ dir_hash_table[hash % dir_hash_table_entry_count] = cur_dir->entry_offset;
+
+ cur_entry.name_size = name_size;
+
+ std::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry,
+ sizeof(RomFSDirectoryEntry));
+ std::memset(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry), 0,
+ Common::AlignUp(cur_entry.name_size, 4));
+ std::memcpy(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry),
+ cur_dir->path.data() + cur_dir->cur_path_ofs, name_size);
+ }
+
+ // Set header fields.
+ header.header_size = sizeof(RomFSHeader);
+ header.file_hash_table_size = file_hash_table_size;
+ header.file_table_size = file_table_size;
+ header.dir_hash_table_size = dir_hash_table_size;
+ header.dir_table_size = dir_table_size;
+ header.file_partition_ofs = ROMFS_FILEPARTITION_OFS;
+ header.dir_hash_table_ofs = Common::AlignUp(header.file_partition_ofs + file_partition_size, 4);
+ header.dir_table_ofs = header.dir_hash_table_ofs + header.dir_hash_table_size;
+ header.file_hash_table_ofs = header.dir_table_ofs + header.dir_table_size;
+ header.file_table_ofs = header.file_hash_table_ofs + header.file_hash_table_size;
+
+ std::vector<u8> header_data(sizeof(RomFSHeader));
+ std::memcpy(header_data.data(), &header, header_data.size());
+ out.emplace(0, std::make_shared<VectorVfsFile>(std::move(header_data)));
+
+ std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
+ dir_table_size);
+ std::size_t index = 0;
+ std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32));
+ index += dir_hash_table.size() * sizeof(u32);
+ std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size());
+ index += dir_table.size();
+ std::memcpy(metadata.data() + index, file_hash_table.data(),
+ file_hash_table.size() * sizeof(u32));
+ index += file_hash_table.size() * sizeof(u32);
+ std::memcpy(metadata.data() + index, file_table.data(), file_table.size());
+ out.emplace(header.dir_hash_table_ofs, std::make_shared<VectorVfsFile>(std::move(metadata)));
+
+ return out;
+}
+
+} // namespace FileSys
diff --git a/src/core/file_sys/fsmitm_romfsbuild.h b/src/core/file_sys/fsmitm_romfsbuild.h
new file mode 100644
index 000000000..b0c3c123b
--- /dev/null
+++ b/src/core/file_sys/fsmitm_romfsbuild.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2018 Atmosphère-NX
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/*
+ * Adapted by DarkLordZach for use/interaction with yuzu
+ *
+ * Modifications Copyright 2018 yuzu emulator team
+ * Licensed under GPLv2 or any later version
+ * Refer to the license.txt file included.
+ */
+
+#pragma once
+
+#include <map>
+#include <memory>
+#include <string>
+#include <boost/detail/container_fwd.hpp>
+#include "common/common_types.h"
+#include "core/file_sys/vfs.h"
+
+namespace FileSys {
+
+struct RomFSBuildDirectoryContext;
+struct RomFSBuildFileContext;
+struct RomFSDirectoryEntry;
+struct RomFSFileEntry;
+
+class RomFSBuildContext {
+public:
+ explicit RomFSBuildContext(VirtualDir base);
+ ~RomFSBuildContext();
+
+ // This finalizes the context.
+ std::map<u64, VirtualFile> Build();
+
+private:
+ VirtualDir base;
+ std::shared_ptr<RomFSBuildDirectoryContext> root;
+ std::map<std::string, std::shared_ptr<RomFSBuildDirectoryContext>, std::less<>> directories;
+ std::map<std::string, std::shared_ptr<RomFSBuildFileContext>, std::less<>> files;
+ u64 num_dirs = 0;
+ u64 num_files = 0;
+ u64 dir_table_size = 0;
+ u64 file_table_size = 0;
+ u64 dir_hash_table_size = 0;
+ u64 file_hash_table_size = 0;
+ u64 file_partition_size = 0;
+
+ void VisitDirectory(VirtualDir filesys, std::shared_ptr<RomFSBuildDirectoryContext> parent);
+
+ bool AddDirectory(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
+ std::shared_ptr<RomFSBuildDirectoryContext> dir_ctx);
+ bool AddFile(std::shared_ptr<RomFSBuildDirectoryContext> parent_dir_ctx,
+ std::shared_ptr<RomFSBuildFileContext> file_ctx);
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp
index cdfbc5aaf..6f34b7836 100644
--- a/src/core/file_sys/nca_metadata.cpp
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -11,11 +11,11 @@
namespace FileSys {
bool operator>=(TitleType lhs, TitleType rhs) {
- return static_cast<size_t>(lhs) >= static_cast<size_t>(rhs);
+ return static_cast<std::size_t>(lhs) >= static_cast<std::size_t>(rhs);
}
bool operator<=(TitleType lhs, TitleType rhs) {
- return static_cast<size_t>(lhs) <= static_cast<size_t>(rhs);
+ return static_cast<std::size_t>(lhs) <= static_cast<std::size_t>(rhs);
}
CNMT::CNMT(VirtualFile file) {
@@ -51,6 +51,8 @@ CNMT::CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentReco
: header(std::move(header)), opt_header(std::move(opt_header)),
content_records(std::move(content_records)), meta_records(std::move(meta_records)) {}
+CNMT::~CNMT() = default;
+
u64 CNMT::GetTitleID() const {
return header.title_id;
}
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index da5a8dbe8..a05d155f4 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -87,6 +87,7 @@ public:
explicit CNMT(VirtualFile file);
CNMT(CNMTHeader header, OptionalHeader opt_header, std::vector<ContentRecord> content_records,
std::vector<MetaRecord> meta_records);
+ ~CNMT();
u64 GetTitleID() const;
u32 GetTitleVersion() const;
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp
index 6fc5bd7d8..0090cc6c4 100644
--- a/src/core/file_sys/nca_patch.cpp
+++ b/src/core/file_sys/nca_patch.cpp
@@ -22,11 +22,11 @@ BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock rel
base_romfs(std::move(base_romfs_)), bktr_romfs(std::move(bktr_romfs_)),
encrypted(is_encrypted_), key(key_), base_offset(base_offset_), ivfc_offset(ivfc_offset_),
section_ctr(section_ctr_) {
- for (size_t i = 0; i < relocation.number_buckets - 1; ++i) {
+ for (std::size_t i = 0; i < relocation.number_buckets - 1; ++i) {
relocation_buckets[i].entries.push_back({relocation.base_offsets[i + 1], 0, 0});
}
- for (size_t i = 0; i < subsection.number_buckets - 1; ++i) {
+ for (std::size_t i = 0; i < subsection.number_buckets - 1; ++i) {
subsection_buckets[i].entries.push_back({subsection_buckets[i + 1].entries[0].address_patch,
{0},
subsection_buckets[i + 1].entries[0].ctr});
@@ -37,7 +37,7 @@ BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock rel
BKTR::~BKTR() = default;
-size_t BKTR::Read(u8* data, size_t length, size_t offset) const {
+std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const {
// Read out of bounds.
if (offset >= relocation.size)
return 0;
@@ -69,14 +69,14 @@ size_t BKTR::Read(u8* data, size_t length, size_t offset) const {
std::vector<u8> iv(16);
auto subsection_ctr = subsection.ctr;
auto offset_iv = section_offset + base_offset;
- for (size_t i = 0; i < section_ctr.size(); ++i)
+ for (std::size_t i = 0; i < section_ctr.size(); ++i)
iv[i] = section_ctr[0x8 - i - 1];
offset_iv >>= 4;
- for (size_t i = 0; i < sizeof(u64); ++i) {
+ for (std::size_t i = 0; i < sizeof(u64); ++i) {
iv[0xF - i] = static_cast<u8>(offset_iv & 0xFF);
offset_iv >>= 8;
}
- for (size_t i = 0; i < sizeof(u32); ++i) {
+ for (std::size_t i = 0; i < sizeof(u32); ++i) {
iv[0x7 - i] = static_cast<u8>(subsection_ctr & 0xFF);
subsection_ctr >>= 8;
}
@@ -110,8 +110,8 @@ size_t BKTR::Read(u8* data, size_t length, size_t offset) const {
}
template <bool Subsection, typename BlockType, typename BucketType>
-std::pair<size_t, size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
- BucketType buckets) const {
+std::pair<std::size_t, std::size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
+ BucketType buckets) const {
if constexpr (Subsection) {
const auto last_bucket = buckets[block.number_buckets - 1];
if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch)
@@ -120,18 +120,18 @@ std::pair<size_t, size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block.");
}
- size_t bucket_id = std::count_if(block.base_offsets.begin() + 1,
- block.base_offsets.begin() + block.number_buckets,
- [&offset](u64 base_offset) { return base_offset <= offset; });
+ std::size_t bucket_id = std::count_if(
+ block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets,
+ [&offset](u64 base_offset) { return base_offset <= offset; });
const auto bucket = buckets[bucket_id];
if (bucket.number_entries == 1)
return {bucket_id, 0};
- size_t low = 0;
- size_t mid = 0;
- size_t high = bucket.number_entries - 1;
+ std::size_t low = 0;
+ std::size_t mid = 0;
+ std::size_t high = bucket.number_entries - 1;
while (low <= high) {
mid = (low + high) / 2;
if (bucket.entries[mid].address_patch > offset) {
@@ -179,11 +179,11 @@ std::string BKTR::GetName() const {
return base_romfs->GetName();
}
-size_t BKTR::GetSize() const {
+std::size_t BKTR::GetSize() const {
return relocation.size;
}
-bool BKTR::Resize(size_t new_size) {
+bool BKTR::Resize(std::size_t new_size) {
return false;
}
@@ -199,7 +199,7 @@ bool BKTR::IsReadable() const {
return true;
}
-size_t BKTR::Write(const u8* data, size_t length, size_t offset) {
+std::size_t BKTR::Write(const u8* data, std::size_t length, std::size_t offset) {
return 0;
}
diff --git a/src/core/file_sys/nca_patch.h b/src/core/file_sys/nca_patch.h
index 381f3504f..8e64e8378 100644
--- a/src/core/file_sys/nca_patch.h
+++ b/src/core/file_sys/nca_patch.h
@@ -98,13 +98,13 @@ public:
Core::Crypto::Key128 key, u64 base_offset, u64 ivfc_offset, std::array<u8, 8> section_ctr);
~BKTR() override;
- size_t Read(u8* data, size_t length, size_t offset) const override;
+ std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
std::string GetName() const override;
- size_t GetSize() const override;
+ std::size_t GetSize() const override;
- bool Resize(size_t new_size) override;
+ bool Resize(std::size_t new_size) override;
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
@@ -112,14 +112,14 @@ public:
bool IsReadable() const override;
- size_t Write(const u8* data, size_t length, size_t offset) override;
+ std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
bool Rename(std::string_view name) override;
private:
template <bool Subsection, typename BlockType, typename BucketType>
- std::pair<size_t, size_t> SearchBucketEntry(u64 offset, BlockType block,
- BucketType buckets) const;
+ std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, BlockType block,
+ BucketType buckets) const;
RelocationEntry GetRelocationEntry(u64 offset) const;
RelocationEntry GetNextRelocationEntry(u64 offset) const;
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index c377edc9c..5791c76ff 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -42,21 +42,21 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
is_hfs = pfs_header.magic == Common::MakeMagic('H', 'F', 'S', '0');
- size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
- size_t metadata_size =
+ std::size_t entry_size = is_hfs ? sizeof(HFSEntry) : sizeof(PFSEntry);
+ std::size_t metadata_size =
sizeof(Header) + (pfs_header.num_entries * entry_size) + pfs_header.strtab_size;
// Actually read in now...
std::vector<u8> file_data = file->ReadBytes(metadata_size);
- const size_t total_size = file_data.size();
+ const std::size_t total_size = file_data.size();
if (total_size != metadata_size) {
status = Loader::ResultStatus::ErrorIncorrectPFSFileSize;
return;
}
- size_t entries_offset = sizeof(Header);
- size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size);
+ std::size_t entries_offset = sizeof(Header);
+ std::size_t strtab_offset = entries_offset + (pfs_header.num_entries * entry_size);
content_offset = strtab_offset + pfs_header.strtab_size;
for (u16 i = 0; i < pfs_header.num_entries; i++) {
FSEntry entry;
@@ -72,6 +72,8 @@ PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
status = Loader::ResultStatus::Success;
}
+PartitionFilesystem::~PartitionFilesystem() = default;
+
Loader::ResultStatus PartitionFilesystem::GetStatus() const {
return status;
}
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h
index be7bc32a8..739c63a7f 100644
--- a/src/core/file_sys/partition_filesystem.h
+++ b/src/core/file_sys/partition_filesystem.h
@@ -25,6 +25,8 @@ namespace FileSys {
class PartitionFilesystem : public ReadOnlyVfsDirectory {
public:
explicit PartitionFilesystem(std::shared_ptr<VfsFile> file);
+ ~PartitionFilesystem() override;
+
Loader::ResultStatus GetStatus() const;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
@@ -79,7 +81,7 @@ private:
Header pfs_header{};
bool is_hfs = false;
- size_t content_offset = 0;
+ std::size_t content_offset = 0;
std::vector<VirtualFile> pfs_files;
std::vector<VirtualDir> pfs_dirs;
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 6cecab336..4b3b5e665 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -11,6 +11,7 @@
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/romfs.h"
+#include "core/file_sys/vfs_layered.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
@@ -21,7 +22,7 @@ constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
std::array<u8, sizeof(u32)> bytes{};
bytes[0] = version % SINGLE_BYTE_MODULUS;
- for (size_t i = 1; i < bytes.size(); ++i) {
+ for (std::size_t i = 1; i < bytes.size(); ++i) {
version /= SINGLE_BYTE_MODULUS;
bytes[i] = version % SINGLE_BYTE_MODULUS;
}
@@ -31,16 +32,19 @@ std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
}
-constexpr std::array<const char*, 1> PATCH_TYPE_NAMES{
+constexpr std::array<const char*, 2> PATCH_TYPE_NAMES{
"Update",
+ "LayeredFS",
};
std::string FormatPatchTypeName(PatchType type) {
- return PATCH_TYPE_NAMES.at(static_cast<size_t>(type));
+ return PATCH_TYPE_NAMES.at(static_cast<std::size_t>(type));
}
PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
+PatchManager::~PatchManager() = default;
+
VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
LOG_INFO(Loader, "Patching ExeFS for title_id={:016X}", title_id);
@@ -64,6 +68,44 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
return exefs;
}
+static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
+ const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+ if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) {
+ return;
+ }
+
+ auto extracted = ExtractRomFS(romfs);
+ if (extracted == nullptr) {
+ return;
+ }
+
+ auto patch_dirs = load_dir->GetSubdirectories();
+ std::sort(patch_dirs.begin(), patch_dirs.end(),
+ [](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
+
+ std::vector<VirtualDir> layers;
+ layers.reserve(patch_dirs.size() + 1);
+ for (const auto& subdir : patch_dirs) {
+ auto romfs_dir = subdir->GetSubdirectory("romfs");
+ if (romfs_dir != nullptr)
+ layers.push_back(std::move(romfs_dir));
+ }
+ layers.push_back(std::move(extracted));
+
+ auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
+ if (layered == nullptr) {
+ return;
+ }
+
+ auto packed = CreateRomFS(std::move(layered));
+ if (packed == nullptr) {
+ return;
+ }
+
+ LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully");
+ romfs = std::move(packed);
+}
+
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
ContentRecordType type) const {
LOG_INFO(Loader, "Patching RomFS for title_id={:016X}, type={:02X}", title_id,
@@ -87,6 +129,9 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
}
}
+ // LayeredFS
+ ApplyLayeredFS(romfs, title_id, type);
+
return romfs;
}
@@ -112,6 +157,10 @@ std::map<PatchType, std::string> PatchManager::GetPatchVersionNames() const {
}
}
+ const auto lfs_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
+ if (lfs_dir != nullptr && lfs_dir->GetSize() > 0)
+ out.insert_or_assign(PatchType::LayeredFS, "");
+
return out;
}
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index b521977b2..464f17515 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -26,6 +26,7 @@ std::string FormatTitleVersion(u32 version,
enum class PatchType {
Update,
+ LayeredFS,
};
std::string FormatPatchTypeName(PatchType type);
@@ -34,6 +35,7 @@ std::string FormatPatchTypeName(PatchType type);
class PatchManager {
public:
explicit PatchManager(u64 title_id);
+ ~PatchManager();
// Currently tracked ExeFS patches:
// - Game Updates
@@ -41,6 +43,7 @@ public:
// Currently tracked RomFS patches:
// - Game Updates
+ // - LayeredFS
VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
ContentRecordType type = ContentRecordType::Program) const;
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index ccb685526..8903ed1d3 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -12,8 +12,12 @@
namespace FileSys {
+ProgramMetadata::ProgramMetadata() = default;
+
+ProgramMetadata::~ProgramMetadata() = default;
+
Loader::ResultStatus ProgramMetadata::Load(VirtualFile file) {
- size_t total_size = static_cast<size_t>(file->GetSize());
+ std::size_t total_size = static_cast<std::size_t>(file->GetSize());
if (total_size < sizeof(Header))
return Loader::ResultStatus::ErrorBadNPDMHeader;
@@ -79,10 +83,12 @@ void ProgramMetadata::Print() const {
auto address_space = "Unknown";
switch (npdm_header.address_space_type) {
- case ProgramAddressSpaceType::Is64Bit:
+ case ProgramAddressSpaceType::Is36Bit:
+ case ProgramAddressSpaceType::Is39Bit:
address_space = "64-bit";
break;
case ProgramAddressSpaceType::Is32Bit:
+ case ProgramAddressSpaceType::Is32BitNoMap:
address_space = "32-bit";
break;
}
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 3c0a49f16..e4470d6f0 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -17,8 +17,10 @@ enum class ResultStatus : u16;
namespace FileSys {
enum class ProgramAddressSpaceType : u8 {
- Is64Bit = 1,
- Is32Bit = 2,
+ Is32Bit = 0,
+ Is36Bit = 1,
+ Is32BitNoMap = 2,
+ Is39Bit = 3,
};
enum class ProgramFilePermission : u64 {
@@ -36,6 +38,9 @@ enum class ProgramFilePermission : u64 {
*/
class ProgramMetadata {
public:
+ ProgramMetadata();
+ ~ProgramMetadata();
+
Loader::ResultStatus Load(VirtualFile file);
bool Is64BitProgram() const;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index 7361a67be..e9b040689 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -18,6 +18,10 @@
#include "core/loader/loader.h"
namespace FileSys {
+
+// The size of blocks to use when vfs raw copying into nand.
+constexpr size_t VFS_RC_LARGE_COPY_BLOCK = 0x400000;
+
std::string RegisteredCacheEntry::DebugInfo() const {
return fmt::format("title_id={:016X}, content_type={:02X}", title_id, static_cast<u8>(type));
}
@@ -62,11 +66,11 @@ static std::string GetCNMTName(TitleType type, u64 title_id) {
"" ///< Currently unknown 'DeltaTitle'
};
- auto index = static_cast<size_t>(type);
+ auto index = static_cast<std::size_t>(type);
// 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) -
- static_cast<size_t>(TitleType::FirmwarePackageB);
+ if (index >= static_cast<std::size_t>(TitleType::Application)) {
+ index -= static_cast<std::size_t>(TitleType::Application) -
+ static_cast<std::size_t>(TitleType::FirmwarePackageB);
}
return fmt::format("{}_{:016x}.cnmt", TITLE_TYPE_NAMES[index], title_id);
}
@@ -105,7 +109,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
} else {
std::vector<VirtualFile> concat;
// Since the files are a two-digit hex number, max is FF.
- for (size_t i = 0; i < 0x100; ++i) {
+ for (std::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));
@@ -121,7 +125,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
if (concat.empty())
return nullptr;
- file = FileSys::ConcatenateFiles(concat);
+ file = ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
}
return file;
@@ -480,7 +484,8 @@ InstallResult RegisteredCache::RawInstallNCA(std::shared_ptr<NCA> nca, const Vfs
auto out = dir->CreateFileRelative(path);
if (out == nullptr)
return InstallResult::ErrorCopyFailed;
- return copy(in, out) ? InstallResult::Success : InstallResult::ErrorCopyFailed;
+ return copy(in, out, VFS_RC_LARGE_COPY_BLOCK) ? InstallResult::Success
+ : InstallResult::ErrorCopyFailed;
}
bool RegisteredCache::RawInstallYuzuMeta(const CNMT& cnmt) {
diff --git a/src/core/file_sys/registered_cache.h b/src/core/file_sys/registered_cache.h
index f487b0cf0..c0cd59fc5 100644
--- a/src/core/file_sys/registered_cache.h
+++ b/src/core/file_sys/registered_cache.h
@@ -27,7 +27,7 @@ struct ContentRecord;
using NcaID = std::array<u8, 0x10>;
using RegisteredCacheParsingFunction = std::function<VirtualFile(const VirtualFile&, const NcaID&)>;
-using VfsCopyFunction = std::function<bool(VirtualFile, VirtualFile)>;
+using VfsCopyFunction = std::function<bool(const VirtualFile&, const VirtualFile&, size_t)>;
enum class InstallResult {
Success,
diff --git a/src/core/file_sys/romfs.cpp b/src/core/file_sys/romfs.cpp
index e490c8ace..5910f7046 100644
--- a/src/core/file_sys/romfs.cpp
+++ b/src/core/file_sys/romfs.cpp
@@ -4,8 +4,10 @@
#include "common/common_types.h"
#include "common/swap.h"
+#include "core/file_sys/fsmitm_romfsbuild.h"
#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
@@ -49,7 +51,7 @@ struct FileEntry {
static_assert(sizeof(FileEntry) == 0x20, "FileEntry has incorrect size.");
template <typename Entry>
-static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t offset) {
+static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) {
Entry entry{};
if (file->ReadObject(&entry, offset) != sizeof(Entry))
return {};
@@ -59,8 +61,8 @@ static std::pair<Entry, std::string> GetEntry(const VirtualFile& file, size_t of
return {entry, string};
}
-void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 this_file_offset,
- std::shared_ptr<VectorVfsDirectory> parent) {
+void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset,
+ u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) {
while (true) {
auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
@@ -74,8 +76,9 @@ void ProcessFile(VirtualFile file, size_t file_offset, size_t data_offset, u32 t
}
}
-void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, size_t data_offset,
- u32 this_dir_offset, std::shared_ptr<VectorVfsDirectory> parent) {
+void ProcessDirectory(VirtualFile file, std::size_t dir_offset, std::size_t file_offset,
+ std::size_t data_offset, u32 this_dir_offset,
+ std::shared_ptr<VectorVfsDirectory> parent) {
while (true) {
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
auto current = std::make_shared<VectorVfsDirectory>(
@@ -97,7 +100,7 @@ void ProcessDirectory(VirtualFile file, size_t dir_offset, size_t file_offset, s
}
}
-VirtualDir ExtractRomFS(VirtualFile file) {
+VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
RomFSHeader header{};
if (file->ReadObject(&header) != sizeof(RomFSHeader))
return nullptr;
@@ -116,9 +119,22 @@ VirtualDir ExtractRomFS(VirtualFile file) {
VirtualDir out = std::move(root);
- while (out->GetSubdirectory("") != nullptr)
- out = out->GetSubdirectory("");
+ while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
+ if (out->GetSubdirectories().front()->GetName() == "data" &&
+ type == RomFSExtractionType::Truncated)
+ break;
+ out = out->GetSubdirectories().front();
+ }
return out;
}
+
+VirtualFile CreateRomFS(VirtualDir dir) {
+ if (dir == nullptr)
+ return nullptr;
+
+ RomFSBuildContext ctx{dir};
+ return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName());
+}
+
} // namespace FileSys
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
index e54a7d7a9..ecd1eb725 100644
--- a/src/core/file_sys/romfs.h
+++ b/src/core/file_sys/romfs.h
@@ -5,6 +5,7 @@
#pragma once
#include <array>
+#include <map>
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
@@ -12,6 +13,8 @@
namespace FileSys {
+struct RomFSHeader;
+
struct IVFCLevel {
u64_le offset;
u64_le size;
@@ -29,8 +32,18 @@ struct IVFCHeader {
};
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
+enum class RomFSExtractionType {
+ Full, // Includes data directory
+ Truncated, // Traverses into data directory
+};
+
// Converts a RomFS binary blob to VFS Filesystem
// Returns nullptr on failure
-VirtualDir ExtractRomFS(VirtualFile file);
+VirtualDir ExtractRomFS(VirtualFile file,
+ RomFSExtractionType type = RomFSExtractionType::Truncated);
+
+// Converts a VFS filesystem into a RomFS binary
+// Returns nullptr on failure
+VirtualFile CreateRomFS(VirtualDir dir);
} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index d9d90939e..d027a8d59 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -28,11 +28,13 @@ RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
ivfc_offset = app_loader.ReadRomFSIVFCOffset();
}
+RomFSFactory::~RomFSFactory() = default;
+
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess() {
if (!updatable)
return MakeResult<VirtualFile>(file);
- const PatchManager patch_manager(Core::CurrentProcess()->program_id);
+ const PatchManager patch_manager(Core::CurrentProcess()->GetTitleID());
return MakeResult<VirtualFile>(patch_manager.PatchRomFS(file, ivfc_offset));
}
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index 26b8f46cc..2cace8180 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -30,6 +30,7 @@ enum class StorageId : u8 {
class RomFSFactory {
public:
explicit RomFSFactory(Loader::AppLoader& app_loader);
+ ~RomFSFactory();
ResultVal<VirtualFile> OpenCurrentProcess();
ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type);
diff --git a/src/core/file_sys/savedata_factory.cpp b/src/core/file_sys/savedata_factory.cpp
index e437d34e5..47f2ab9e0 100644
--- a/src/core/file_sys/savedata_factory.cpp
+++ b/src/core/file_sys/savedata_factory.cpp
@@ -20,6 +20,8 @@ std::string SaveDataDescriptor::DebugInfo() const {
SaveDataFactory::SaveDataFactory(VirtualDir save_directory) : dir(std::move(save_directory)) {}
+SaveDataFactory::~SaveDataFactory() = default;
+
ResultVal<VirtualDir> SaveDataFactory::Open(SaveDataSpaceId space, SaveDataDescriptor meta) {
if (meta.type == SaveDataType::SystemSaveData || meta.type == SaveDataType::SaveData) {
if (meta.zero_1 != 0) {
@@ -79,16 +81,16 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
// According to switchbrew, if a save is of type SaveData and the title id field is 0, it should
// be interpreted as the title id of the current process.
if (type == SaveDataType::SaveData && title_id == 0)
- title_id = Core::CurrentProcess()->program_id;
+ title_id = Core::CurrentProcess()->GetTitleID();
std::string out;
switch (space) {
case SaveDataSpaceId::NandSystem:
- out = "/system/save/";
+ out = "/system/";
break;
case SaveDataSpaceId::NandUser:
- out = "/user/save/";
+ out = "/user/";
break;
default:
ASSERT_MSG(false, "Unrecognized SaveDataSpaceId: {:02X}", static_cast<u8>(space));
@@ -96,9 +98,12 @@ std::string SaveDataFactory::GetFullPath(SaveDataSpaceId space, SaveDataType typ
switch (type) {
case SaveDataType::SystemSaveData:
- return fmt::format("{}{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
+ return fmt::format("{}save/{:016X}/{:016X}{:016X}", out, save_id, user_id[1], user_id[0]);
case SaveDataType::SaveData:
- return fmt::format("{}{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
+ return fmt::format("{}save/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
+ title_id);
+ case SaveDataType::TemporaryStorage:
+ return fmt::format("{}temp/{:016X}/{:016X}{:016X}/{:016X}", out, 0, user_id[1], user_id[0],
title_id);
default:
ASSERT_MSG(false, "Unrecognized SaveDataType: {:02X}", static_cast<u8>(type));
diff --git a/src/core/file_sys/savedata_factory.h b/src/core/file_sys/savedata_factory.h
index ba978695b..d69ef6741 100644
--- a/src/core/file_sys/savedata_factory.h
+++ b/src/core/file_sys/savedata_factory.h
@@ -48,6 +48,7 @@ static_assert(sizeof(SaveDataDescriptor) == 0x40, "SaveDataDescriptor has incorr
class SaveDataFactory {
public:
explicit SaveDataFactory(VirtualDir dir);
+ ~SaveDataFactory();
ResultVal<VirtualDir> Open(SaveDataSpaceId space, SaveDataDescriptor meta);
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index 1120a4920..e85a2b76e 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -24,7 +24,7 @@ enum class ContentRecordType : u8;
class NSP : public ReadOnlyVfsDirectory {
public:
explicit NSP(VirtualFile file);
- ~NSP();
+ ~NSP() override;
Loader::ResultStatus GetStatus() const;
Loader::ResultStatus GetProgramStatus(u64 title_id) const;
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index 146c839f4..bfe50da73 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -167,18 +167,18 @@ std::string VfsFile::GetExtension() const {
VfsDirectory::~VfsDirectory() = default;
-boost::optional<u8> VfsFile::ReadByte(size_t offset) const {
+boost::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
u8 out{};
- size_t size = Read(&out, 1, offset);
+ std::size_t size = Read(&out, 1, offset);
if (size == 1)
return out;
return boost::none;
}
-std::vector<u8> VfsFile::ReadBytes(size_t size, size_t offset) const {
+std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
std::vector<u8> out(size);
- size_t read_size = Read(out.data(), size, offset);
+ std::size_t read_size = Read(out.data(), size, offset);
out.resize(read_size);
return out;
}
@@ -187,11 +187,11 @@ std::vector<u8> VfsFile::ReadAllBytes() const {
return ReadBytes(GetSize());
}
-bool VfsFile::WriteByte(u8 data, size_t offset) {
+bool VfsFile::WriteByte(u8 data, std::size_t offset) {
return Write(&data, 1, offset) == 1;
}
-size_t VfsFile::WriteBytes(const std::vector<u8>& data, size_t offset) {
+std::size_t VfsFile::WriteBytes(const std::vector<u8>& data, std::size_t offset) {
return Write(data.data(), data.size(), offset);
}
@@ -215,7 +215,7 @@ std::shared_ptr<VfsFile> VfsDirectory::GetFileRelative(std::string_view path) co
}
auto dir = GetSubdirectory(vec[0]);
- for (size_t component = 1; component < vec.size() - 1; ++component) {
+ for (std::size_t component = 1; component < vec.size() - 1; ++component) {
if (dir == nullptr) {
return nullptr;
}
@@ -249,7 +249,7 @@ std::shared_ptr<VfsDirectory> VfsDirectory::GetDirectoryRelative(std::string_vie
}
auto dir = GetSubdirectory(vec[0]);
- for (size_t component = 1; component < vec.size(); ++component) {
+ for (std::size_t component = 1; component < vec.size(); ++component) {
if (dir == nullptr) {
return nullptr;
}
@@ -286,7 +286,7 @@ bool VfsDirectory::IsRoot() const {
return GetParentDirectory() == nullptr;
}
-size_t VfsDirectory::GetSize() const {
+std::size_t VfsDirectory::GetSize() const {
const auto& files = GetFiles();
const auto sum_sizes = [](const auto& range) {
return std::accumulate(range.begin(), range.end(), 0ULL,
@@ -399,6 +399,15 @@ bool VfsDirectory::Copy(std::string_view src, std::string_view dest) {
return f2->WriteBytes(f1->ReadAllBytes()) == f1->GetSize();
}
+std::map<std::string, VfsEntryType, std::less<>> VfsDirectory::GetEntries() const {
+ std::map<std::string, VfsEntryType, std::less<>> out;
+ for (const auto& dir : GetSubdirectories())
+ out.emplace(dir->GetName(), VfsEntryType::Directory);
+ for (const auto& file : GetFiles())
+ out.emplace(file->GetName(), VfsEntryType::File);
+ return out;
+}
+
std::string VfsDirectory::GetFullPath() const {
if (IsRoot())
return GetName();
@@ -434,13 +443,13 @@ bool ReadOnlyVfsDirectory::Rename(std::string_view name) {
return false;
}
-bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size) {
+bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t block_size) {
if (file1->GetSize() != file2->GetSize())
return false;
std::vector<u8> f1_v(block_size);
std::vector<u8> f2_v(block_size);
- for (size_t i = 0; i < file1->GetSize(); i += block_size) {
+ for (std::size_t i = 0; i < file1->GetSize(); i += block_size) {
auto f1_vs = file1->Read(f1_v.data(), block_size, i);
auto f2_vs = file2->Read(f2_v.data(), block_size, i);
@@ -454,13 +463,41 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block
return true;
}
-bool VfsRawCopy(VirtualFile src, VirtualFile dest) {
- if (src == nullptr || dest == nullptr)
+bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) {
+ if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
return false;
if (!dest->Resize(src->GetSize()))
return false;
- std::vector<u8> data = src->ReadAllBytes();
- return dest->WriteBytes(data, 0) == data.size();
+
+ std::vector<u8> temp(std::min(block_size, src->GetSize()));
+ for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
+ const auto read = std::min(block_size, src->GetSize() - i);
+ const auto block = src->Read(temp.data(), read, i);
+
+ if (dest->Write(temp.data(), read, i) != read)
+ return false;
+ }
+
+ return true;
+}
+
+bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) {
+ if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
+ return false;
+
+ for (const auto& file : src->GetFiles()) {
+ const auto out = dest->CreateFile(file->GetName());
+ if (!VfsRawCopy(file, out, block_size))
+ return false;
+ }
+
+ for (const auto& dir : src->GetSubdirectories()) {
+ const auto out = dest->CreateSubdirectory(dir->GetName());
+ if (!VfsRawCopyD(dir, out, block_size))
+ return false;
+ }
+
+ return true;
}
VirtualDir GetOrCreateDirectoryRelative(const VirtualDir& rel, std::string_view path) {
diff --git a/src/core/file_sys/vfs.h b/src/core/file_sys/vfs.h
index 5142a3e86..270291631 100644
--- a/src/core/file_sys/vfs.h
+++ b/src/core/file_sys/vfs.h
@@ -4,6 +4,7 @@
#pragma once
+#include <map>
#include <memory>
#include <string>
#include <string_view>
@@ -92,9 +93,9 @@ public:
// Retrieves the extension of the file name.
virtual std::string GetExtension() const;
// Retrieves the size of the file.
- virtual size_t GetSize() const = 0;
+ virtual std::size_t GetSize() const = 0;
// Resizes the file to new_size. Returns whether or not the operation was successful.
- virtual bool Resize(size_t new_size) = 0;
+ virtual bool Resize(std::size_t new_size) = 0;
// Gets a pointer to the directory containing this file, returning nullptr if there is none.
virtual std::shared_ptr<VfsDirectory> GetContainingDirectory() const = 0;
@@ -105,15 +106,15 @@ public:
// The primary method of reading from the file. Reads length bytes into data starting at offset
// into file. Returns number of bytes successfully read.
- virtual size_t Read(u8* data, size_t length, size_t offset = 0) const = 0;
+ virtual std::size_t Read(u8* data, std::size_t length, std::size_t offset = 0) const = 0;
// The primary method of writing to the file. Writes length bytes from data starting at offset
// into file. Returns number of bytes successfully written.
- virtual size_t Write(const u8* data, size_t length, size_t offset = 0) = 0;
+ virtual std::size_t Write(const u8* data, std::size_t length, std::size_t offset = 0) = 0;
// Reads exactly one byte at the offset provided, returning boost::none on error.
- virtual boost::optional<u8> ReadByte(size_t offset = 0) const;
+ virtual boost::optional<u8> ReadByte(std::size_t offset = 0) const;
// Reads size bytes starting at offset in file into a vector.
- virtual std::vector<u8> ReadBytes(size_t size, size_t offset = 0) const;
+ virtual std::vector<u8> ReadBytes(std::size_t size, std::size_t offset = 0) const;
// Reads all the bytes from the file into a vector. Equivalent to 'file->Read(file->GetSize(),
// 0)'
virtual std::vector<u8> ReadAllBytes() const;
@@ -121,7 +122,7 @@ public:
// Reads an array of type T, size number_elements starting at offset.
// Returns the number of bytes (sizeof(T)*number_elements) read successfully.
template <typename T>
- size_t ReadArray(T* data, size_t number_elements, size_t offset = 0) const {
+ std::size_t ReadArray(T* data, std::size_t number_elements, std::size_t offset = 0) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), number_elements * sizeof(T), offset);
@@ -130,7 +131,7 @@ public:
// Reads size bytes into the memory starting at data starting at offset into the file.
// Returns the number of bytes read successfully.
template <typename T>
- size_t ReadBytes(T* data, size_t size, size_t offset = 0) const {
+ std::size_t ReadBytes(T* data, std::size_t size, std::size_t offset = 0) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), size, offset);
}
@@ -138,22 +139,22 @@ public:
// Reads one object of type T starting at offset in file.
// Returns the number of bytes read successfully (sizeof(T)).
template <typename T>
- size_t ReadObject(T* data, size_t offset = 0) const {
+ std::size_t ReadObject(T* data, std::size_t offset = 0) const {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Read(reinterpret_cast<u8*>(data), sizeof(T), offset);
}
// Writes exactly one byte to offset in file and retuns whether or not the byte was written
// successfully.
- virtual bool WriteByte(u8 data, size_t offset = 0);
+ virtual bool WriteByte(u8 data, std::size_t offset = 0);
// Writes a vector of bytes to offset in file and returns the number of bytes successfully
// written.
- virtual size_t WriteBytes(const std::vector<u8>& data, size_t offset = 0);
+ virtual std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset = 0);
// Writes an array of type T, size number_elements to offset in file.
// Returns the number of bytes (sizeof(T)*number_elements) written successfully.
template <typename T>
- size_t WriteArray(const T* data, size_t number_elements, size_t offset = 0) {
+ std::size_t WriteArray(const T* data, std::size_t number_elements, std::size_t offset = 0) {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(data, number_elements * sizeof(T), offset);
}
@@ -161,7 +162,7 @@ public:
// Writes size bytes starting at memory location data to offset in file.
// Returns the number of bytes written successfully.
template <typename T>
- size_t WriteBytes(const T* data, size_t size, size_t offset = 0) {
+ std::size_t WriteBytes(const T* data, std::size_t size, std::size_t offset = 0) {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(reinterpret_cast<const u8*>(data), size, offset);
}
@@ -169,7 +170,7 @@ public:
// Writes one object of type T to offset in file.
// Returns the number of bytes written successfully (sizeof(T)).
template <typename T>
- size_t WriteObject(const T& data, size_t offset = 0) {
+ std::size_t WriteObject(const T& data, std::size_t offset = 0) {
static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable.");
return Write(&data, sizeof(T), offset);
}
@@ -221,7 +222,7 @@ public:
// Returns the name of the directory.
virtual std::string GetName() const = 0;
// Returns the total size of all files and subdirectories in this directory.
- virtual size_t GetSize() const;
+ virtual std::size_t GetSize() const;
// Returns the parent directory of this directory. Returns nullptr if this directory is root or
// has no parent.
virtual std::shared_ptr<VfsDirectory> GetParentDirectory() const = 0;
@@ -265,6 +266,10 @@ public:
// dest.
virtual bool Copy(std::string_view src, std::string_view dest);
+ // Gets all of the entries directly in the directory (files and dirs), returning a map between
+ // item name -> type.
+ virtual std::map<std::string, VfsEntryType, std::less<>> GetEntries() const;
+
// Interprets the file with name file instead as a directory of type directory.
// The directory must have a constructor that takes a single argument of type
// std::shared_ptr<VfsFile>. Allows to reinterpret container files (i.e NCA, zip, XCI, etc) as a
@@ -310,13 +315,19 @@ public:
bool Rename(std::string_view name) override;
};
-// Compare the two files, byte-for-byte, in increments specificed by block_size
-bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x200);
+// Compare the two files, byte-for-byte, in increments specified by block_size
+bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2,
+ std::size_t block_size = 0x1000);
// A method that copies the raw data between two different implementations of VirtualFile. If you
// are using the same implementation, it is probably better to use the Copy method in the parent
// directory of src/dest.
-bool VfsRawCopy(VirtualFile src, VirtualFile dest);
+bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size = 0x1000);
+
+// A method that performs a similar function to VfsRawCopy above, but instead copies entire
+// directories. It suffers the same performance penalties as above and an implementation-specific
+// Copy should always be preferred.
+bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size = 0x1000);
// Checks if the directory at path relative to rel exists. If it does, returns that. If it does not
// it attempts to create it and returns the new dir or nullptr on failure.
diff --git a/src/core/file_sys/vfs_concat.cpp b/src/core/file_sys/vfs_concat.cpp
index e6bf586a3..16d801c0c 100644
--- a/src/core/file_sys/vfs_concat.cpp
+++ b/src/core/file_sys/vfs_concat.cpp
@@ -5,28 +5,75 @@
#include <algorithm>
#include <utility>
+#include "common/assert.h"
#include "core/file_sys/vfs_concat.h"
+#include "core/file_sys/vfs_static.h"
namespace FileSys {
-VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name) {
- if (files.empty())
- return nullptr;
- if (files.size() == 1)
- return files[0];
+static bool VerifyConcatenationMapContinuity(const std::map<u64, VirtualFile>& map) {
+ const auto last_valid = --map.end();
+ for (auto iter = map.begin(); iter != last_valid;) {
+ const auto old = iter++;
+ if (old->first + old->second->GetSize() != iter->first) {
+ return false;
+ }
+ }
- return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
+ return map.begin()->first == 0;
}
ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name)
: name(std::move(name)) {
- size_t next_offset = 0;
+ std::size_t next_offset = 0;
for (const auto& file : files_) {
files[next_offset] = file;
next_offset += file->GetSize();
}
}
+ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std::string name)
+ : files(std::move(files_)), name(std::move(name)) {
+ ASSERT(VerifyConcatenationMapContinuity(files));
+}
+
+ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
+
+VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(std::vector<VirtualFile> files,
+ std::string name) {
+ if (files.empty())
+ return nullptr;
+ if (files.size() == 1)
+ return files[0];
+
+ return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
+}
+
+VirtualFile ConcatenatedVfsFile::MakeConcatenatedFile(u8 filler_byte,
+ std::map<u64, VirtualFile> files,
+ std::string name) {
+ if (files.empty())
+ return nullptr;
+ if (files.size() == 1)
+ return files.begin()->second;
+
+ const auto last_valid = --files.end();
+ for (auto iter = files.begin(); iter != last_valid;) {
+ const auto old = iter++;
+ if (old->first + old->second->GetSize() != iter->first) {
+ files.emplace(old->first + old->second->GetSize(),
+ std::make_shared<StaticVfsFile>(filler_byte, iter->first - old->first -
+ old->second->GetSize()));
+ }
+ }
+
+ // Ensure the map starts at offset 0 (start of file), otherwise pad to fill.
+ if (files.begin()->first != 0)
+ files.emplace(0, std::make_shared<StaticVfsFile>(filler_byte, files.begin()->first));
+
+ return std::shared_ptr<VfsFile>(new ConcatenatedVfsFile(std::move(files), std::move(name)));
+}
+
std::string ConcatenatedVfsFile::GetName() const {
if (files.empty())
return "";
@@ -35,13 +82,13 @@ std::string ConcatenatedVfsFile::GetName() const {
return files.begin()->second->GetName();
}
-size_t ConcatenatedVfsFile::GetSize() const {
+std::size_t ConcatenatedVfsFile::GetSize() const {
if (files.empty())
return 0;
return files.rbegin()->first + files.rbegin()->second->GetSize();
}
-bool ConcatenatedVfsFile::Resize(size_t new_size) {
+bool ConcatenatedVfsFile::Resize(std::size_t new_size) {
return false;
}
@@ -59,8 +106,8 @@ bool ConcatenatedVfsFile::IsReadable() const {
return true;
}
-size_t ConcatenatedVfsFile::Read(u8* data, size_t length, size_t offset) const {
- auto entry = files.end();
+std::size_t ConcatenatedVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
+ auto entry = --files.end();
for (auto iter = files.begin(); iter != files.end(); ++iter) {
if (iter->first > offset) {
entry = --iter;
@@ -68,27 +115,25 @@ 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;
-
- if (entry == files.end())
+ if (entry->first + entry->second->GetSize() <= offset)
return 0;
- const auto remaining = entry->second->GetSize() + offset - entry->first;
- if (length > remaining) {
- return entry->second->Read(data, remaining, offset - entry->first) +
- Read(data + remaining, length - remaining, offset + remaining);
+ const auto read_in =
+ std::min<u64>(entry->first + entry->second->GetSize() - offset, entry->second->GetSize());
+ if (length > read_in) {
+ return entry->second->Read(data, read_in, offset - entry->first) +
+ Read(data + read_in, length - read_in, offset + read_in);
}
- return entry->second->Read(data, length, offset - entry->first);
+ return entry->second->Read(data, std::min<u64>(read_in, length), offset - entry->first);
}
-size_t ConcatenatedVfsFile::Write(const u8* data, size_t length, size_t offset) {
+std::size_t ConcatenatedVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
return 0;
}
bool ConcatenatedVfsFile::Rename(std::string_view name) {
return false;
}
+
} // namespace FileSys
diff --git a/src/core/file_sys/vfs_concat.h b/src/core/file_sys/vfs_concat.h
index 686d32515..c90f9d5d1 100644
--- a/src/core/file_sys/vfs_concat.h
+++ b/src/core/file_sys/vfs_concat.h
@@ -4,37 +4,43 @@
#pragma once
+#include <map>
#include <memory>
#include <string_view>
-#include <boost/container/flat_map.hpp>
#include "core/file_sys/vfs.h"
namespace FileSys {
-// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
-VirtualFile ConcatenateFiles(std::vector<VirtualFile> 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<VirtualFile> files, std::string name);
-
ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
+ ConcatenatedVfsFile(std::map<u64, VirtualFile> files, std::string name);
public:
+ ~ConcatenatedVfsFile() override;
+
+ /// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
+ static VirtualFile MakeConcatenatedFile(std::vector<VirtualFile> files, std::string name);
+
+ /// Convenience function that turns a map of offsets to files into a concatenated file, filling
+ /// gaps with a given filler byte.
+ static VirtualFile MakeConcatenatedFile(u8 filler_byte, std::map<u64, VirtualFile> files,
+ std::string name);
+
std::string GetName() const override;
- size_t GetSize() const override;
- bool Resize(size_t new_size) override;
+ std::size_t GetSize() const override;
+ bool Resize(std::size_t new_size) override;
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
- size_t Read(u8* data, size_t length, size_t offset) const override;
- size_t Write(const u8* data, size_t length, size_t offset) override;
+ std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
+ std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
bool Rename(std::string_view name) override;
private:
// Maps starting offset to file -- more efficient.
- boost::container::flat_map<u64, VirtualFile> files;
+ std::map<u64, VirtualFile> files;
std::string name;
};
diff --git a/src/core/file_sys/vfs_layered.cpp b/src/core/file_sys/vfs_layered.cpp
new file mode 100644
index 000000000..bfee01725
--- /dev/null
+++ b/src/core/file_sys/vfs_layered.cpp
@@ -0,0 +1,132 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <utility>
+#include "core/file_sys/vfs_layered.h"
+
+namespace FileSys {
+
+LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name)
+ : dirs(std::move(dirs)), name(std::move(name)) {}
+
+LayeredVfsDirectory::~LayeredVfsDirectory() = default;
+
+VirtualDir LayeredVfsDirectory::MakeLayeredDirectory(std::vector<VirtualDir> dirs,
+ std::string name) {
+ if (dirs.empty())
+ return nullptr;
+ if (dirs.size() == 1)
+ return dirs[0];
+
+ return std::shared_ptr<VfsDirectory>(new LayeredVfsDirectory(std::move(dirs), std::move(name)));
+}
+
+std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
+ for (const auto& layer : dirs) {
+ const auto file = layer->GetFileRelative(path);
+ if (file != nullptr)
+ return file;
+ }
+
+ return nullptr;
+}
+
+std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetDirectoryRelative(
+ std::string_view path) const {
+ std::vector<VirtualDir> out;
+ for (const auto& layer : dirs) {
+ auto dir = layer->GetDirectoryRelative(path);
+ if (dir != nullptr)
+ out.push_back(std::move(dir));
+ }
+
+ return MakeLayeredDirectory(std::move(out));
+}
+
+std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const {
+ return GetFileRelative(name);
+}
+
+std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetSubdirectory(std::string_view name) const {
+ return GetDirectoryRelative(name);
+}
+
+std::string LayeredVfsDirectory::GetFullPath() const {
+ return dirs[0]->GetFullPath();
+}
+
+std::vector<std::shared_ptr<VfsFile>> LayeredVfsDirectory::GetFiles() const {
+ std::vector<VirtualFile> out;
+ for (const auto& layer : dirs) {
+ for (const auto& file : layer->GetFiles()) {
+ if (std::find_if(out.begin(), out.end(), [&file](const VirtualFile& comp) {
+ return comp->GetName() == file->GetName();
+ }) == out.end()) {
+ out.push_back(file);
+ }
+ }
+ }
+
+ return out;
+}
+
+std::vector<std::shared_ptr<VfsDirectory>> LayeredVfsDirectory::GetSubdirectories() const {
+ std::vector<std::string> names;
+ for (const auto& layer : dirs) {
+ for (const auto& sd : layer->GetSubdirectories()) {
+ if (std::find(names.begin(), names.end(), sd->GetName()) == names.end())
+ names.push_back(sd->GetName());
+ }
+ }
+
+ std::vector<VirtualDir> out;
+ out.reserve(names.size());
+ for (const auto& subdir : names)
+ out.push_back(GetSubdirectory(subdir));
+
+ return out;
+}
+
+bool LayeredVfsDirectory::IsWritable() const {
+ return false;
+}
+
+bool LayeredVfsDirectory::IsReadable() const {
+ return true;
+}
+
+std::string LayeredVfsDirectory::GetName() const {
+ return name.empty() ? dirs[0]->GetName() : name;
+}
+
+std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetParentDirectory() const {
+ return dirs[0]->GetParentDirectory();
+}
+
+std::shared_ptr<VfsDirectory> LayeredVfsDirectory::CreateSubdirectory(std::string_view name) {
+ return nullptr;
+}
+
+std::shared_ptr<VfsFile> LayeredVfsDirectory::CreateFile(std::string_view name) {
+ return nullptr;
+}
+
+bool LayeredVfsDirectory::DeleteSubdirectory(std::string_view name) {
+ return false;
+}
+
+bool LayeredVfsDirectory::DeleteFile(std::string_view name) {
+ return false;
+}
+
+bool LayeredVfsDirectory::Rename(std::string_view name_) {
+ name = name_;
+ return true;
+}
+
+bool LayeredVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
+ return false;
+}
+} // namespace FileSys
diff --git a/src/core/file_sys/vfs_layered.h b/src/core/file_sys/vfs_layered.h
new file mode 100644
index 000000000..d85310f57
--- /dev/null
+++ b/src/core/file_sys/vfs_layered.h
@@ -0,0 +1,50 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "core/file_sys/vfs.h"
+
+namespace FileSys {
+
+// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first
+// one and falling back to the one after. The highest priority directory (overwrites all others)
+// should be element 0 in the dirs vector.
+class LayeredVfsDirectory : public VfsDirectory {
+ LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name);
+
+public:
+ ~LayeredVfsDirectory() override;
+
+ /// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
+ static VirtualDir MakeLayeredDirectory(std::vector<VirtualDir> dirs, std::string name = "");
+
+ std::shared_ptr<VfsFile> GetFileRelative(std::string_view path) const override;
+ std::shared_ptr<VfsDirectory> GetDirectoryRelative(std::string_view path) const override;
+ std::shared_ptr<VfsFile> GetFile(std::string_view name) const override;
+ std::shared_ptr<VfsDirectory> GetSubdirectory(std::string_view name) const override;
+ std::string GetFullPath() const override;
+
+ std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
+ std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
+ bool IsWritable() const override;
+ bool IsReadable() const override;
+ std::string GetName() const override;
+ std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
+ std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
+ std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
+ bool DeleteSubdirectory(std::string_view name) override;
+ bool DeleteFile(std::string_view name) override;
+ bool Rename(std::string_view name) override;
+
+protected:
+ bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
+
+private:
+ std::vector<VirtualDir> dirs;
+ std::string name;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index 847cde2f5..a4c6719a0 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -9,20 +9,22 @@
namespace FileSys {
-OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, size_t size_, size_t offset_,
+OffsetVfsFile::OffsetVfsFile(std::shared_ptr<VfsFile> file_, std::size_t size_, std::size_t offset_,
std::string name_, VirtualDir parent_)
: file(file_), offset(offset_), size(size_), name(std::move(name_)),
parent(parent_ == nullptr ? file->GetContainingDirectory() : std::move(parent_)) {}
+OffsetVfsFile::~OffsetVfsFile() = default;
+
std::string OffsetVfsFile::GetName() const {
return name.empty() ? file->GetName() : name;
}
-size_t OffsetVfsFile::GetSize() const {
+std::size_t OffsetVfsFile::GetSize() const {
return size;
}
-bool OffsetVfsFile::Resize(size_t new_size) {
+bool OffsetVfsFile::Resize(std::size_t new_size) {
if (offset + new_size < file->GetSize()) {
size = new_size;
} else {
@@ -47,22 +49,22 @@ bool OffsetVfsFile::IsReadable() const {
return file->IsReadable();
}
-size_t OffsetVfsFile::Read(u8* data, size_t length, size_t r_offset) const {
+std::size_t OffsetVfsFile::Read(u8* data, std::size_t length, std::size_t r_offset) const {
return file->Read(data, TrimToFit(length, r_offset), offset + r_offset);
}
-size_t OffsetVfsFile::Write(const u8* data, size_t length, size_t r_offset) {
+std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t r_offset) {
return file->Write(data, TrimToFit(length, r_offset), offset + r_offset);
}
-boost::optional<u8> OffsetVfsFile::ReadByte(size_t r_offset) const {
+boost::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
if (r_offset < size)
return file->ReadByte(offset + r_offset);
return boost::none;
}
-std::vector<u8> OffsetVfsFile::ReadBytes(size_t r_size, size_t r_offset) const {
+std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {
return file->ReadBytes(TrimToFit(r_size, r_offset), offset + r_offset);
}
@@ -70,14 +72,14 @@ std::vector<u8> OffsetVfsFile::ReadAllBytes() const {
return file->ReadBytes(size, offset);
}
-bool OffsetVfsFile::WriteByte(u8 data, size_t r_offset) {
+bool OffsetVfsFile::WriteByte(u8 data, std::size_t r_offset) {
if (r_offset < size)
return file->WriteByte(data, offset + r_offset);
return false;
}
-size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, size_t r_offset) {
+std::size_t OffsetVfsFile::WriteBytes(const std::vector<u8>& data, std::size_t r_offset) {
return file->Write(data.data(), TrimToFit(data.size(), r_offset), offset + r_offset);
}
@@ -85,12 +87,12 @@ bool OffsetVfsFile::Rename(std::string_view name) {
return file->Rename(name);
}
-size_t OffsetVfsFile::GetOffset() const {
+std::size_t OffsetVfsFile::GetOffset() const {
return offset;
}
-size_t OffsetVfsFile::TrimToFit(size_t r_size, size_t r_offset) const {
- return std::clamp(r_size, size_t{0}, size - r_offset);
+std::size_t OffsetVfsFile::TrimToFit(std::size_t r_size, std::size_t r_offset) const {
+ return std::clamp(r_size, std::size_t{0}, size - r_offset);
}
} // namespace FileSys
diff --git a/src/core/file_sys/vfs_offset.h b/src/core/file_sys/vfs_offset.h
index cb92d1570..8062702a7 100644
--- a/src/core/file_sys/vfs_offset.h
+++ b/src/core/file_sys/vfs_offset.h
@@ -17,33 +17,34 @@ namespace FileSys {
// the size of this wrapper.
class OffsetVfsFile : public VfsFile {
public:
- OffsetVfsFile(std::shared_ptr<VfsFile> file, size_t size, size_t offset = 0,
+ OffsetVfsFile(std::shared_ptr<VfsFile> file, std::size_t size, std::size_t offset = 0,
std::string new_name = "", VirtualDir new_parent = nullptr);
+ ~OffsetVfsFile() override;
std::string GetName() const override;
- size_t GetSize() const override;
- bool Resize(size_t new_size) override;
+ std::size_t GetSize() const override;
+ bool Resize(std::size_t new_size) override;
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
- size_t Read(u8* data, size_t length, size_t offset) const override;
- size_t Write(const u8* data, size_t length, size_t offset) override;
- boost::optional<u8> ReadByte(size_t offset) const override;
- std::vector<u8> ReadBytes(size_t size, size_t offset) const override;
+ std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
+ std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
+ boost::optional<u8> ReadByte(std::size_t offset) const override;
+ std::vector<u8> ReadBytes(std::size_t size, std::size_t offset) const override;
std::vector<u8> ReadAllBytes() const override;
- bool WriteByte(u8 data, size_t offset) override;
- size_t WriteBytes(const std::vector<u8>& data, size_t offset) override;
+ bool WriteByte(u8 data, std::size_t offset) override;
+ std::size_t WriteBytes(const std::vector<u8>& data, std::size_t offset) override;
bool Rename(std::string_view name) override;
- size_t GetOffset() const;
+ std::size_t GetOffset() const;
private:
- size_t TrimToFit(size_t r_size, size_t r_offset) const;
+ std::size_t TrimToFit(std::size_t r_size, std::size_t r_offset) const;
std::shared_ptr<VfsFile> file;
- size_t offset;
- size_t size;
+ std::size_t offset;
+ std::size_t size;
std::string name;
VirtualDir parent;
};
diff --git a/src/core/file_sys/vfs_real.cpp b/src/core/file_sys/vfs_real.cpp
index 89b101145..9defad04c 100644
--- a/src/core/file_sys/vfs_real.cpp
+++ b/src/core/file_sys/vfs_real.cpp
@@ -227,11 +227,11 @@ std::string RealVfsFile::GetName() const {
return path_components.back();
}
-size_t RealVfsFile::GetSize() const {
+std::size_t RealVfsFile::GetSize() const {
return backing->GetSize();
}
-bool RealVfsFile::Resize(size_t new_size) {
+bool RealVfsFile::Resize(std::size_t new_size) {
return backing->Resize(new_size);
}
@@ -247,13 +247,13 @@ bool RealVfsFile::IsReadable() const {
return (perms & Mode::ReadWrite) != 0;
}
-size_t RealVfsFile::Read(u8* data, size_t length, size_t offset) const {
+std::size_t RealVfsFile::Read(u8* data, std::size_t length, std::size_t offset) const {
if (!backing->Seek(offset, SEEK_SET))
return 0;
return backing->ReadBytes(data, length);
}
-size_t RealVfsFile::Write(const u8* data, size_t length, size_t offset) {
+std::size_t RealVfsFile::Write(const u8* data, std::size_t length, std::size_t offset) {
if (!backing->Seek(offset, SEEK_SET))
return 0;
return backing->WriteBytes(data, length);
@@ -413,6 +413,23 @@ std::string RealVfsDirectory::GetFullPath() const {
return out;
}
+std::map<std::string, VfsEntryType, std::less<>> RealVfsDirectory::GetEntries() const {
+ if (perms == Mode::Append)
+ return {};
+
+ std::map<std::string, VfsEntryType, std::less<>> out;
+ FileUtil::ForeachDirectoryEntry(
+ nullptr, path,
+ [&out](u64* entries_out, const std::string& directory, const std::string& filename) {
+ const std::string full_path = directory + DIR_SEP + filename;
+ out.emplace(filename, FileUtil::IsDirectory(full_path) ? VfsEntryType::Directory
+ : VfsEntryType::File);
+ return true;
+ });
+
+ return out;
+}
+
bool RealVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
return false;
}
diff --git a/src/core/file_sys/vfs_real.h b/src/core/file_sys/vfs_real.h
index 7db86691f..5b61db90d 100644
--- a/src/core/file_sys/vfs_real.h
+++ b/src/core/file_sys/vfs_real.h
@@ -48,13 +48,13 @@ public:
~RealVfsFile() override;
std::string GetName() const override;
- size_t GetSize() const override;
- bool Resize(size_t new_size) override;
+ std::size_t GetSize() const override;
+ bool Resize(std::size_t new_size) override;
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
bool IsWritable() const override;
bool IsReadable() const override;
- size_t Read(u8* data, size_t length, size_t offset) const override;
- size_t Write(const u8* data, size_t length, size_t offset) override;
+ std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
+ std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
bool Rename(std::string_view name) override;
private:
@@ -98,6 +98,7 @@ public:
bool DeleteFile(std::string_view name) override;
bool Rename(std::string_view name) override;
std::string GetFullPath() const override;
+ std::map<std::string, VfsEntryType, std::less<>> GetEntries() const override;
protected:
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h
new file mode 100644
index 000000000..44fab51d1
--- /dev/null
+++ b/src/core/file_sys/vfs_static.h
@@ -0,0 +1,79 @@
+// Copyright 2018 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <algorithm>
+#include <memory>
+#include <string_view>
+
+#include "core/file_sys/vfs.h"
+
+namespace FileSys {
+
+class StaticVfsFile : public VfsFile {
+public:
+ explicit StaticVfsFile(u8 value, std::size_t size = 0, std::string name = "",
+ VirtualDir parent = nullptr)
+ : value{value}, size{size}, name{std::move(name)}, parent{std::move(parent)} {}
+
+ std::string GetName() const override {
+ return name;
+ }
+
+ std::size_t GetSize() const override {
+ return size;
+ }
+
+ bool Resize(std::size_t new_size) override {
+ size = new_size;
+ return true;
+ }
+
+ std::shared_ptr<VfsDirectory> GetContainingDirectory() const override {
+ return parent;
+ }
+
+ bool IsWritable() const override {
+ return false;
+ }
+
+ bool IsReadable() const override {
+ return true;
+ }
+
+ std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override {
+ const auto read = std::min(length, size - offset);
+ std::fill(data, data + read, value);
+ return read;
+ }
+
+ std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override {
+ return 0;
+ }
+
+ boost::optional<u8> ReadByte(std::size_t offset) const override {
+ if (offset < size)
+ return value;
+ return boost::none;
+ }
+
+ std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
+ const auto read = std::min(length, size - offset);
+ return std::vector<u8>(read, value);
+ }
+
+ bool Rename(std::string_view new_name) override {
+ name = new_name;
+ return true;
+ }
+
+private:
+ u8 value;
+ std::size_t size;
+ std::string name;
+ VirtualDir parent;
+};
+
+} // namespace FileSys
diff --git a/src/core/file_sys/vfs_vector.cpp b/src/core/file_sys/vfs_vector.cpp
index 98e7c4598..389c7e003 100644
--- a/src/core/file_sys/vfs_vector.cpp
+++ b/src/core/file_sys/vfs_vector.cpp
@@ -3,16 +3,72 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <cstring>
#include <utility>
#include "core/file_sys/vfs_vector.h"
namespace FileSys {
+VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent)
+ : data(std::move(initial_data)), parent(std::move(parent)), name(std::move(name)) {}
+
+VectorVfsFile::~VectorVfsFile() = default;
+
+std::string VectorVfsFile::GetName() const {
+ return name;
+}
+
+size_t VectorVfsFile::GetSize() const {
+ return data.size();
+}
+
+bool VectorVfsFile::Resize(size_t new_size) {
+ data.resize(new_size);
+ return true;
+}
+
+std::shared_ptr<VfsDirectory> VectorVfsFile::GetContainingDirectory() const {
+ return parent;
+}
+
+bool VectorVfsFile::IsWritable() const {
+ return true;
+}
+
+bool VectorVfsFile::IsReadable() const {
+ return true;
+}
+
+std::size_t VectorVfsFile::Read(u8* data_, std::size_t length, std::size_t offset) const {
+ const auto read = std::min(length, data.size() - offset);
+ std::memcpy(data_, data.data() + offset, read);
+ return read;
+}
+
+std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) {
+ if (offset + length > data.size())
+ data.resize(offset + length);
+ const auto write = std::min(length, data.size() - offset);
+ std::memcpy(data.data(), data_, write);
+ return write;
+}
+
+bool VectorVfsFile::Rename(std::string_view name_) {
+ name = name_;
+ return true;
+}
+
+void VectorVfsFile::Assign(std::vector<u8> new_data) {
+ data = std::move(new_data);
+}
+
VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
std::vector<VirtualDir> dirs_, std::string name_,
VirtualDir parent_)
: files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)),
name(std::move(name_)) {}
+VectorVfsDirectory::~VectorVfsDirectory() = default;
+
std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const {
return files;
}
diff --git a/src/core/file_sys/vfs_vector.h b/src/core/file_sys/vfs_vector.h
index 179f62e4b..48a414c98 100644
--- a/src/core/file_sys/vfs_vector.h
+++ b/src/core/file_sys/vfs_vector.h
@@ -8,6 +8,31 @@
namespace FileSys {
+// An implementation of VfsFile that is backed by a vector optionally supplied upon construction
+class VectorVfsFile : public VfsFile {
+public:
+ explicit VectorVfsFile(std::vector<u8> initial_data = {}, std::string name = "",
+ VirtualDir parent = nullptr);
+ ~VectorVfsFile() override;
+
+ std::string GetName() const override;
+ std::size_t GetSize() const override;
+ bool Resize(std::size_t new_size) override;
+ std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
+ bool IsWritable() const override;
+ bool IsReadable() const override;
+ std::size_t Read(u8* data, std::size_t length, std::size_t offset) const override;
+ std::size_t Write(const u8* data, std::size_t length, std::size_t offset) override;
+ bool Rename(std::string_view name) override;
+
+ virtual void Assign(std::vector<u8> new_data);
+
+private:
+ std::vector<u8> data;
+ VirtualDir parent;
+ std::string name;
+};
+
// An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
// Vector data is supplied upon construction.
class VectorVfsDirectory : public VfsDirectory {
@@ -15,6 +40,7 @@ public:
explicit VectorVfsDirectory(std::vector<VirtualFile> files = {},
std::vector<VirtualDir> dirs = {}, std::string name = "",
VirtualDir parent = nullptr);
+ ~VectorVfsDirectory() override;
std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index 4dbc25c55..b2b164368 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -25,14 +25,11 @@ namespace FileSys {
constexpr u64 NAX_HEADER_PADDING_SIZE = 0x4000;
template <typename SourceData, typename SourceKey, typename Destination>
-static bool CalculateHMAC256(Destination* out, const SourceKey* key, size_t key_length,
- const SourceData* data, size_t data_length) {
+static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t key_length,
+ const SourceData* data, std::size_t data_length) {
mbedtls_md_context_t context;
mbedtls_md_init(&context);
- const auto key_f = reinterpret_cast<const u8*>(key);
- const std::vector<u8> key_v(key_f, key_f + key_length);
-
if (mbedtls_md_setup(&context, mbedtls_md_info_from_type(MBEDTLS_MD_SHA256), 1) ||
mbedtls_md_hmac_starts(&context, reinterpret_cast<const u8*>(key), key_length) ||
mbedtls_md_hmac_update(&context, reinterpret_cast<const u8*>(data), data_length) ||
@@ -45,7 +42,7 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, size_t key_
return true;
}
-NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NAXHeader>()) {
+NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
std::string path = FileUtil::SanitizePath(file->GetFullPath());
static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
std::regex_constants::ECMAScript |
@@ -65,13 +62,15 @@ NAX::NAX(VirtualFile file_) : file(std::move(file_)), header(std::make_unique<NA
}
NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
- : file(std::move(file_)), header(std::make_unique<NAXHeader>()) {
+ : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256(nca_id.data(), nca_id.size(), hash.data(), 0);
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
Common::HexArrayToString(nca_id, false)));
}
+NAX::~NAX() = default;
+
Loader::ResultStatus NAX::Parse(std::string_view path) {
if (file->ReadObject(header.get()) != sizeof(NAXHeader))
return Loader::ResultStatus::ErrorBadNAXHeader;
@@ -91,7 +90,7 @@ Loader::ResultStatus NAX::Parse(std::string_view path) {
const auto enc_keys = header->key_area;
- size_t i = 0;
+ std::size_t i = 0;
for (; i < sd_keys.size(); ++i) {
std::array<Core::Crypto::Key128, 2> nax_keys{};
if (!CalculateHMAC256(nax_keys.data(), sd_keys[i].data(), 0x10, std::string(path).c_str(),
@@ -99,7 +98,7 @@ Loader::ResultStatus NAX::Parse(std::string_view path) {
return Loader::ResultStatus::ErrorNAXKeyHMACFailed;
}
- for (size_t j = 0; j < nax_keys.size(); ++j) {
+ for (std::size_t j = 0; j < nax_keys.size(); ++j) {
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(nax_keys[j],
Core::Crypto::Mode::ECB);
cipher.Transcode(enc_keys[j].data(), 0x10, header->key_area[j].data(),
@@ -138,9 +137,9 @@ VirtualFile NAX::GetDecrypted() const {
return dec_file;
}
-std::shared_ptr<NCA> NAX::AsNCA() const {
+std::unique_ptr<NCA> NAX::AsNCA() const {
if (type == NAXContentType::NCA)
- return std::make_shared<NCA>(GetDecrypted());
+ return std::make_unique<NCA>(GetDecrypted());
return nullptr;
}
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h
index 55d2154a6..8fedd8585 100644
--- a/src/core/file_sys/xts_archive.h
+++ b/src/core/file_sys/xts_archive.h
@@ -33,12 +33,13 @@ class NAX : public ReadOnlyVfsDirectory {
public:
explicit NAX(VirtualFile file);
explicit NAX(VirtualFile file, std::array<u8, 0x10> nca_id);
+ ~NAX() override;
Loader::ResultStatus GetStatus() const;
VirtualFile GetDecrypted() const;
- std::shared_ptr<NCA> AsNCA() const;
+ std::unique_ptr<NCA> AsNCA() const;
NAXContentType GetContentType() const;
@@ -60,7 +61,7 @@ private:
VirtualFile file;
Loader::ResultStatus status;
- NAXContentType type;
+ NAXContentType type{};
VirtualFile dec_file;