2019-10-04 10:37:34 -07:00
|
|
|
#include "quam/pack.h"
|
2019-09-27 00:09:09 -07:00
|
|
|
|
2019-10-04 10:37:34 -07:00
|
|
|
static constexpr quint32 sizeOfPackEntry = 64;
|
2019-09-27 00:09:09 -07:00
|
|
|
|
2019-10-10 14:29:08 -07:00
|
|
|
static std::pair<quint32, quint32> readPackHeader(std::istream &st) {
|
2019-09-27 00:09:09 -07:00
|
|
|
auto magic = readBytes<4>(st);
|
|
|
|
|
|
|
|
if(magic != std::array{'P', 'A', 'C', 'K'}) {
|
2019-10-04 10:37:34 -07:00
|
|
|
throw FileFormatError("not a pak file (invalid magic number)");
|
2019-09-27 00:09:09 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
auto dirOffset = readLE<quint32>(st);
|
|
|
|
auto dirSize = readLE<quint32>(st);
|
|
|
|
|
2019-10-04 10:37:34 -07:00
|
|
|
if(dirSize % sizeOfPackEntry != 0) {
|
|
|
|
throw FileFormatError("invalid directory size");
|
2019-09-27 00:09:09 -07:00
|
|
|
}
|
|
|
|
|
2019-10-10 14:29:08 -07:00
|
|
|
return std::make_pair(dirOffset, dirSize / sizeOfPackEntry);
|
2019-09-27 00:09:09 -07:00
|
|
|
}
|
|
|
|
|
2019-10-10 14:29:08 -07:00
|
|
|
static Arc::File readPackEntry(std::istream &st) {
|
2019-09-27 00:09:09 -07:00
|
|
|
auto entName = readBytes<56>(st);
|
|
|
|
auto entOffset = readLE<quint32>(st);
|
|
|
|
auto entSize = readLE<quint32>(st);
|
|
|
|
|
2019-11-06 22:42:59 -08:00
|
|
|
if(entName.front() == '/') {
|
|
|
|
throw FileFormatError("empty root directory name");
|
|
|
|
}
|
|
|
|
|
2019-09-27 00:09:09 -07:00
|
|
|
auto pos = st.tellg();
|
|
|
|
|
|
|
|
st.seekg(entOffset);
|
|
|
|
|
2019-11-06 22:42:59 -08:00
|
|
|
QByteArray data;
|
|
|
|
data.resize(entSize);
|
|
|
|
st.read(data.data(), entSize);
|
2019-09-27 00:09:09 -07:00
|
|
|
st.seekg(pos);
|
|
|
|
|
2019-11-06 22:42:59 -08:00
|
|
|
return Arc::File{ntbsToString(entName), std::move(data)};
|
2019-09-27 00:09:09 -07:00
|
|
|
}
|
|
|
|
|
2019-10-10 14:29:08 -07:00
|
|
|
static void insertFile(Arc::Dir &dir, Arc::File &file, std::string name) {
|
2019-10-05 16:58:19 -07:00
|
|
|
Option<std::string> next;
|
2019-09-28 22:29:31 -07:00
|
|
|
|
2019-09-27 23:09:51 -07:00
|
|
|
if(auto slash = name.find('/'); slash != std::string::npos) {
|
2019-09-28 22:29:31 -07:00
|
|
|
next = name.substr(slash + 1);
|
|
|
|
name = name.substr(0, slash);
|
|
|
|
}
|
|
|
|
|
2019-10-03 14:06:05 -07:00
|
|
|
auto existing = dir.findNode(name);
|
2019-09-28 22:29:31 -07:00
|
|
|
if(next) {
|
2019-10-10 14:29:08 -07:00
|
|
|
Arc::Node *ref;
|
|
|
|
if(existing) {
|
|
|
|
ref = existing;
|
|
|
|
} else {
|
2019-11-06 22:42:59 -08:00
|
|
|
ref = new Arc::Dir(name);
|
|
|
|
dir.emplaceBack(ref);
|
2019-10-10 14:29:08 -07:00
|
|
|
}
|
|
|
|
insertFile(*ref->getDir(), file, *next);
|
2019-10-08 20:43:35 -07:00
|
|
|
} else if(!existing) {
|
2019-11-06 22:42:59 -08:00
|
|
|
file.name = name;
|
|
|
|
file.parent = &dir;
|
|
|
|
dir.emplaceBack(new Arc::File(std::move(file)));
|
2019-10-08 20:43:35 -07:00
|
|
|
} else {
|
|
|
|
throw FileFormatError("duplicate file");
|
2019-09-27 23:09:51 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-10-05 16:58:19 -07:00
|
|
|
Arc::Dir readPack(std::istream &st) {
|
2019-10-04 10:37:34 -07:00
|
|
|
auto hdr = readPackHeader(st);
|
2019-10-10 14:29:08 -07:00
|
|
|
st.seekg(hdr.first);
|
2019-09-27 00:09:09 -07:00
|
|
|
|
2019-10-10 14:29:08 -07:00
|
|
|
Arc::Dir root{std::string{}};
|
2019-09-27 00:09:09 -07:00
|
|
|
|
2019-10-10 14:29:08 -07:00
|
|
|
for(quint32 i = 0; i < hdr.second; i++) {
|
|
|
|
auto file = readPackEntry(st);
|
|
|
|
insertFile(root, file, file.name);
|
2019-09-27 00:09:09 -07:00
|
|
|
}
|
|
|
|
|
2019-09-27 23:09:51 -07:00
|
|
|
return root;
|
2019-09-27 00:09:09 -07:00
|
|
|
}
|
|
|
|
|
2019-10-09 16:06:14 -07:00
|
|
|
PackValidator::PackValidator(QObject *parent) :
|
|
|
|
QValidator{parent}
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
QValidator::State PackValidator::validate(QString &input, int &pos) const {
|
|
|
|
return QValidator::Acceptable;
|
|
|
|
}
|
|
|
|
|
2019-09-27 00:09:09 -07:00
|
|
|
// EOF
|