#include #include #include namespace lightningbolt { struct BoltReader::Impl { Impl(Game game) : game(game) {} ErrorOr OpenBolt(const fs::path& path) { // Load the BOLT file if(auto error = boltFile.Open(path); error.HasError()) return error; lib = std::bit_cast(boltFile.GetMapping()); if(!lib->Validate()) return std::make_error_code(BoltErrc::InvalidMagic); if(game == Game::SimpsonsSkateboarding) { auto p = path; p.replace_filename("SLUS_201.14"); if(auto error = elfFile.Open(p); error.HasError()) return error; // Load table entries. GetTableEntries(); } return {}; } const std::vector& GetTableEntries() { if(game == Game::SimpsonsSkateboarding) { if(entryTable.empty()) { auto* base = elfFile.GetMapping(); auto* table = std::bit_cast(base + elf::BoltTableOffsets.usTable); while(table->filenamePtr != 0x0) { auto string_offset = elf::AddressToElfFileOffset(table->filenamePtr); BoltReader::File te; te.filename = { std::bit_cast(base + string_offset) }; te.index = table->entryId; te.gid = table->groupId; if(te.filename == "") break; entryTable.emplace_back(te); table++; } } } return entryTable; } template void ForEachFile(F f) { for(auto& file : entryTable) { if(file.uncompressedData == nullptr) { auto gid = (file.gid >> 8); auto entries = lib->GroupDescriptors()[gid].Entries(boltFile.GetMapping()); auto size = entries[file.index & 0x00ff].fileSize; auto offset = entries[file.index & 0x00ff].fileOffset; file.compressed = !(entries[file.index & 0x00ff].unk & 0x8); file.uncompressedData = std::bit_cast(boltFile.GetMapping() + offset); file.uncompressedSize = size; } if(!f(file)) break; } } private: Game game; std::vector entryTable; MmapFile elfFile; MmapFile boltFile; BoltLibraryHeader* lib; }; BoltReader::BoltReader(Game game) : impl(std::make_unique(game)) { } BoltReader::~BoltReader() = default; ErrorOr BoltReader::OpenBolt(const fs::path& path) { return impl->OpenBolt(path); } void BoltReader::ForEachFile(std::function f) { impl->ForEachFile([&f](auto& file) { return f(file); }); } } // namespace lightningbolt