#include "common.h" #include "quam/pak.h" struct PakHeader { quint32 dirOffset; quint32 dirNum; }; struct PakEntry { std::string name; PakFile data; }; 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); PakFile bytes; bytes.resize(entSize); st.read(bytes.data(), entSize); st.seekg(pos); auto zero = std::find(entName.cbegin(), entName.cend(), '\0'); std::string name; std::copy(entName.cbegin(), zero, std::back_inserter(name)); if(name.front() == '/') { throw std::runtime_error("empty root directory name"); } PakEntry ent; ent.name = std::move(name); ent.data = std::move(bytes); return ent; } void insertFile(PakDir &dir, std::string name, PakFile file) { if(auto slash = name.find('/'); slash != std::string::npos) { auto folder = name.substr(0, slash); auto next = name.substr(slash + 1); dir[folder] = PakNode{PakDir{}}; insertFile(std::get(dir[folder]), std::move(next), std::move(file)); } else { dir[name] = PakNode{std::move(file)}; } } PakDir readPak(std::istream &st) { auto hdr = readPakHeader(st); st.seekg(hdr.dirOffset); PakDir root; for(quint32 i = 0; i < hdr.dirNum; i++) { auto ent = readPakEntry(st); insertFile(root, std::move(ent.name), std::move(ent.data)); } return root; } // EOF