2020-02-07 04:54:07 +00:00
|
|
|
// Copyright 2020 Citra Emulator Project
|
|
|
|
// Licensed under GPLv2 or any later version
|
|
|
|
// Refer to the license.txt file included.
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
|
|
|
#include <string>
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <vector>
|
2020-03-27 21:48:58 +00:00
|
|
|
#include <boost/serialization/base_object.hpp>
|
|
|
|
#include <boost/serialization/export.hpp>
|
|
|
|
#include <boost/serialization/shared_ptr.hpp>
|
|
|
|
#include <boost/serialization/string.hpp>
|
2020-02-07 04:54:07 +00:00
|
|
|
#include "common/common_types.h"
|
|
|
|
#include "common/swap.h"
|
|
|
|
#include "core/file_sys/romfs_reader.h"
|
|
|
|
|
|
|
|
namespace FileSys {
|
|
|
|
|
|
|
|
struct RomFSHeader {
|
|
|
|
struct Descriptor {
|
|
|
|
u32_le offset;
|
|
|
|
u32_le length;
|
2020-03-27 21:48:58 +00:00
|
|
|
|
|
|
|
private:
|
|
|
|
template <class Archive>
|
|
|
|
void serialize(Archive& ar, const unsigned int) {
|
|
|
|
ar& offset;
|
|
|
|
ar& length;
|
|
|
|
}
|
|
|
|
friend class boost::serialization::access;
|
2020-02-07 04:54:07 +00:00
|
|
|
};
|
|
|
|
u32_le header_length;
|
|
|
|
Descriptor directory_hash_table;
|
|
|
|
Descriptor directory_metadata_table;
|
|
|
|
Descriptor file_hash_table;
|
|
|
|
Descriptor file_metadata_table;
|
|
|
|
u32_le file_data_offset;
|
|
|
|
};
|
|
|
|
static_assert(sizeof(RomFSHeader) == 0x28, "Size of RomFSHeader is not correct");
|
|
|
|
|
|
|
|
/**
|
|
|
|
* LayeredFS implementation. This basically adds a layer to another RomFSReader.
|
|
|
|
*
|
|
|
|
* patch_path: Path for RomFS replacements. Files present in this path replace or create
|
|
|
|
* corresponding files in RomFS.
|
|
|
|
* patch_ext_path: Path for RomFS extensions. Files present in this path:
|
|
|
|
* - When with an extension of ".stub", remove the corresponding file in the RomFS.
|
|
|
|
* - When with an extension of ".ips" or ".bps", patch the file in the RomFS.
|
|
|
|
*/
|
|
|
|
class LayeredFS : public RomFSReader {
|
|
|
|
public:
|
|
|
|
explicit LayeredFS(std::shared_ptr<RomFSReader> romfs, std::string patch_path,
|
2020-02-09 12:59:31 +00:00
|
|
|
std::string patch_ext_path, bool load_relocations = true);
|
2020-02-07 08:26:33 +00:00
|
|
|
~LayeredFS() override;
|
2020-02-07 04:54:07 +00:00
|
|
|
|
|
|
|
std::size_t GetSize() const override;
|
|
|
|
std::size_t ReadFile(std::size_t offset, std::size_t length, u8* buffer) override;
|
|
|
|
|
2020-02-09 12:59:31 +00:00
|
|
|
bool DumpRomFS(const std::string& target_path);
|
|
|
|
|
2020-02-07 04:54:07 +00:00
|
|
|
private:
|
|
|
|
struct File;
|
|
|
|
struct Directory {
|
|
|
|
std::string name;
|
|
|
|
std::string path; // with trailing '/'
|
|
|
|
std::vector<std::unique_ptr<File>> files;
|
|
|
|
std::vector<std::unique_ptr<Directory>> directories;
|
|
|
|
Directory* parent;
|
|
|
|
};
|
|
|
|
|
|
|
|
std::string ReadName(u32 offset, u32 name_length);
|
|
|
|
|
|
|
|
// Loads the current directory, then its siblings, and then its children.
|
|
|
|
void LoadDirectory(Directory& current, u32 offset);
|
|
|
|
|
|
|
|
// Load the file at offset, and then its siblings.
|
|
|
|
void LoadFile(Directory& parent, u32 offset);
|
|
|
|
|
|
|
|
// Load replace/create relocations
|
|
|
|
void LoadRelocations();
|
|
|
|
|
|
|
|
// Load patch/remove relocations
|
|
|
|
void LoadExtRelocations();
|
|
|
|
|
|
|
|
// Calculate the offset of a single directory add it to the map and list of directories
|
|
|
|
void PrepareBuildDirectory(Directory& current);
|
|
|
|
|
|
|
|
// Calculate the offset of a single file add it to the map and list of files
|
|
|
|
void PrepareBuildFile(File& current);
|
|
|
|
|
|
|
|
// Recursively generate a sequence of files and directories and their offsets for all
|
|
|
|
// children of current. (The current directory itself is not handled.)
|
|
|
|
void PrepareBuild(Directory& current);
|
|
|
|
|
|
|
|
void BuildDirectories();
|
|
|
|
void BuildFiles();
|
|
|
|
|
2020-02-09 12:59:31 +00:00
|
|
|
// Recursively extract a directory and all its contents to target_path
|
|
|
|
// target_path should be without trailing '/'.
|
|
|
|
bool ExtractDirectory(Directory& current, const std::string& target_path);
|
|
|
|
|
2020-02-07 04:54:07 +00:00
|
|
|
void RebuildMetadata();
|
|
|
|
|
2020-03-27 21:48:58 +00:00
|
|
|
void Load();
|
|
|
|
|
2020-02-07 04:54:07 +00:00
|
|
|
std::shared_ptr<RomFSReader> romfs;
|
|
|
|
std::string patch_path;
|
|
|
|
std::string patch_ext_path;
|
2020-03-27 21:48:58 +00:00
|
|
|
bool load_relocations;
|
2020-02-07 04:54:07 +00:00
|
|
|
|
|
|
|
RomFSHeader header;
|
|
|
|
Directory root;
|
|
|
|
std::unordered_map<std::string, File*> file_path_map;
|
|
|
|
std::unordered_map<std::string, Directory*> directory_path_map;
|
|
|
|
std::map<u64, File*> data_offset_map; // assigned data offset -> file
|
|
|
|
std::vector<u8> metadata; // Includes header, hash table and metadata
|
|
|
|
|
|
|
|
// Used for rebuilding header
|
|
|
|
std::vector<u32_le> directory_hash_table;
|
|
|
|
std::vector<u32_le> file_hash_table;
|
|
|
|
|
|
|
|
std::unordered_map<Directory*, u32>
|
|
|
|
directory_metadata_offset_map; // directory -> metadata offset
|
|
|
|
std::vector<Directory*> directory_list; // sequence of directories to be written to metadata
|
|
|
|
u64 current_directory_offset{}; // current directory metadata offset
|
|
|
|
std::vector<u8> directory_metadata_table; // rebuilt directory metadata table
|
|
|
|
|
|
|
|
std::unordered_map<File*, u32> file_metadata_offset_map; // file -> metadata offset
|
|
|
|
std::vector<File*> file_list; // sequence of files to be written to metadata
|
|
|
|
u64 current_file_offset{}; // current file metadata offset
|
|
|
|
std::vector<u8> file_metadata_table; // rebuilt file metadata table
|
|
|
|
u64 current_data_offset{}; // current assigned data offset
|
2020-03-27 21:48:58 +00:00
|
|
|
|
|
|
|
LayeredFS();
|
|
|
|
|
|
|
|
template <class Archive>
|
|
|
|
void serialize(Archive& ar, const unsigned int) {
|
|
|
|
ar& boost::serialization::base_object<RomFSReader>(*this);
|
|
|
|
ar& romfs;
|
|
|
|
ar& patch_path;
|
|
|
|
ar& patch_ext_path;
|
|
|
|
ar& load_relocations;
|
|
|
|
if (Archive::is_loading::value) {
|
|
|
|
Load();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
friend class boost::serialization::access;
|
2020-02-07 04:54:07 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace FileSys
|
2020-03-27 21:48:58 +00:00
|
|
|
|
|
|
|
BOOST_CLASS_EXPORT_KEY(FileSys::LayeredFS)
|