#include "quam/pak.h" struct PakHeader { quint32 dirOffset; quint32 dirNum; }; struct PakEntry { std::string name; ArcFile file; }; static constexpr quint32 sizeOfPakEntry = 64; static PakHeader readPakHeader(std::istream &st) { auto magic = readBytes<4>(st); if(magic != std::array{'P', 'A', 'C', 'K'}) { throw std::runtime_error("not a pak file (invalid magic number)"); } auto dirOffset = readLE(st); auto dirSize = readLE(st); if(dirSize % sizeOfPakEntry != 0) { throw std::runtime_error("invalid directory size"); } PakHeader hdr; hdr.dirOffset = dirOffset; hdr.dirNum = dirSize / sizeOfPakEntry; return hdr; } static PakEntry readPakEntry(std::istream &st) { auto entName = readBytes<56>(st); auto entOffset = readLE(st); auto entSize = readLE(st); auto pos = st.tellg(); st.seekg(entOffset); ArcFile file; file.resize(entSize); st.read(file.data(), entSize); st.seekg(pos); std::string name = ntbsToString(entName); if(name.front() == '/') { throw std::runtime_error("empty root directory name"); } PakEntry ent; ent.name = std::move(name); ent.file = std::move(file); return ent; } static void insertFile(ArcDir &dir, std::string name, ArcFile file) { std::optional next; if(auto slash = name.find('/'); slash != std::string::npos) { next = name.substr(slash + 1); name = name.substr(0, slash); } auto existing = dir.findNode(name); if(next) { auto ref = existing != dir.end() ? *existing : dir.emplace_back(ArcDir{}, std::move(name)); insertFile(std::get(ref), *std::move(next), std::move(file)); } else { if(existing != dir.end()) { throw std::runtime_error("duplicate file"); } dir.emplace_back(std::move(file), std::move(name)); } } ArcDir readPak(std::istream &st) { auto hdr = readPakHeader(st); st.seekg(hdr.dirOffset); ArcDir root; for(quint32 i = 0; i < hdr.dirNum; i++) { auto ent = readPakEntry(st); insertFile(root, std::move(ent.name), std::move(ent.file)); } return root; } // EOF