#include #include #include namespace lightningbolt { namespace elf { /// Table entry in the ELF. We use this to create file names. struct [[gnu::packed]] BoltTableEntry { /// Pointer to filename. Should be adjusted u32 filenamePtr; u16 entryId; // (GID >> 8) | ID u16 groupId; // (entryId & 0xff00) }; /// Offsets in the ELF to the table. struct BoltTableOffsets { u32 usTable; }; /// Convert a address to ELF file offset. constexpr u32 AddressToElfFileOffset(u32 address) { constexpr u32 LoadAddress = 0x00100000; constexpr u32 SectionOffset = 0x80; return (address - LoadAddress) + SectionOffset; } static constexpr BoltTableOffsets BoltTableOffsets = { .usTable = AddressToElfFileOffset(0x0033d400) }; } // namespace elf struct [[gnu::packed]] BoltGroupEntry { u32 unk; u32 fileSize; u32 fileOffset; u32 unk3; // name hash? }; struct [[gnu::packed]] BoltGroupDescriptor { u8 unk; u8 unk2; u8 unk3; u8 entryCount; u32 groupSize; u32 groupOffset; u32 EntryCount() { // Special case: 0x0 == 256 entries. // I have NO idea why they did it like this, // this really seems extra when they could have // used a short field in the same exact space. if(entryCount == 0x0) return 256; return entryCount; } std::span Entries(u8* base) { return { std::bit_cast(base + groupOffset), EntryCount() }; } }; struct [[gnu::packed]] BoltLibraryHeader { static constexpr char VALID_MAGIC[] = "BOLT\r\n"; char magic[6]; u8 unk; u8 unk2; u8 unk3; u8 unk4; u8 unk5; u8 groupCount; u32 libSize; std::span GroupDescriptors() { // The group descriptors are after the primary library header return { std::bit_cast(this + 1), groupCount }; } inline bool Validate() const { return !std::memcmp(&magic[0], &VALID_MAGIC[0], sizeof(magic)); } }; } // namespace lightningbolt