#include "common.h" #include "quam/pak.h" struct PakHeader { quint32 dirOffset; quint32 dirNum; }; struct PakEntry { std::string name; PakFile 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, quint32 id) { auto entName = readBytes<56>(st); auto entOffset = readLE(st); auto entSize = readLE(st); auto pos = st.tellg(); st.seekg(entOffset); PakFile file; file.id = id; file.resize(entSize); st.read(file.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.file = std::move(file); 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 id = 0; id < hdr.dirNum; id++) { auto ent = readPakEntry(st, id); insertFile(root, std::move(ent.name), std::move(ent.file)); } return root; } void setTableToPakDir(QTableWidget &table, PakDir const &dir) { constexpr auto Flags = Qt::ItemIsSelectable | Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemNeverHasChildren; auto sorted = table.isSortingEnabled(); table.clearContents(); table.setSortingEnabled(false); quint32 row{0}; for(auto const &kv : dir) { auto const &name = kv.first; auto const &node = kv.second; table.setRowCount(row + 1); { auto item = new QTableWidgetItem; item->setFlags(Flags); if(auto file = std::get_if(&node)) { item->setText(QString::number(file->id)); } table.setItem(row, Pak::ColumnId, item); } { auto item = new QTableWidgetItem; item->setFlags(Flags); if(auto file = std::get_if(&node)) { item->setText(QString::number(file->size())); } table.setItem(row, Pak::ColumnSize, item); } { auto item = new QTableWidgetItem; auto icon = std::holds_alternative(node) ? "folder" : "text-x-generic"; item->setFlags(Flags); item->setText(QString::fromStdString(name)); item->setIcon(QIcon::fromTheme(icon)); table.setItem(row, Pak::ColumnName, item); } ++row; } table.setSortingEnabled(sorted); table.resizeColumnsToContents(); } // EOF