mirror of https://github.com/yuzu-mirror/yuzu
				
				
				
			
						commit
						7b81e1e525
					
				@ -0,0 +1,372 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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/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 = nullptr;
 | 
			
		||||
 | 
			
		||||
    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) {
 | 
			
		||||
    u32 hash = parent ^ 123456789;
 | 
			
		||||
    for (u32 i = 0; i < path_len; i++) {
 | 
			
		||||
        hash = (hash >> 5) | (hash << 27);
 | 
			
		||||
        hash ^= path[start + i];
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return hash;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static u32 romfs_get_hash_table_count(u32 num_entries) {
 | 
			
		||||
    if (num_entries < 3) {
 | 
			
		||||
        return 3;
 | 
			
		||||
    } else if (num_entries < 19) {
 | 
			
		||||
        return num_entries | 1;
 | 
			
		||||
    }
 | 
			
		||||
    u32 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 + 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 + 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) + ((dir_ctx->path_len - dir_ctx->cur_path_ofs + 3) & ~3);
 | 
			
		||||
    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) + ((file_ctx->path_len - file_ctx->cur_path_ofs + 3) & ~3);
 | 
			
		||||
    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 auto 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);
 | 
			
		||||
    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);
 | 
			
		||||
 | 
			
		||||
    // 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;
 | 
			
		||||
 | 
			
		||||
    // 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 = (file_partition_size + 0xFULL) & ~0xFULL;
 | 
			
		||||
        cur_file->offset = file_partition_size;
 | 
			
		||||
        file_partition_size += cur_file->size;
 | 
			
		||||
        cur_file->entry_offset = entry_offset;
 | 
			
		||||
        entry_offset +=
 | 
			
		||||
            sizeof(RomFSFileEntry) + ((cur_file->path_len - cur_file->cur_path_ofs + 3) & ~3);
 | 
			
		||||
        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) + ((cur_dir->path_len - cur_dir->cur_path_ofs + 3) & ~3);
 | 
			
		||||
    }
 | 
			
		||||
    // 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,
 | 
			
		||||
                    (cur_entry.name_size + 3) & ~3);
 | 
			
		||||
        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::memcpy(dir_table.data() + cur_dir->entry_offset, &cur_entry,
 | 
			
		||||
                    sizeof(RomFSDirectoryEntry));
 | 
			
		||||
        std::memset(dir_table.data() + cur_dir->entry_offset + sizeof(RomFSDirectoryEntry), 0,
 | 
			
		||||
                    (cur_entry.name_size + 3) & ~3);
 | 
			
		||||
        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 = (header.file_partition_ofs + file_partition_size + 3ULL) & ~3ULL;
 | 
			
		||||
    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>(header_data));
 | 
			
		||||
 | 
			
		||||
    std::vector<u8> metadata(file_hash_table_size + file_table_size + dir_hash_table_size +
 | 
			
		||||
                             dir_table_size);
 | 
			
		||||
    auto 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>(metadata));
 | 
			
		||||
 | 
			
		||||
    return out;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
} // namespace FileSys
 | 
			
		||||
@ -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
 | 
			
		||||
@ -0,0 +1,131 @@
 | 
			
		||||
// 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 {
 | 
			
		||||
 | 
			
		||||
VirtualDir LayerDirectories(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)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 {
 | 
			
		||||
    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 LayerDirectories(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
 | 
			
		||||
@ -0,0 +1,52 @@
 | 
			
		||||
// 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 {
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
// 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 {
 | 
			
		||||
    friend VirtualDir LayerDirectories(std::vector<VirtualDir> dirs, std::string name);
 | 
			
		||||
 | 
			
		||||
    LayeredVfsDirectory(std::vector<VirtualDir> dirs, std::string name);
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
    ~LayeredVfsDirectory() 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<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
 | 
			
		||||
@ -0,0 +1,78 @@
 | 
			
		||||
// 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 {
 | 
			
		||||
 | 
			
		||||
template <u8 value>
 | 
			
		||||
class StaticVfsFile : public VfsFile {
 | 
			
		||||
public:
 | 
			
		||||
    explicit StaticVfsFile(size_t size = 0, std::string name = "", VirtualDir parent = nullptr)
 | 
			
		||||
        : size(size), name(std::move(name)), parent(std::move(parent)) {}
 | 
			
		||||
 | 
			
		||||
    std::string GetName() const override {
 | 
			
		||||
        return name;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t GetSize() const override {
 | 
			
		||||
        return size;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool Resize(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;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t Read(u8* data, size_t length, size_t offset) const override {
 | 
			
		||||
        const auto read = std::min(length, size - offset);
 | 
			
		||||
        std::fill(data, data + read, value);
 | 
			
		||||
        return read;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    size_t Write(const u8* data, size_t length, size_t offset) override {
 | 
			
		||||
        return 0;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    boost::optional<u8> ReadByte(size_t offset) const override {
 | 
			
		||||
        if (offset < size)
 | 
			
		||||
            return value;
 | 
			
		||||
        return boost::none;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::vector<u8> ReadBytes(size_t length, 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:
 | 
			
		||||
    size_t size;
 | 
			
		||||
    std::string name;
 | 
			
		||||
    VirtualDir parent;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace FileSys
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue