Merge pull request #1401 from lioncash/vfs
vfs: Minor cleanup related changes to layered VFS code
This commit is contained in:
commit
92dd496fb9
|
@ -64,7 +64,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
|
||||||
if (concat.empty())
|
if (concat.empty())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
return FileSys::ConcatenateFiles(concat, dir->GetName());
|
return FileSys::ConcatenatedVfsFile::MakeConcatenatedFile(concat, dir->GetName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return vfs->OpenFile(path, FileSys::Mode::Read);
|
return vfs->OpenFile(path, FileSys::Mode::Read);
|
||||||
|
|
|
@ -73,7 +73,7 @@ static_assert(sizeof(RomFSFileEntry) == 0x20, "RomFSFileEntry has incorrect size
|
||||||
struct RomFSBuildFileContext;
|
struct RomFSBuildFileContext;
|
||||||
|
|
||||||
struct RomFSBuildDirectoryContext {
|
struct RomFSBuildDirectoryContext {
|
||||||
std::string path = "";
|
std::string path;
|
||||||
u32 cur_path_ofs = 0;
|
u32 cur_path_ofs = 0;
|
||||||
u32 path_len = 0;
|
u32 path_len = 0;
|
||||||
u32 entry_offset = 0;
|
u32 entry_offset = 0;
|
||||||
|
@ -84,7 +84,7 @@ struct RomFSBuildDirectoryContext {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RomFSBuildFileContext {
|
struct RomFSBuildFileContext {
|
||||||
std::string path = "";
|
std::string path;
|
||||||
u32 cur_path_ofs = 0;
|
u32 cur_path_ofs = 0;
|
||||||
u32 path_len = 0;
|
u32 path_len = 0;
|
||||||
u32 entry_offset = 0;
|
u32 entry_offset = 0;
|
||||||
|
@ -92,12 +92,10 @@ struct RomFSBuildFileContext {
|
||||||
u64 size = 0;
|
u64 size = 0;
|
||||||
std::shared_ptr<RomFSBuildDirectoryContext> parent;
|
std::shared_ptr<RomFSBuildDirectoryContext> parent;
|
||||||
std::shared_ptr<RomFSBuildFileContext> sibling;
|
std::shared_ptr<RomFSBuildFileContext> sibling;
|
||||||
VirtualFile source = nullptr;
|
VirtualFile source;
|
||||||
|
|
||||||
RomFSBuildFileContext() : path(""), cur_path_ofs(0), path_len(0) {}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, size_t path_len) {
|
static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, std::size_t path_len) {
|
||||||
u32 hash = parent ^ 123456789;
|
u32 hash = parent ^ 123456789;
|
||||||
for (u32 i = 0; i < path_len; i++) {
|
for (u32 i = 0; i < path_len; i++) {
|
||||||
hash = (hash >> 5) | (hash << 27);
|
hash = (hash >> 5) | (hash << 27);
|
||||||
|
@ -107,13 +105,16 @@ static u32 romfs_calc_path_hash(u32 parent, std::string path, u32 start, size_t
|
||||||
return hash;
|
return hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
static u32 romfs_get_hash_table_count(u32 num_entries) {
|
static u64 romfs_get_hash_table_count(u64 num_entries) {
|
||||||
if (num_entries < 3) {
|
if (num_entries < 3) {
|
||||||
return 3;
|
return 3;
|
||||||
} else if (num_entries < 19) {
|
}
|
||||||
|
|
||||||
|
if (num_entries < 19) {
|
||||||
return num_entries | 1;
|
return num_entries | 1;
|
||||||
}
|
}
|
||||||
u32 count = num_entries;
|
|
||||||
|
u64 count = num_entries;
|
||||||
while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 ||
|
while (count % 2 == 0 || count % 3 == 0 || count % 5 == 0 || count % 7 == 0 ||
|
||||||
count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
|
count % 11 == 0 || count % 13 == 0 || count % 17 == 0) {
|
||||||
count++;
|
count++;
|
||||||
|
@ -139,7 +140,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
|
||||||
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
|
const auto child = std::make_shared<RomFSBuildDirectoryContext>();
|
||||||
// Set child's path.
|
// Set child's path.
|
||||||
child->cur_path_ofs = parent->path_len + 1;
|
child->cur_path_ofs = parent->path_len + 1;
|
||||||
child->path_len = child->cur_path_ofs + kv.first.size();
|
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
|
||||||
child->path = parent->path + "/" + kv.first;
|
child->path = parent->path + "/" + kv.first;
|
||||||
|
|
||||||
// Sanity check on path_len
|
// Sanity check on path_len
|
||||||
|
@ -152,7 +153,7 @@ void RomFSBuildContext::VisitDirectory(VirtualDir root_romfs,
|
||||||
const auto child = std::make_shared<RomFSBuildFileContext>();
|
const auto child = std::make_shared<RomFSBuildFileContext>();
|
||||||
// Set child's path.
|
// Set child's path.
|
||||||
child->cur_path_ofs = parent->path_len + 1;
|
child->cur_path_ofs = parent->path_len + 1;
|
||||||
child->path_len = child->cur_path_ofs + kv.first.size();
|
child->path_len = child->cur_path_ofs + static_cast<u32>(kv.first.size());
|
||||||
child->path = parent->path + "/" + kv.first;
|
child->path = parent->path + "/" + kv.first;
|
||||||
|
|
||||||
// Sanity check on path_len
|
// Sanity check on path_len
|
||||||
|
@ -219,8 +220,8 @@ RomFSBuildContext::RomFSBuildContext(VirtualDir base_) : base(std::move(base_))
|
||||||
RomFSBuildContext::~RomFSBuildContext() = default;
|
RomFSBuildContext::~RomFSBuildContext() = default;
|
||||||
|
|
||||||
std::map<u64, VirtualFile> RomFSBuildContext::Build() {
|
std::map<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||||
const auto dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
|
const u64 dir_hash_table_entry_count = romfs_get_hash_table_count(num_dirs);
|
||||||
const auto file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
|
const u64 file_hash_table_entry_count = romfs_get_hash_table_count(num_files);
|
||||||
dir_hash_table_size = 4 * dir_hash_table_entry_count;
|
dir_hash_table_size = 4 * dir_hash_table_entry_count;
|
||||||
file_hash_table_size = 4 * file_hash_table_entry_count;
|
file_hash_table_size = 4 * file_hash_table_entry_count;
|
||||||
|
|
||||||
|
@ -233,12 +234,6 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||||
std::vector<u8> dir_table(dir_table_size);
|
std::vector<u8> dir_table(dir_table_size);
|
||||||
std::vector<u8> file_table(file_table_size);
|
std::vector<u8> file_table(file_table_size);
|
||||||
|
|
||||||
// Clear out hash tables.
|
|
||||||
for (u32 i = 0; i < dir_hash_table_entry_count; i++)
|
|
||||||
dir_hash_table[i] = ROMFS_ENTRY_EMPTY;
|
|
||||||
for (u32 i = 0; i < file_hash_table_entry_count; i++)
|
|
||||||
file_hash_table[i] = ROMFS_ENTRY_EMPTY;
|
|
||||||
|
|
||||||
std::shared_ptr<RomFSBuildFileContext> cur_file;
|
std::shared_ptr<RomFSBuildFileContext> cur_file;
|
||||||
|
|
||||||
// Determine file offsets.
|
// Determine file offsets.
|
||||||
|
@ -355,7 +350,7 @@ std::map<u64, VirtualFile> RomFSBuildContext::Build() {
|
||||||
|
|
||||||
std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
|
std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
|
||||||
dir_table_size);
|
dir_table_size);
|
||||||
auto index = 0;
|
std::size_t index = 0;
|
||||||
std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32));
|
std::memcpy(metadata.data(), dir_hash_table.data(), dir_hash_table.size() * sizeof(u32));
|
||||||
index += dir_hash_table.size() * sizeof(u32);
|
index += dir_hash_table.size() * sizeof(u32);
|
||||||
std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size());
|
std::memcpy(metadata.data() + index, dir_table.data(), dir_table.size());
|
||||||
|
|
|
@ -70,15 +70,18 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
|
||||||
|
|
||||||
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
|
static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
|
||||||
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
const auto load_dir = Service::FileSystem::GetModificationLoadRoot(title_id);
|
||||||
if (type == ContentRecordType::Program && load_dir != nullptr && load_dir->GetSize() > 0) {
|
if (type != ContentRecordType::Program || load_dir == nullptr || load_dir->GetSize() <= 0) {
|
||||||
auto extracted = ExtractRomFS(romfs);
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto extracted = ExtractRomFS(romfs);
|
||||||
|
if (extracted == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (extracted != nullptr) {
|
|
||||||
auto patch_dirs = load_dir->GetSubdirectories();
|
auto patch_dirs = load_dir->GetSubdirectories();
|
||||||
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
std::sort(patch_dirs.begin(), patch_dirs.end(),
|
||||||
[](const VirtualDir& l, const VirtualDir& r) {
|
[](const VirtualDir& l, const VirtualDir& r) { return l->GetName() < r->GetName(); });
|
||||||
return l->GetName() < r->GetName();
|
|
||||||
});
|
|
||||||
|
|
||||||
std::vector<VirtualDir> layers;
|
std::vector<VirtualDir> layers;
|
||||||
layers.reserve(patch_dirs.size() + 1);
|
layers.reserve(patch_dirs.size() + 1);
|
||||||
|
@ -87,22 +90,21 @@ static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType t
|
||||||
if (romfs_dir != nullptr)
|
if (romfs_dir != nullptr)
|
||||||
layers.push_back(std::move(romfs_dir));
|
layers.push_back(std::move(romfs_dir));
|
||||||
}
|
}
|
||||||
|
|
||||||
layers.push_back(std::move(extracted));
|
layers.push_back(std::move(extracted));
|
||||||
|
|
||||||
const auto layered = LayerDirectories(layers);
|
auto layered = LayeredVfsDirectory::MakeLayeredDirectory(std::move(layers));
|
||||||
|
if (layered == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (layered != nullptr) {
|
auto packed = CreateRomFS(std::move(layered));
|
||||||
auto packed = CreateRomFS(layered);
|
if (packed == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (packed != nullptr) {
|
|
||||||
LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully");
|
LOG_INFO(Loader, " RomFS: LayeredFS patches applied successfully");
|
||||||
romfs = std::move(packed);
|
romfs = std::move(packed);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
|
VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset,
|
||||||
ContentRecordType type) const {
|
ContentRecordType type) const {
|
||||||
|
|
|
@ -125,7 +125,7 @@ VirtualFile RegisteredCache::OpenFileOrDirectoryConcat(const VirtualDir& dir,
|
||||||
if (concat.empty())
|
if (concat.empty())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
file = FileSys::ConcatenateFiles(concat, concat.front()->GetName());
|
file = ConcatenatedVfsFile::MakeConcatenatedFile(concat, concat.front()->GetName());
|
||||||
}
|
}
|
||||||
|
|
||||||
return file;
|
return file;
|
||||||
|
|
|
@ -134,7 +134,7 @@ VirtualFile CreateRomFS(VirtualDir dir) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
|
||||||
RomFSBuildContext ctx{dir};
|
RomFSBuildContext ctx{dir};
|
||||||
return ConcatenateFiles<0>(ctx.Build(), dir->GetName());
|
return ConcatenatedVfsFile::MakeConcatenatedFile(0, ctx.Build(), dir->GetName());
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|
|
@ -463,14 +463,14 @@ bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, std::size_t
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_size) {
|
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, std::size_t block_size) {
|
||||||
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
|
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
|
||||||
return false;
|
return false;
|
||||||
if (!dest->Resize(src->GetSize()))
|
if (!dest->Resize(src->GetSize()))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
std::vector<u8> temp(std::min(block_size, src->GetSize()));
|
std::vector<u8> temp(std::min(block_size, src->GetSize()));
|
||||||
for (size_t i = 0; i < src->GetSize(); i += block_size) {
|
for (std::size_t i = 0; i < src->GetSize(); i += block_size) {
|
||||||
const auto read = std::min(block_size, src->GetSize() - i);
|
const auto read = std::min(block_size, src->GetSize() - i);
|
||||||
const auto block = src->Read(temp.data(), read, i);
|
const auto block = src->Read(temp.data(), read, i);
|
||||||
|
|
||||||
|
@ -481,7 +481,7 @@ bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_si
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, size_t block_size) {
|
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, std::size_t block_size) {
|
||||||
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
|
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
|
@ -315,18 +315,19 @@ public:
|
||||||
bool Rename(std::string_view name) override;
|
bool Rename(std::string_view name) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Compare the two files, byte-for-byte, in increments specificed by block_size
|
// Compare the two files, byte-for-byte, in increments specified by block_size
|
||||||
bool DeepEquals(const VirtualFile& file1, const VirtualFile& file2, size_t block_size = 0x1000);
|
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
|
// 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
|
// are using the same implementation, it is probably better to use the Copy method in the parent
|
||||||
// directory of src/dest.
|
// directory of src/dest.
|
||||||
bool VfsRawCopy(const VirtualFile& src, const VirtualFile& dest, size_t block_size = 0x1000);
|
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
|
// 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
|
// directories. It suffers the same performance penalties as above and an implementation-specific
|
||||||
// Copy should always be preferred.
|
// Copy should always be preferred.
|
||||||
bool VfsRawCopyD(const VirtualDir& src, const VirtualDir& dest, size_t block_size = 0x1000);
|
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
|
// 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.
|
// it attempts to create it and returns the new dir or nullptr on failure.
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
|
|
||||||
#include "common/assert.h"
|
#include "common/assert.h"
|
||||||
#include "core/file_sys/vfs_concat.h"
|
#include "core/file_sys/vfs_concat.h"
|
||||||
|
#include "core/file_sys/vfs_static.h"
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
|
@ -22,15 +23,6 @@ static bool VerifyConcatenationMapContinuity(const std::map<u64, VirtualFile>& m
|
||||||
return map.begin()->first == 0;
|
return map.begin()->first == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
VirtualFile ConcatenateFiles(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)));
|
|
||||||
}
|
|
||||||
|
|
||||||
ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name)
|
ConcatenatedVfsFile::ConcatenatedVfsFile(std::vector<VirtualFile> files_, std::string name)
|
||||||
: name(std::move(name)) {
|
: name(std::move(name)) {
|
||||||
std::size_t next_offset = 0;
|
std::size_t next_offset = 0;
|
||||||
|
@ -47,6 +39,41 @@ ConcatenatedVfsFile::ConcatenatedVfsFile(std::map<u64, VirtualFile> files_, std:
|
||||||
|
|
||||||
ConcatenatedVfsFile::~ConcatenatedVfsFile() = default;
|
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 {
|
std::string ConcatenatedVfsFile::GetName() const {
|
||||||
if (files.empty())
|
if (files.empty())
|
||||||
return "";
|
return "";
|
||||||
|
|
|
@ -7,26 +7,27 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <boost/container/flat_map.hpp>
|
|
||||||
#include "core/file_sys/vfs.h"
|
#include "core/file_sys/vfs.h"
|
||||||
#include "core/file_sys/vfs_static.h"
|
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently
|
// Class that wraps multiple vfs files and concatenates them, making reads seamless. Currently
|
||||||
// read-only.
|
// read-only.
|
||||||
class ConcatenatedVfsFile : public VfsFile {
|
class ConcatenatedVfsFile : public VfsFile {
|
||||||
friend VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name);
|
|
||||||
|
|
||||||
template <u8 filler_byte>
|
|
||||||
friend VirtualFile ConcatenateFiles(std::map<u64, VirtualFile> files, std::string name);
|
|
||||||
|
|
||||||
ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
|
ConcatenatedVfsFile(std::vector<VirtualFile> files, std::string name);
|
||||||
ConcatenatedVfsFile(std::map<u64, VirtualFile> files, std::string name);
|
ConcatenatedVfsFile(std::map<u64, VirtualFile> files, std::string name);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~ConcatenatedVfsFile() override;
|
~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;
|
std::string GetName() const override;
|
||||||
std::size_t GetSize() const override;
|
std::size_t GetSize() const override;
|
||||||
bool Resize(std::size_t new_size) override;
|
bool Resize(std::size_t new_size) override;
|
||||||
|
@ -43,33 +44,4 @@ private:
|
||||||
std::string name;
|
std::string name;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Wrapper function to allow for more efficient handling of files.size() == 0, 1 cases.
|
|
||||||
VirtualFile ConcatenateFiles(std::vector<VirtualFile> files, std::string name);
|
|
||||||
|
|
||||||
// Convenience function that turns a map of offsets to files into a concatenated file, filling gaps
|
|
||||||
// with template parameter.
|
|
||||||
template <u8 filler_byte>
|
|
||||||
VirtualFile ConcatenateFiles(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)));
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace FileSys
|
} // namespace FileSys
|
||||||
|
|
|
@ -8,7 +8,13 @@
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name) {
|
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())
|
if (dirs.empty())
|
||||||
return nullptr;
|
return nullptr;
|
||||||
if (dirs.size() == 1)
|
if (dirs.size() == 1)
|
||||||
|
@ -17,11 +23,6 @@ VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name) {
|
||||||
return std::shared_ptr<VfsDirectory>(new LayeredVfsDirectory(std::move(dirs), std::move(name)));
|
return std::shared_ptr<VfsDirectory>(new LayeredVfsDirectory(std::move(dirs), std::move(name)));
|
||||||
}
|
}
|
||||||
|
|
||||||
LayeredVfsDirectory::LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name)
|
|
||||||
: dirs(std::move(dirs)), name(std::move(name)) {}
|
|
||||||
|
|
||||||
LayeredVfsDirectory::~LayeredVfsDirectory() = default;
|
|
||||||
|
|
||||||
std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
|
std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFileRelative(std::string_view path) const {
|
||||||
for (const auto& layer : dirs) {
|
for (const auto& layer : dirs) {
|
||||||
const auto file = layer->GetFileRelative(path);
|
const auto file = layer->GetFileRelative(path);
|
||||||
|
@ -41,7 +42,7 @@ std::shared_ptr<VfsDirectory> LayeredVfsDirectory::GetDirectoryRelative(
|
||||||
out.push_back(std::move(dir));
|
out.push_back(std::move(dir));
|
||||||
}
|
}
|
||||||
|
|
||||||
return LayerDirectories(std::move(out));
|
return MakeLayeredDirectory(std::move(out));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const {
|
std::shared_ptr<VfsFile> LayeredVfsDirectory::GetFile(std::string_view name) const {
|
||||||
|
|
|
@ -9,20 +9,18 @@
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
// Wrapper function to allow for more efficient handling of dirs.size() == 0, 1 cases.
|
|
||||||
VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name = "");
|
|
||||||
|
|
||||||
// Class that stacks multiple VfsDirectories on top of each other, attempting to read from the first
|
// 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)
|
// one and falling back to the one after. The highest priority directory (overwrites all others)
|
||||||
// should be element 0 in the dirs vector.
|
// should be element 0 in the dirs vector.
|
||||||
class LayeredVfsDirectory : public VfsDirectory {
|
class LayeredVfsDirectory : public VfsDirectory {
|
||||||
friend VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name);
|
|
||||||
|
|
||||||
LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name);
|
LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
~LayeredVfsDirectory() override;
|
~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<VfsFile> GetFileRelative(std::string_view path) const override;
|
||||||
std::shared_ptr<VfsDirectory> GetDirectoryRelative(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<VfsFile> GetFile(std::string_view name) const override;
|
||||||
|
|
|
@ -12,21 +12,21 @@
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
|
|
||||||
template <u8 value>
|
|
||||||
class StaticVfsFile : public VfsFile {
|
class StaticVfsFile : public VfsFile {
|
||||||
public:
|
public:
|
||||||
explicit StaticVfsFile(size_t size = 0, std::string name = "", VirtualDir parent = nullptr)
|
explicit StaticVfsFile(u8 value, std::size_t size = 0, std::string name = "",
|
||||||
: size(size), name(std::move(name)), parent(std::move(parent)) {}
|
VirtualDir parent = nullptr)
|
||||||
|
: value{value}, size{size}, name{std::move(name)}, parent{std::move(parent)} {}
|
||||||
|
|
||||||
std::string GetName() const override {
|
std::string GetName() const override {
|
||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t GetSize() const override {
|
std::size_t GetSize() const override {
|
||||||
return size;
|
return size;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Resize(size_t new_size) override {
|
bool Resize(std::size_t new_size) override {
|
||||||
size = new_size;
|
size = new_size;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -43,23 +43,23 @@ public:
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
const auto read = std::min(length, size - offset);
|
const auto read = std::min(length, size - offset);
|
||||||
std::fill(data, data + read, value);
|
std::fill(data, data + read, value);
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
boost::optional<u8> ReadByte(size_t offset) const override {
|
boost::optional<u8> ReadByte(std::size_t offset) const override {
|
||||||
if (offset < size)
|
if (offset < size)
|
||||||
return value;
|
return value;
|
||||||
return boost::none;
|
return boost::none;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<u8> ReadBytes(size_t length, size_t offset) const override {
|
std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
|
||||||
const auto read = std::min(length, size - offset);
|
const auto read = std::min(length, size - offset);
|
||||||
return std::vector<u8>(read, value);
|
return std::vector<u8>(read, value);
|
||||||
}
|
}
|
||||||
|
@ -70,7 +70,8 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t size;
|
u8 value;
|
||||||
|
std::size_t size;
|
||||||
std::string name;
|
std::string name;
|
||||||
VirtualDir parent;
|
VirtualDir parent;
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
|
|
||||||
namespace FileSys {
|
namespace FileSys {
|
||||||
VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent)
|
VectorVfsFile::VectorVfsFile(std::vector<u8> initial_data, std::string name, VirtualDir parent)
|
||||||
: data(std::move(initial_data)), name(std::move(name)), parent(std::move(parent)) {}
|
: data(std::move(initial_data)), parent(std::move(parent)), name(std::move(name)) {}
|
||||||
|
|
||||||
VectorVfsFile::~VectorVfsFile() = default;
|
VectorVfsFile::~VectorVfsFile() = default;
|
||||||
|
|
||||||
|
@ -38,13 +38,13 @@ bool VectorVfsFile::IsReadable() const {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t VectorVfsFile::Read(u8* data_, size_t length, size_t offset) const {
|
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);
|
const auto read = std::min(length, data.size() - offset);
|
||||||
std::memcpy(data_, data.data() + offset, read);
|
std::memcpy(data_, data.data() + offset, read);
|
||||||
return read;
|
return read;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t VectorVfsFile::Write(const u8* data_, size_t length, size_t offset) {
|
std::size_t VectorVfsFile::Write(const u8* data_, std::size_t length, std::size_t offset) {
|
||||||
if (offset + length > data.size())
|
if (offset + length > data.size())
|
||||||
data.resize(offset + length);
|
data.resize(offset + length);
|
||||||
const auto write = std::min(length, data.size() - offset);
|
const auto write = std::min(length, data.size() - offset);
|
||||||
|
|
|
@ -16,13 +16,13 @@ public:
|
||||||
~VectorVfsFile() override;
|
~VectorVfsFile() override;
|
||||||
|
|
||||||
std::string GetName() 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;
|
std::shared_ptr<VfsDirectory> GetContainingDirectory() const override;
|
||||||
bool IsWritable() const override;
|
bool IsWritable() const override;
|
||||||
bool IsReadable() const override;
|
bool IsReadable() const 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;
|
||||||
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;
|
bool Rename(std::string_view name) override;
|
||||||
|
|
||||||
virtual void Assign(std::vector<u8> new_data);
|
virtual void Assign(std::vector<u8> new_data);
|
||||||
|
|
|
@ -756,11 +756,51 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
|
||||||
QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
|
QDesktopServices::openUrl(QUrl::fromLocalFile(qpath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static std::size_t CalculateRomFSEntrySize(const FileSys::VirtualDir& dir, bool full) {
|
||||||
|
std::size_t out = 0;
|
||||||
|
|
||||||
|
for (const auto& subdir : dir->GetSubdirectories()) {
|
||||||
|
out += 1 + CalculateRomFSEntrySize(subdir, full);
|
||||||
|
}
|
||||||
|
|
||||||
|
return out + (full ? dir->GetFiles().size() : 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool RomFSRawCopy(QProgressDialog& dialog, const FileSys::VirtualDir& src,
|
||||||
|
const FileSys::VirtualDir& dest, std::size_t block_size, bool full) {
|
||||||
|
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
|
||||||
|
return false;
|
||||||
|
if (dialog.wasCanceled())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (full) {
|
||||||
|
for (const auto& file : src->GetFiles()) {
|
||||||
|
const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName());
|
||||||
|
if (!FileSys::VfsRawCopy(file, out, block_size))
|
||||||
|
return false;
|
||||||
|
dialog.setValue(dialog.value() + 1);
|
||||||
|
if (dialog.wasCanceled())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& dir : src->GetSubdirectories()) {
|
||||||
|
const auto out = dest->CreateSubdirectory(dir->GetName());
|
||||||
|
if (!RomFSRawCopy(dialog, dir, out, block_size, full))
|
||||||
|
return false;
|
||||||
|
dialog.setValue(dialog.value() + 1);
|
||||||
|
if (dialog.wasCanceled())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
|
void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_path) {
|
||||||
const auto path = fmt::format("{}{:016X}/romfs",
|
const auto path = fmt::format("{}{:016X}/romfs",
|
||||||
FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
|
FileUtil::GetUserPath(FileUtil::UserPath::DumpDir), program_id);
|
||||||
|
|
||||||
auto failed = [this, &path]() {
|
const auto failed = [this, &path] {
|
||||||
QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
|
QMessageBox::warning(this, tr("RomFS Extraction Failed!"),
|
||||||
tr("There was an error copying the RomFS files or the user "
|
tr("There was an error copying the RomFS files or the user "
|
||||||
"cancelled the operation."));
|
"cancelled the operation."));
|
||||||
|
@ -808,53 +848,13 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
|
||||||
failed();
|
failed();
|
||||||
|
|
||||||
const auto full = res == "Full";
|
const auto full = res == "Full";
|
||||||
|
const auto entry_size = CalculateRomFSEntrySize(extracted, full);
|
||||||
const static std::function<size_t(const FileSys::VirtualDir&, bool)> calculate_entry_size =
|
|
||||||
[](const FileSys::VirtualDir& dir, bool full) {
|
|
||||||
size_t out = 0;
|
|
||||||
for (const auto& subdir : dir->GetSubdirectories())
|
|
||||||
out += 1 + calculate_entry_size(subdir, full);
|
|
||||||
return out + full ? dir->GetFiles().size() : 0;
|
|
||||||
};
|
|
||||||
const auto entry_size = calculate_entry_size(extracted, full);
|
|
||||||
|
|
||||||
QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this);
|
QProgressDialog progress(tr("Extracting RomFS..."), tr("Cancel"), 0, entry_size, this);
|
||||||
progress.setWindowModality(Qt::WindowModal);
|
progress.setWindowModality(Qt::WindowModal);
|
||||||
progress.setMinimumDuration(100);
|
progress.setMinimumDuration(100);
|
||||||
|
|
||||||
const static std::function<bool(QProgressDialog&, const FileSys::VirtualDir&,
|
if (RomFSRawCopy(progress, extracted, out, 0x400000, full)) {
|
||||||
const FileSys::VirtualDir&, size_t, bool)>
|
|
||||||
qt_raw_copy = [](QProgressDialog& dialog, const FileSys::VirtualDir& src,
|
|
||||||
const FileSys::VirtualDir& dest, size_t block_size, bool full) {
|
|
||||||
if (src == nullptr || dest == nullptr || !src->IsReadable() || !dest->IsWritable())
|
|
||||||
return false;
|
|
||||||
if (dialog.wasCanceled())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (full) {
|
|
||||||
for (const auto& file : src->GetFiles()) {
|
|
||||||
const auto out = VfsDirectoryCreateFileWrapper(dest, file->GetName());
|
|
||||||
if (!FileSys::VfsRawCopy(file, out, block_size))
|
|
||||||
return false;
|
|
||||||
dialog.setValue(dialog.value() + 1);
|
|
||||||
if (dialog.wasCanceled())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& dir : src->GetSubdirectories()) {
|
|
||||||
const auto out = dest->CreateSubdirectory(dir->GetName());
|
|
||||||
if (!qt_raw_copy(dialog, dir, out, block_size, full))
|
|
||||||
return false;
|
|
||||||
dialog.setValue(dialog.value() + 1);
|
|
||||||
if (dialog.wasCanceled())
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
if (qt_raw_copy(progress, extracted, out, 0x400000, full)) {
|
|
||||||
progress.close();
|
progress.close();
|
||||||
QMessageBox::information(this, tr("RomFS Extraction Succeeded!"),
|
QMessageBox::information(this, tr("RomFS Extraction Succeeded!"),
|
||||||
tr("The operation completed successfully."));
|
tr("The operation completed successfully."));
|
||||||
|
@ -931,7 +931,7 @@ void GMainWindow::OnMenuInstallToNAND() {
|
||||||
}
|
}
|
||||||
|
|
||||||
const auto qt_raw_copy = [this](const FileSys::VirtualFile& src,
|
const auto qt_raw_copy = [this](const FileSys::VirtualFile& src,
|
||||||
const FileSys::VirtualFile& dest, size_t block_size) {
|
const FileSys::VirtualFile& dest, std::size_t block_size) {
|
||||||
if (src == nullptr || dest == nullptr)
|
if (src == nullptr || dest == nullptr)
|
||||||
return false;
|
return false;
|
||||||
if (!dest->Resize(src->GetSize()))
|
if (!dest->Resize(src->GetSize()))
|
||||||
|
|
Reference in New Issue