2022-04-23 04:59:50 -04:00
|
|
|
// SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project
|
|
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
2018-07-27 18:14:03 -04:00
|
|
|
|
2019-11-27 05:28:19 -05:00
|
|
|
#include <memory>
|
|
|
|
|
2018-07-27 18:14:03 -04:00
|
|
|
#include "common/common_types.h"
|
2020-04-06 20:03:32 -04:00
|
|
|
#include "common/string_util.h"
|
2018-07-27 18:14:03 -04:00
|
|
|
#include "common/swap.h"
|
2018-09-23 21:50:16 -04:00
|
|
|
#include "core/file_sys/fsmitm_romfsbuild.h"
|
2018-07-27 18:14:03 -04:00
|
|
|
#include "core/file_sys/romfs.h"
|
|
|
|
#include "core/file_sys/vfs.h"
|
2023-06-03 08:56:59 -04:00
|
|
|
#include "core/file_sys/vfs_cached.h"
|
2018-09-19 22:04:51 -04:00
|
|
|
#include "core/file_sys/vfs_concat.h"
|
2018-07-27 18:14:03 -04:00
|
|
|
#include "core/file_sys/vfs_offset.h"
|
|
|
|
#include "core/file_sys/vfs_vector.h"
|
|
|
|
|
|
|
|
namespace FileSys {
|
2019-11-27 05:26:31 -05:00
|
|
|
namespace {
|
2018-07-27 18:14:03 -04:00
|
|
|
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>
|
2019-11-27 05:26:31 -05:00
|
|
|
std::pair<Entry, std::string> GetEntry(const VirtualFile& file, std::size_t offset) {
|
2018-07-27 18:14:03 -04:00
|
|
|
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};
|
|
|
|
}
|
|
|
|
|
2018-09-15 09:21:06 -04:00
|
|
|
void ProcessFile(VirtualFile file, std::size_t file_offset, std::size_t data_offset,
|
|
|
|
u32 this_file_offset, std::shared_ptr<VectorVfsDirectory> parent) {
|
2018-07-27 18:14:03 -04:00
|
|
|
while (true) {
|
|
|
|
auto entry = GetEntry<FileEntry>(file, file_offset + this_file_offset);
|
|
|
|
|
|
|
|
parent->AddFile(std::make_shared<OffsetVfsFile>(
|
2018-08-11 22:44:50 -04:00
|
|
|
file, entry.first.size, entry.first.offset + data_offset, entry.second));
|
2018-07-27 18:14:03 -04:00
|
|
|
|
|
|
|
if (entry.first.sibling == ROMFS_ENTRY_EMPTY)
|
|
|
|
break;
|
|
|
|
|
|
|
|
this_file_offset = entry.first.sibling;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-15 09:21:06 -04:00
|
|
|
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) {
|
2018-07-27 18:14:03 -04:00
|
|
|
while (true) {
|
|
|
|
auto entry = GetEntry<DirectoryEntry>(file, dir_offset + this_dir_offset);
|
|
|
|
auto current = std::make_shared<VectorVfsDirectory>(
|
2018-08-11 22:44:50 -04:00
|
|
|
std::vector<VirtualFile>{}, std::vector<VirtualDir>{}, entry.second);
|
2018-07-27 18:14:03 -04:00
|
|
|
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
2019-11-27 05:26:31 -05:00
|
|
|
} // Anonymous namespace
|
2018-07-27 18:14:03 -04:00
|
|
|
|
2018-09-23 21:50:16 -04:00
|
|
|
VirtualDir ExtractRomFS(VirtualFile file, RomFSExtractionType type) {
|
2018-07-27 18:14:03 -04:00
|
|
|
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;
|
|
|
|
|
2018-08-11 22:44:50 -04:00
|
|
|
auto root =
|
2018-07-27 18:14:03 -04:00
|
|
|
std::make_shared<VectorVfsDirectory>(std::vector<VirtualFile>{}, std::vector<VirtualDir>{},
|
2018-08-11 22:44:50 -04:00
|
|
|
file->GetName(), file->GetContainingDirectory());
|
2018-07-27 18:14:03 -04:00
|
|
|
|
|
|
|
ProcessDirectory(file, dir_offset, file_offset, header.data_offset, 0, root);
|
|
|
|
|
|
|
|
VirtualDir out = std::move(root);
|
|
|
|
|
2018-12-24 16:18:28 -05:00
|
|
|
if (type == RomFSExtractionType::SingleDiscard)
|
|
|
|
return out->GetSubdirectories().front();
|
|
|
|
|
2018-09-23 21:50:16 -04:00
|
|
|
while (out->GetSubdirectories().size() == 1 && out->GetFiles().empty()) {
|
2020-04-06 20:03:32 -04:00
|
|
|
if (Common::ToLower(out->GetSubdirectories().front()->GetName()) == "data" &&
|
2018-09-23 21:50:16 -04:00
|
|
|
type == RomFSExtractionType::Truncated)
|
2018-09-19 22:04:51 -04:00
|
|
|
break;
|
|
|
|
out = out->GetSubdirectories().front();
|
|
|
|
}
|
2018-07-27 18:14:03 -04:00
|
|
|
|
2023-06-03 08:56:59 -04:00
|
|
|
return std::make_shared<CachedVfsDirectory>(out);
|
2018-07-27 18:14:03 -04:00
|
|
|
}
|
2018-09-19 22:04:51 -04:00
|
|
|
|
2018-10-02 08:56:56 -04:00
|
|
|
VirtualFile CreateRomFS(VirtualDir dir, VirtualDir ext) {
|
2018-09-19 22:04:51 -04:00
|
|
|
if (dir == nullptr)
|
|
|
|
return nullptr;
|
|
|
|
|
2018-10-02 08:56:56 -04:00
|
|
|
RomFSBuildContext ctx{dir, ext};
|
2023-05-26 15:41:17 -04:00
|
|
|
auto file_map = ctx.Build();
|
|
|
|
return ConcatenatedVfsFile::MakeConcatenatedFile(0, file_map, dir->GetName());
|
2018-09-19 22:04:51 -04:00
|
|
|
}
|
|
|
|
|
2018-07-27 18:14:03 -04:00
|
|
|
} // namespace FileSys
|