#include "quam/pack.h" struct PackHeader { quint32 dirOffset; quint32 dirNum; }; struct PackEntry { std::string name; Arc::File file; }; static constexpr quint32 sizeOfPackEntry = 64; static PackHeader readPackHeader(std::istream &st) { auto magic = readBytes<4>(st); if(magic != std::array{'P', 'A', 'C', 'K'}) { throw FileFormatError("not a pak file (invalid magic number)"); } auto dirOffset = readLE(st); auto dirSize = readLE(st); if(dirSize % sizeOfPackEntry != 0) { throw FileFormatError("invalid directory size"); } PackHeader hdr; hdr.dirOffset = dirOffset; hdr.dirNum = dirSize / sizeOfPackEntry; return hdr; } static PackEntry readPackEntry(std::istream &st) { auto entName = readBytes<56>(st); auto entOffset = readLE(st); auto entSize = readLE(st); auto pos = st.tellg(); st.seekg(entOffset); Arc::File file; file.resize(entSize); st.read(file.data(), entSize); st.seekg(pos); std::string name = ntbsToString(entName); if(name.front() == '/') { throw FileFormatError("empty root directory name"); } PackEntry ent; ent.name = std::move(name); ent.file = std::move(file); return ent; } static void insertFile(Arc::Dir &dir, std::string name, Arc::File file) { Option 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(Arc::Dir{}, std::move(name)); insertFile(std::get(ref), *std::move(next), std::move(file)); } else { if(existing != dir.end()) { throw FileFormatError("duplicate file"); } dir.emplace_back(std::move(file), std::move(name)); } } Arc::Dir readPack(std::istream &st) { auto hdr = readPackHeader(st); st.seekg(hdr.dirOffset); Arc::Dir root; for(quint32 i = 0; i < hdr.dirNum; i++) { auto ent = readPackEntry(st); insertFile(root, std::move(ent.name), std::move(ent.file)); } return root; } // EOF