#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