mirror of https://git.suyu.dev/suyu/suyu
RomFS Extraction
parent
0191a1e526
commit
906d785c73
@ -0,0 +1,124 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include "common/common_types.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/romfs.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
#include "core/file_sys/vfs_offset.h"
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
constexpr u32 ROMFS_ENTRY_EMPTY = 0xFFFFFFFF;
|
||||
|
||||
struct TableLocation {
|
||||
u64_le offset;
|
||||
u64_le size;
|
||||
};
|
||||
static_assert(sizeof(TableLocation) == 0x10, "TableLocation has incorrect size.");
|
||||
|
||||
struct RomFSHeader {
|
||||
u64_le header_size;
|
||||
TableLocation directory_hash;
|
||||
TableLocation directory_meta;
|
||||
TableLocation file_hash;
|
||||
TableLocation file_meta;
|
||||
u64_le data_offset;
|
||||
};
|
||||
static_assert(sizeof(RomFSHeader) == 0x50, "RomFSHeader has incorrect size.");
|
||||
|
||||
struct DirectoryEntry {
|
||||
u32_le sibling;
|
||||
u32_le child_dir;
|
||||
u32_le child_file;
|
||||
u32_le hash;
|
||||
u32_le name_length;
|
||||
};
|
||||
static_assert(sizeof(DirectoryEntry) == 0x14, "DirectoryEntry has incorrect size.");
|
||||
|
||||
struct FileEntry {
|
||||
u32_le parent;
|
||||
u32_le sibling;
|
||||
u64_le offset;
|
||||
u64_le size;
|
||||
u32_le hash;
|
||||
u32_le name_length;
|
||||
};
|
||||
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) {
|
||||
Entry entry{};
|
||||
if (file->ReadObject(&entry, offset) != sizeof(Entry))
|
||||
return {};
|
||||
std::string string(entry.name_length, '\0');
|
||||
if (file->ReadArray(&string[0], string.size(), offset + sizeof(Entry)) != string.size())
|
||||
return {};
|
||||
return {entry, string};
|
||||
}
|
||||
|
||||
void ProcessFile(VirtualFile file, size_t file_offset, 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);
|
||||
|
||||
parent->AddFile(std::make_shared<OffsetVfsFile>(
|
||||
file, entry.first.size, entry.first.offset + data_offset, entry.second, parent));
|
||||
|
||||
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
|
||||
break;
|
||||
|
||||
this_file_offset = entry.first.sibling;
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
while (true) {
|
||||
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
|
||||
auto current = std::make_shared<VectorVfsDirectory>(
|
||||
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, parent, entry.second);
|
||||
|
||||
if (entry.first.child_file != ROMFS_ENTRY_EMPTY) {
|
||||
ProcessFile(file, file_offset, data_offset, entry.first.child_file, current);
|
||||
}
|
||||
|
||||
if (entry.first.child_dir != ROMFS_ENTRY_EMPTY) {
|
||||
ProcessDirectory(file, dir_offset, file_offset, data_offset, entry.first.child_dir,
|
||||
current);
|
||||
}
|
||||
|
||||
parent->AddDirectory(current);
|
||||
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
|
||||
break;
|
||||
this_dir_offset = entry.first.sibling;
|
||||
}
|
||||
}
|
||||
|
||||
VirtualDir ExtractRomFS(VirtualFile file) {
|
||||
RomFSHeader header{};
|
||||
if (file->ReadObject(&header) != sizeof(RomFSHeader))
|
||||
return nullptr;
|
||||
|
||||
if (header.header_size != sizeof(RomFSHeader))
|
||||
return nullptr;
|
||||
|
||||
const u64 file_offset = header.file_meta.offset;
|
||||
const u64 dir_offset = header.directory_meta.offset + 4;
|
||||
|
||||
const auto root =
|
||||
std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
|
||||
file->GetContainingDirectory(), file->GetName());
|
||||
|
||||
ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root);
|
||||
|
||||
VirtualDir out = std::move(root);
|
||||
|
||||
while (out->GetSubdirectory("") != nullptr)
|
||||
out = out->GetSubdirectory("");
|
||||
|
||||
return out;
|
||||
}
|
||||
} // namespace FileSys
|
@ -0,0 +1,35 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include "common/common_funcs.h"
|
||||
#include "common/swap.h"
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
struct IVFCLevel {
|
||||
u64_le offset;
|
||||
u64_le size;
|
||||
u32_le block_size;
|
||||
u32_le reserved;
|
||||
};
|
||||
static_assert(sizeof(IVFCLevel) == 0x18, "IVFCLevel has incorrect size.");
|
||||
|
||||
struct IVFCHeader {
|
||||
u32_le magic;
|
||||
u32_le magic_number;
|
||||
INSERT_PADDING_BYTES(8);
|
||||
std::array<IVFCLevel, 6> levels;
|
||||
INSERT_PADDING_BYTES(64);
|
||||
};
|
||||
static_assert(sizeof(IVFCHeader) == 0xE0, "IVFCHeader has incorrect size.");
|
||||
|
||||
// Converts a RomFS binary blob to VFS Filesystem
|
||||
// Returns nullptr on failure
|
||||
VirtualDir ExtractRomFS(VirtualFile file);
|
||||
|
||||
} // namespace FileSys
|
@ -0,0 +1,83 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <algorithm>
|
||||
#include "core/file_sys/vfs_vector.h"
|
||||
|
||||
namespace FileSys {
|
||||
VectorVfsDirectory::VectorVfsDirectory(std::vector<VirtualFile> files_,
|
||||
std::vector<VirtualDir> dirs_, VirtualDir parent_,
|
||||
std::string name_)
|
||||
: files(std::move(files_)), dirs(std::move(dirs_)), parent(std::move(parent_)),
|
||||
name(std::move(name_)) {}
|
||||
|
||||
std::vector<std::shared_ptr<VfsFile>> VectorVfsDirectory::GetFiles() const {
|
||||
return files;
|
||||
}
|
||||
|
||||
std::vector<std::shared_ptr<VfsDirectory>> VectorVfsDirectory::GetSubdirectories() const {
|
||||
return dirs;
|
||||
}
|
||||
|
||||
bool VectorVfsDirectory::IsWritable() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VectorVfsDirectory::IsReadable() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string VectorVfsDirectory::GetName() const {
|
||||
return name;
|
||||
}
|
||||
std::shared_ptr<VfsDirectory> VectorVfsDirectory::GetParentDirectory() const {
|
||||
return parent;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
static bool FindAndRemoveVectorElement(std::vector<T>& vec, std::string_view name) {
|
||||
auto iter = std::find_if(vec.begin(), vec.end(), [name](T e) { return e->GetName() == name; });
|
||||
if (iter == vec.end())
|
||||
return false;
|
||||
auto old_size = vec.size();
|
||||
vec.erase(iter);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool VectorVfsDirectory::DeleteSubdirectory(std::string_view name) {
|
||||
return FindAndRemoveVectorElement(dirs, name);
|
||||
}
|
||||
|
||||
bool VectorVfsDirectory::DeleteFile(std::string_view name) {
|
||||
return FindAndRemoveVectorElement(files, name);
|
||||
}
|
||||
|
||||
bool VectorVfsDirectory::Rename(std::string_view name_) {
|
||||
name = name_;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsDirectory> VectorVfsDirectory::CreateSubdirectory(std::string_view name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<VfsFile> VectorVfsDirectory::CreateFile(std::string_view name) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void VectorVfsDirectory::AddFile(VirtualFile file) {
|
||||
files.push_back(std::move(file));
|
||||
}
|
||||
|
||||
void VectorVfsDirectory::AddDirectory(VirtualDir dir) {
|
||||
dirs.push_back(std::move(dir));
|
||||
}
|
||||
|
||||
bool VectorVfsDirectory::ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) {
|
||||
if (!DeleteFile(file->GetName()))
|
||||
return false;
|
||||
dirs.emplace_back(dir);
|
||||
return true;
|
||||
}
|
||||
} // namespace FileSys
|
@ -0,0 +1,44 @@
|
||||
// Copyright 2018 yuzu emulator team
|
||||
// Licensed under GPLv2 or any later version
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "core/file_sys/vfs.h"
|
||||
|
||||
namespace FileSys {
|
||||
|
||||
// An implementation of VfsDirectory that maintains two vectors for subdirectories and files.
|
||||
// Vector data is supplied upon construction.
|
||||
struct VectorVfsDirectory : public VfsDirectory {
|
||||
explicit VectorVfsDirectory(std::vector<VirtualFile> files = {},
|
||||
std::vector<VirtualDir> dirs = {}, VirtualDir parent = nullptr,
|
||||
std::string name = "");
|
||||
|
||||
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;
|
||||
bool DeleteSubdirectory(std::string_view name) override;
|
||||
bool DeleteFile(std::string_view name) override;
|
||||
bool Rename(std::string_view name) override;
|
||||
std::shared_ptr<VfsDirectory> CreateSubdirectory(std::string_view name) override;
|
||||
std::shared_ptr<VfsFile> CreateFile(std::string_view name) override;
|
||||
|
||||
virtual void AddFile(VirtualFile file);
|
||||
virtual void AddDirectory(VirtualDir dir);
|
||||
|
||||
protected:
|
||||
bool ReplaceFileWithSubdirectory(VirtualFile file, VirtualDir dir) override;
|
||||
|
||||
private:
|
||||
std::vector<VirtualFile> files;
|
||||
std::vector<VirtualDir> dirs;
|
||||
|
||||
VirtualDir parent;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
} // namespace FileSys
|
Loading…
Reference in New Issue