diff --git a/bolt.hexpat b/bolt.hexpat new file mode 100644 index 0000000..f666455 --- /dev/null +++ b/bolt.hexpat @@ -0,0 +1,50 @@ +struct BoltHdr { + char magic[6]; // BOLT\r\n + + // have no idea what these are yet + u8 unk2; + u8 unk3; + + // or these, they don't seem to be + // directly referenced by bolt stuff + u8 unk5; + u8 unk6; + u8 unk7; + + u8 groupCount; + + // in bytes + u32 libSize; +}; + +struct BoltGroupEnt { + u32 unk; + u32 fileSize; + u32 fileOffset; + u32 unk3; + + u8 fileData[4] @ fileOffset; +}; + +struct BoltGroupDesc { + // flags? + u8 unk; + u8 unk2; + u8 unk3; + u8 groupEntCount; + + // in bytes + u32 groupSize; + u32 groupOffset; + + // ptr padding for ps2 code + u32 resolvedptr; + + if(groupEntCount == 0) + BoltGroupEnt groupEntries[0x100] @ groupOffset; + else + BoltGroupEnt groupEntries[groupEntCount] @ groupOffset; +}; + +BoltHdr hdr @0; +BoltGroupDesc groupDescs[hdr.groupCount] @ $; \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 45b1897..e841db9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,48 +5,51 @@ #include #include -std::vector BoltDecompress(std::span data) { - u8* inptr = data.data(); +#include + +namespace fs = std::filesystem; + +std::vector BoltDecompress(u8* input, usize decompressedSize) { + u8* inptr = input; std::vector res; - i32 iVar3; - i32 iVar6; - u32 uVar5; - u32 pbVar4; // outrun + res.resize(decompressedSize); - for(u32 i = 0; i < data.size(); ++i) { - auto uVar7 = inptr[i]; + u8* pOut = res.data(); + u8* pEnd = res.data() + decompressedSize; - if(uVar7 < 128) { + i32 iVar3 = 0; + i32 iVar6 = 0; + u32 uVar5 = 0; + u8* pbVar4 = nullptr; // outrun + bool bVar1; + + while(pOut < pEnd) { + auto uVar7 = *inptr++; + + if(uVar7 < 128) { // lookback/run? iVar3 = iVar3 + uVar5 * 8 + ((int)(uVar7 & 0x70) >> 4); - pbVar4 = -(iVar6 * 0x10 + (uVar7 & 0xf) + 1); + pbVar4 = pOut + -(iVar6 * 0x10 + (uVar7 & 0xf) + 1); iVar6 = iVar3 + 1; - - if(iVar3 != -2) { do { - //*pOut = *pbVar4; - pbVar4 = pbVar4 + 1; - //bVar1 = iVar6 != 0; - // pOut = pOut + 1; - iVar6 = iVar6 + -1; - } while(iVar6 != 0); + *pOut = *pbVar4; + pbVar4 = pbVar4 + 1; + bVar1 = iVar6 != 0; + pOut = pOut + 1; + iVar6 = iVar6 + -1; + } while(bVar1); } iVar6 = 0; iVar3 = 0; uVar5 = 0; - } else if(uVar7 < 144) { + } else if(uVar7 < 144) { // literal copy from stream iVar3 = uVar5 * 0x10 + (uVar7 & 0xf) + 1; while(iVar3 != 0) { iVar3 = iVar3 + -1; - //*pOut = *pbRam00590aa8; - // pOut = pOut + 1; - // pbRam00590aa8 = pbRam00590aa8 + 1; - // iRam00590ab0 = iRam00590ab0 + -1; - // if(iRam00590ab0 == 0) { - // NextBlock(); - //} + *pOut = *inptr++; + pOut = pOut + 1; } iVar3 = 0; uVar5 = 0; @@ -72,12 +75,26 @@ struct BoltReader { u16 index; u16 gid; - std::span uncompressedData; + bool compressed; + + u8* uncompressedData { nullptr }; + usize uncompressedSize; + + fs::path pathify() { + // Create a mutable copy of the filename + std::string s = { filename.data(), filename.size() }; + + for(auto& c : s) + if(c == '\\') + c = '/'; + + return fs::current_path() / "ASSETS" / fs::path(s); + } }; BoltReader() {} - ErrorOr OpenBolt(const lightningbolt::fs::path& path) { + ErrorOr OpenBolt(const fs::path& path) { auto p = path; p.replace_filename("SLUS_201.14"); @@ -110,7 +127,6 @@ struct BoltReader { if(te.filename == "") break; - // std::cout << std::format("te: {} {:04x} {:04x}\n", te.filename, te.index, te.gid); entryTable.emplace_back(te); table++; @@ -122,13 +138,16 @@ struct BoltReader { template void ForEachFile(F f) { for(auto& file : entryTable) { - if(file.uncompressedData.empty()) { + 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.uncompressedData = { std::bit_cast(boltFile.GetMapping() + offset), size }; + file.compressed = !(entries[file.index & 0x00ff].unk & 0x8); + + file.uncompressedData = std::bit_cast(boltFile.GetMapping() + offset); + file.uncompressedSize = size; } if(!f(file)) @@ -146,13 +165,41 @@ struct BoltReader { int main() { BoltReader reader; - if(auto error = reader.OpenBolt(lightningbolt::fs::current_path() / "ASSETS.BLT"); error.HasError()) { + if(auto error = reader.OpenBolt(fs::current_path() / "ASSETS.BLT"); error.HasError()) { std::cout << "Error opening Bolt file: " << error.Error(); } reader.ForEachFile([](BoltReader::ParsedTableEntry& ent) { - std::cout << std::format("File: {} magic: ", ent.filename) << ent.uncompressedData[0] << ent.uncompressedData[1] << ent.uncompressedData[2] - << ent.uncompressedData[3] << '\n'; + std::cout << std::format("File: {}", ent.filename); + + auto p = ent.pathify(); + + auto pathonly = p; + pathonly.remove_filename(); + + if(!fs::exists(pathonly)) + fs::create_directories(pathonly); + + std::ofstream ofs(p.string(), std::ofstream::binary); + + if(!ofs) { + std::cout << "... : Could not open " << p.string() << " for writing\n"; + return false; + } + + if(ent.compressed) { + std::cout << std::format(", compressed ({} bytes uncompressed)", ent.uncompressedSize); + + auto decompressed = BoltDecompress(ent.uncompressedData, ent.uncompressedSize); + + ofs.write((char*)decompressed.data(), ent.uncompressedSize); + std::cout << std::format(", written to \"{}\"\n", p.string()); + } else { + std::cout << std::format(", uncompressed ({} bytes)", ent.uncompressedSize); + + ofs.write((char*)ent.uncompressedData, ent.uncompressedSize); + std::cout << std::format(", written to \"{}\"\n", p.string()); + } return true; });