#include #include #include #include namespace lightningbolt { fs::path MakeGamePath(BoltReader::Game game) { switch(game) { case BoltReader::Game::LooseBolt: return "./"; // for now break; case BoltReader::Game::SimpsonsSkateboarding: return "ASSETS.BLT"; break; case BoltReader::Game::NamcoMuseumGCN: return "Data0.blt"; break; } } struct BoltReader::Impl { Impl(Game game) : game(game) { std::printf("game %04x\n", game); } ErrorOr OpenBolt(const fs::path& path) { // Load the BOLT file if(auto error = boltFile.Open(path / MakeGamePath(game)); error.HasError()) return error; std::printf("mewow\n"); lib = std::bit_cast(boltFile.GetMapping()); if(!lib->Validate()) return std::make_error_code(BoltErrc::InvalidMagic); if(game == Game::SimpsonsSkateboarding) { if(auto error = elfFile.Open(path / "SLUS_201.14"); error.HasError()) return error; // Load table entries. GetTableEntries(); } std::printf("ret\n"); 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 auto SwapIfGcn(T v) { return (game == Game::NamcoMuseumGCN) ? ByteSwap(v) : v; } template void ForEachFile(F f) { if(game == Game::SimpsonsSkateboarding) { 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; } } else { // generic "We don't have a table" implementation. You don't get nice filenames like this, // simply because we cannot create them (.. maybe, if the unk3 is a hash, we could crack it, // but that would bloat extraction times to potentionally hours if not days, and also might // end up with wrong filenames at the end anyways due to the hash algorithm possibly being weak) // // We still end up synthesizing a File object for the user to interact with anyways. for(u32 i = 0; i < lib->groupCount; ++i) { auto groupDescriptor = lib->GroupDescriptors()[i]; // We can't use the helper since it doesn't know about GCN byteswapping. // FIXME/TODO? std::span groupEntries = { std::bit_cast(boltFile.GetMapping() + SwapIfGcn(groupDescriptor.groupOffset)), groupDescriptor.EntryCount() }; for(u32 j = 0; j < groupEntries.size(); ++j) { auto& ent = groupEntries[j]; auto filename = std::format("blt_{:02x}_{:02x}.bin", i, j); BoltReader::File file; file.gid = i; file.index = j; // Maybe BANG! but this is only accessed here. file.filename = filename; file.compressed = !(ent.unk & 0x8); file.uncompressedSize = SwapIfGcn(ent.fileSize); file.uncompressedData = std::bit_cast(boltFile.GetMapping() + SwapIfGcn(ent.fileOffset)); if(!f(file)) return; } } } } private: Game game; MmapFile boltFile; // Only applicable if game == Game::SimpsonsSkateboarding std::vector entryTable; MmapFile elfFile; BoltLibraryHeader* lib; }; BoltReader::Game BoltReader::GuessGame(const fs::path& path) { if(fs::exists(path / "SLUS_201.14") && fs::exists(path / "ASSETS.BLT")) return Game::SimpsonsSkateboarding; else if(fs::exists(path / "Data0.blt")) return Game::NamcoMuseumGCN; else return Game::LooseBolt; } 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