// Copyright 2017 Citra Emulator Project // Licensed under GPLv2 or any later version // Refer to the license.txt file included. #include #include "common/swap.h" #include "core/hle/romfs.h" namespace RomFS { struct Header { u32_le header_length; u32_le dir_hash_table_offset; u32_le dir_hash_table_length; u32_le dir_table_offset; u32_le dir_table_length; u32_le file_hash_table_offset; u32_le file_hash_table_length; u32_le file_table_offset; u32_le file_table_length; u32_le data_offset; }; static_assert(sizeof(Header) == 0x28, "Header has incorrect size"); struct DirectoryMetadata { u32_le parent_dir_offset; u32_le next_dir_offset; u32_le first_child_dir_offset; u32_le first_file_offset; u32_le same_hash_next_dir_offset; u32_le name_length; // in bytes // followed by directory name }; static_assert(sizeof(DirectoryMetadata) == 0x18, "DirectoryMetadata has incorrect size"); struct FileMetadata { u32_le parent_dir_offset; u32_le next_file_offset; u64_le data_offset; u64_le data_length; u32_le same_hash_next_file_offset; u32_le name_length; // in bytes // followed by file name }; static_assert(sizeof(FileMetadata) == 0x20, "FileMetadata has incorrect size"); static bool MatchName(const u8* buffer, u32 name_length, const std::u16string& name) { std::vector name_buffer(name_length / sizeof(char16_t)); std::memcpy(name_buffer.data(), buffer, name_length); return name == std::u16string(name_buffer.begin(), name_buffer.end()); } const u8* GetFilePointer(const u8* romfs, const std::vector& path) { constexpr u32 INVALID_FIELD = 0xFFFFFFFF; // Split path into directory names and file name std::vector dir_names = path; dir_names.pop_back(); const std::u16string& file_name = path.back(); Header header; std::memcpy(&header, romfs, sizeof(header)); // Find directories of each level DirectoryMetadata dir; const u8* current_dir = romfs + header.dir_table_offset; std::memcpy(&dir, current_dir, sizeof(dir)); for (const std::u16string& dir_name : dir_names) { u32 child_dir_offset; child_dir_offset = dir.first_child_dir_offset; while (true) { if (child_dir_offset == INVALID_FIELD) { return nullptr; } const u8* current_child_dir = romfs + header.dir_table_offset + child_dir_offset; std::memcpy(&dir, current_child_dir, sizeof(dir)); if (MatchName(current_child_dir + sizeof(dir), dir.name_length, dir_name)) { current_dir = current_child_dir; break; } child_dir_offset = dir.next_dir_offset; } } // Find the file FileMetadata file; u32 file_offset = dir.first_file_offset; while (file_offset != INVALID_FIELD) { const u8* current_file = romfs + header.file_table_offset + file_offset; std::memcpy(&file, current_file, sizeof(file)); if (MatchName(current_file + sizeof(file), file.name_length, file_name)) { return romfs + header.data_offset + file.data_offset; } file_offset = file.next_file_offset; } return nullptr; } } // namespace RomFS