/*
* Copyright (c) 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 .
*/
#pragma once
#include
#include "../dns_mitm/dnsmitm_debug.hpp"
namespace ams::mitm::fs::romfs {
enum class DataSourceType : u8 {
Storage,
File,
LooseSdFile,
Metadata,
Memory,
};
struct SourceInfo {
s64 virtual_offset;
s64 size;
union {
struct {
s64 offset;
} storage_source_info;
struct {
s64 offset;
} file_source_info;
struct {
char *path;
} loose_source_info;
struct {
ams::fs::fsa::IFile *file;
} metadata_source_info;
struct {
u8 *data;
} memory_source_info;
};
DataSourceType source_type;
bool cleaned_up;
SourceInfo(s64 v_o, s64 sz, DataSourceType type, s64 p_o) : virtual_offset(v_o), size(sz), source_type(type), cleaned_up(false) {
switch (this->source_type) {
case DataSourceType::Storage:
this->storage_source_info.offset = p_o;
break;
case DataSourceType::File:
this->file_source_info.offset = p_o;
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
SourceInfo(s64 v_o, s64 sz, DataSourceType type, void *arg) : virtual_offset(v_o), size(sz), source_type(type), cleaned_up(false) {
switch (this->source_type) {
case DataSourceType::LooseSdFile:
this->loose_source_info.path = static_cast(arg);
break;
case DataSourceType::Metadata:
this->metadata_source_info.file = static_cast(arg);
break;
case DataSourceType::Memory:
this->memory_source_info.data = static_cast(arg);
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
void Cleanup() {
AMS_ABORT_UNLESS(!this->cleaned_up);
this->cleaned_up = true;
switch (this->source_type) {
case DataSourceType::Storage:
case DataSourceType::File:
break;
case DataSourceType::Metadata:
delete this->metadata_source_info.file;
break;
case DataSourceType::LooseSdFile:
delete[] this->loose_source_info.path;
break;
case DataSourceType::Memory:
std::free(static_cast(this->memory_source_info.data));
break;
AMS_UNREACHABLE_DEFAULT_CASE();
}
}
};
constexpr inline bool operator<(const SourceInfo &lhs, const SourceInfo &rhs) {
return lhs.virtual_offset < rhs.virtual_offset;
}
constexpr inline bool operator<(const SourceInfo &lhs, const s64 rhs) {
return lhs.virtual_offset <= rhs;
}
struct BuildFileContext;
struct BuildDirectoryContext {
NON_COPYABLE(BuildDirectoryContext);
NON_MOVEABLE(BuildDirectoryContext);
std::unique_ptr path;
union {
BuildDirectoryContext *parent;
u32 parent_offset;
};
union {
BuildDirectoryContext *child;
u32 child_offset;
};
union {
BuildDirectoryContext *sibling;
u32 sibling_offset;
};
union {
BuildFileContext *file;
u32 file_offset;
};
u32 path_len;
u32 entry_offset;
u32 hash_value;
struct RootTag{};
BuildDirectoryContext(RootTag) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), path_len(0), entry_offset(0), hash_value(0xFFFFFFFF) {
this->path = std::make_unique(1);
}
BuildDirectoryContext(const char *entry_name, size_t entry_name_len) : parent(nullptr), child(nullptr), sibling(nullptr), file(nullptr), entry_offset(0) {
this->path_len = entry_name_len;
this->path = std::unique_ptr(new char[this->path_len + 1]);
std::memcpy(this->path.get(), entry_name, entry_name_len);
this->path[this->path_len] = '\x00';
}
size_t GetPathLength() const {
if (this->parent == nullptr) {
return 0;
}
return this->parent->GetPathLength() + 1 + this->path_len;
}
size_t GetPath(char *dst) const {
if (this->parent == nullptr) {
dst[0] = '\x00';
return 0;
}
const size_t parent_len = this->parent->GetPath(dst);
dst[parent_len] = '/';
std::memcpy(dst + parent_len + 1, this->path.get(), this->path_len);
dst[parent_len + 1 + this->path_len] = '\x00';
return parent_len + 1 + this->path_len;
}
bool HasHashMark() const {
return reinterpret_cast(this->sibling) & UINT64_C(0x8000000000000000);
}
void SetHashMark() {
this->sibling = reinterpret_cast(reinterpret_cast(this->sibling) | UINT64_C(0x8000000000000000));
}
void ClearHashMark() {
this->sibling = reinterpret_cast(reinterpret_cast(this->sibling) & ~UINT64_C(0x8000000000000000));
}
};
struct BuildFileContext {
NON_COPYABLE(BuildFileContext);
NON_MOVEABLE(BuildFileContext);
std::unique_ptr path;
BuildDirectoryContext *parent;
union {
BuildFileContext *sibling;
u32 sibling_offset;
};
s64 offset;
s64 size;
s64 orig_offset;
u32 path_len;
u32 entry_offset;
u32 hash_value;
DataSourceType source_type;
BuildFileContext(const char *entry_name, size_t entry_name_len, s64 sz, s64 o_o, DataSourceType type) : parent(nullptr), sibling(nullptr), offset(0), size(sz), orig_offset(o_o), entry_offset(0), hash_value(0xFFFFFFFF), source_type(type) {
this->path_len = entry_name_len;
this->path = std::unique_ptr(new char[this->path_len + 1]);
std::memcpy(this->path.get(), entry_name, entry_name_len);
this->path[this->path_len] = 0;
}
size_t GetPathLength() const {
if (this->parent == nullptr) {
return 0;
}
return this->parent->GetPathLength() + 1 + this->path_len;
}
size_t GetPath(char *dst) const {
if (this->parent == nullptr) {
dst[0] = '\x00';
return 0;
}
const size_t parent_len = this->parent->GetPath(dst);
dst[parent_len] = '/';
std::memcpy(dst + parent_len + 1, this->path.get(), this->path_len);
dst[parent_len + 1 + this->path_len] = '\x00';
return parent_len + 1 + this->path_len;
}
bool HasHashMark() const {
return reinterpret_cast(this->sibling) & UINT64_C(0x8000000000000000);
}
void SetHashMark() {
this->sibling = reinterpret_cast(reinterpret_cast(this->sibling) | UINT64_C(0x8000000000000000));
}
void ClearHashMark() {
this->sibling = reinterpret_cast(reinterpret_cast(this->sibling) & ~UINT64_C(0x8000000000000000));
}
};
class DirectoryTableReader;
class FileTableReader;
class Builder {
NON_COPYABLE(Builder);
NON_MOVEABLE(Builder);
private:
template
struct Comparator {
static constexpr inline int Compare(const char *a, const char *b) {
unsigned char c1{}, c2{};
while ((c1 = *a++) == (c2 = *b++)) {
if (c1 == '\x00') {
return 0;
}
}
return (c1 - c2);
}
constexpr bool operator()(const std::unique_ptr &lhs, const std::unique_ptr &rhs) const {
char lhs_path[ams::fs::EntryNameLengthMax + 1];
char rhs_path[ams::fs::EntryNameLengthMax + 1];
lhs->GetPath(lhs_path);
rhs->GetPath(rhs_path);
return Comparator::Compare(lhs_path, rhs_path) < 0;
}
};
template
using ContextSet = std::set, Comparator>;
private:
ncm::ProgramId m_program_id;
BuildDirectoryContext *m_root;
ContextSet m_directories;
ContextSet m_files;
size_t m_num_dirs;
size_t m_num_files;
size_t m_dir_table_size;
size_t m_file_table_size;
size_t m_dir_hash_table_size;
size_t m_file_hash_table_size;
size_t m_file_partition_size;
::FsDirectoryEntry m_dir_entry;
DataSourceType m_cur_source_type;
private:
void VisitDirectory(FsFileSystem *fs, BuildDirectoryContext *parent);
void VisitDirectory(BuildDirectoryContext *parent, u32 parent_offset, DirectoryTableReader &dir_table, FileTableReader &file_table);
void AddDirectory(BuildDirectoryContext **out, BuildDirectoryContext *parent_ctx, std::unique_ptr file_ctx);
void AddFile(BuildDirectoryContext *parent_ctx, std::unique_ptr file_ctx);
public:
Builder(ncm::ProgramId pr_id);
void AddSdFiles();
void AddStorageFiles(ams::fs::IStorage *storage, DataSourceType source_type);
void Build(std::vector *out_infos);
};
}