#pragma once #include #include #include #include #include #include namespace ResourceDASM { using namespace phosg; struct RELHeader { be_uint32_t module_id; be_uint32_t next_module; // Used at runtime only (unused in file) be_uint32_t prev_module; // Used at runtime only (unused in file) be_uint32_t num_sections; be_uint32_t section_headers_offset; be_uint32_t module_name_offset; // Can be 0 if module is internally unnamed be_uint32_t module_name_size; be_uint32_t format_version; // 1, 2, or 3 be_uint32_t bss_size; be_uint32_t relocation_table_offset; be_uint32_t import_table_offset; be_uint32_t import_table_size; uint8_t on_load_section; // 0 = no on_load function uint8_t on_unload_section; // 0 = no on_unload function uint8_t on_missing_section; // 0 = no on_missing function uint8_t unused; be_uint32_t on_load_offset; // Offset within on_load_section be_uint32_t on_unload_offset; // Offset within on_load_section be_uint32_t on_missing_offset; // Offset within on_load_section be_uint32_t alignment; // Not present if format_version == 1 be_uint32_t bss_alignment; // Not present if format_version == 1 be_uint32_t unknown_a1; // Only present if format_version == 3 } __attribute__((packed)); struct RELSectionHeader { be_uint32_t type_offset; // Low bit is set if section contains code be_uint32_t size; inline bool has_code() const { return this->type_offset & 1; } inline uint32_t offset() const { return this->type_offset & (~1); } } __attribute__((packed)); struct RELImportEntry { be_uint32_t from_module_id; be_uint32_t relocations_offset; } __attribute__((packed)); struct RELRelocationInstruction { enum Type : uint8_t { NONE = 0x00, // Do nothing ADDR32 = 0x01, // Write the absolute address ADDR24 = 0x02, // Write the low 3 bytes of the address, but leave bottom two bits alone ADDR16 = 0x03, // Write the low 2 bytes of the address ADDR16L = 0x04, // Write the low 2 bytes of the address ADDR16H = 0x05, // Write the high 2 bytes of the address ADDR16S = 0x06, // Write the high 2 bytes of the address - 0x10000 ADDR14 = 0x07, // Write the low 14 bits of the address ADDR14T = 0x08, // Write the low 14 bits of the address ADDR14N = 0x09, // Write the low 14 bits of the address REL24 = 0x0A, // Write the offset field of a b instruction REL14 = 0x0B, // Write the offset field of a bc instruction NOP = 0xC9, // Do nothing (but update the offset) SECTION = 0xCA, // Change to section and set offset to 0 STOP = 0xCB, // Stop executing relocation instructions }; be_uint16_t offset; // Bytes after previous relocation entry Type type; // These fields describe where the target symbol is in the impoted module. So // the target address is computed by adding the imported module's base address // and the appropriate section offset to this offset. uint8_t section_index; be_uint32_t symbol_offset; static const char* name_for_type(Type type); } __attribute__((packed)); class RELFile { public: explicit RELFile(const char* filename); RELFile(const char* filename, const std::string& data); RELFile(const char* filename, const void* data, size_t size); ~RELFile() = default; void print( FILE* stream, const std::multimap* labels = nullptr, bool print_hex_view_for_code = false, bool all_sections_as_code = false) const; private: void parse(const void* data, size_t size); const std::string filename; struct Section { uint32_t index; uint32_t offset; uint32_t size; bool has_code; std::string data; }; std::string name; std::vector
sections; RELHeader header; std::unordered_map> import_table; }; } // namespace ResourceDASM