From 18aa69f573caf3dbd58c001ad1caa85aaf151cba Mon Sep 17 00:00:00 2001 From: Alison Watson Date: Fri, 4 Oct 2019 13:37:34 -0400 Subject: [PATCH] piss 2 --- CMakeLists.txt | 8 ++--- source/common.h | 32 +++++++++++------- source/quam/archive.cc | 58 ++++++++++++++++++++++++--------- source/quam/archive.h | 36 +++++++++++++++++++- source/quam/main.cc | 7 ++-- source/quam/main_window.cc | 10 +++--- source/quam/main_window.h | 2 +- source/quam/{pak.cc => pack.cc} | 34 +++++++++---------- source/quam/{pak.h => pack.h} | 3 +- source/quam/project.cc | 10 ++++-- source/quam/project.h | 6 ++-- source/quam/{wad.cc => wad2.cc} | 36 ++++++++++---------- source/quam/{wad.h => wad2.h} | 3 +- 13 files changed, 163 insertions(+), 82 deletions(-) rename source/quam/{pak.cc => pack.cc} (69%) rename source/quam/{pak.h => pack.h} (65%) rename source/quam/{wad.cc => wad2.cc} (66%) rename source/quam/{wad.h => wad2.h} (65%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ccdc7f..b2572f7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -47,13 +47,13 @@ add_executable( source/quam/main_window.cc source/quam/main_window.h source/quam/main_window.ui - source/quam/pak.cc - source/quam/pak.h + source/quam/pack.cc + source/quam/pack.h source/quam/project.cc source/quam/project.h source/quam/project.ui - source/quam/wad.cc - source/quam/wad.h) + source/quam/wad2.cc + source/quam/wad2.h) make_qt_project(quam) diff --git a/source/common.h b/source/common.h index 9c5e670..38a04b6 100644 --- a/source/common.h +++ b/source/common.h @@ -19,6 +19,14 @@ #include #include +struct Error : public std::runtime_error { + using std::runtime_error::runtime_error; +}; + +struct EnumError : public Error {using Error::Error;}; +struct FileFormatError : public Error {using Error::Error;}; +struct UnimplementedError : public Error {using Error::Error;}; + static inline QString trMain(char const *sourceText, char const *disambiguation = nullptr, int n = -1) { @@ -52,6 +60,18 @@ static inline std::array readBytes(std::istream &st) { return std::move(b); } +static inline std::ifstream openReadBin(std::filesystem::path path) { + return std::ifstream{path, std::ios_base::in | std::ios_base::binary}; +} + +template +static inline std::string ntbsToString(std::array const &ntbs) { + std::string str; + auto zero = std::find(ntbs.cbegin(), ntbs.cend(), '\0'); + std::copy(ntbs.cbegin(), zero, std::back_inserter(str)); + return str; +} + static inline QDebug operator<<(QDebug debug, std::string const &t) { debug << QString::fromStdString(t); return debug; @@ -70,16 +90,4 @@ static inline QDebug operator<<(QDebug debug, return debug; } -static inline std::ifstream openReadBin(std::filesystem::path path) { - return std::ifstream{path, std::ios_base::in | std::ios_base::binary}; -} - -template -static inline std::string ntbsToString(std::array const &ntbs) { - std::string str; - auto zero = std::find(ntbs.cbegin(), ntbs.cend(), '\0'); - std::copy(ntbs.cbegin(), zero, std::back_inserter(str)); - return str; -} - // EOF diff --git a/source/quam/archive.cc b/source/quam/archive.cc index f4a0c7e..ec1437e 100644 --- a/source/quam/archive.cc +++ b/source/quam/archive.cc @@ -1,12 +1,40 @@ #include "quam/archive.h" +#include "quam/pack.h" +#include "quam/wad2.h" + +#include #include Arc::FileType Arc::getFileType(int n) { if(int t = QMetaEnum::fromType().value(n); n != -1) { return Arc::FileType(t); } else { - throw std::range_error("invalid file type"); + throw EnumError("invalid file type"); + } +} + +Arc::ArchiveType Arc::getArchiveType(std::istream &st) noexcept { + Arc::ArchiveType ret = Arc::ArcNone; + try { + auto pos = st.tellg(); + auto magic = readBytes<4>(st); + if(magic == std::array{'P', 'A', 'C', 'K'}) { + ret = Arc::ArcPack; + } else if(magic == std::array{'W', 'A', 'D', '2'}) { + ret = Arc::ArcWad2; + } + st.seekg(pos); + } catch(...) { + } + return ret; +} + +ArcDir Arc::readArchive(std::istream &st) { + switch(Arc::getArchiveType(st)) { + case Arc::ArcPack: return readPack(st); + case Arc::ArcWad2: return readWad2(st); + case Arc::ArcNone: throw FileFormatError("not an archive"); } } @@ -30,28 +58,27 @@ ArcNode::ArcNode(ArcFile &&f, std::string &&n, Arc::FileType t) : { } -/* -PakDirModel::PakDirModel(PakDir const *root, QObject *parent) : +ArcModel::ArcModel(ArcDir const *root, QObject *parent) : QAbstractItemModel{parent}, m_root{root} { } -PakDirModel::~PakDirModel() { +ArcModel::~ArcModel() { } -QVariant PakDirModel::data(QModelIndex const &index, int role) const { +QVariant ArcModel::data(QModelIndex const &index, int role) const { if(!index.isValid()) { return QVariant{}; } - auto node = static_cast(index.internalPointer()); + auto node = static_cast(index.internalPointer()); switch(role) { case Qt::DecorationRole: if(index.column() == Arc::ColumnName) { auto icon = - std::holds_alternative(*node) ? "folder" + std::holds_alternative(*node) ? "folder" : "text-x-generic"; return QVariant{QIcon::fromTheme(icon)}; } @@ -59,7 +86,7 @@ QVariant PakDirModel::data(QModelIndex const &index, int role) const { case Qt::DisplayRole: switch(index.column()) { case Arc::ColumnSize: - if(auto file = std::get_if(node)) { + if(auto file = std::get_if(node)) { return QVariant{QString::number(file->size())}; } break; @@ -72,7 +99,7 @@ QVariant PakDirModel::data(QModelIndex const &index, int role) const { return QVariant{}; } -Qt::ItemFlags PakDirModel::flags(QModelIndex const &index) const { +Qt::ItemFlags ArcModel::flags(QModelIndex const &index) const { if(!index.isValid()) { return Qt::NoItemFlags; } else { @@ -83,7 +110,7 @@ Qt::ItemFlags PakDirModel::flags(QModelIndex const &index) const { } } -QVariant PakDirModel::headerData(int section, +QVariant ArcModel::headerData(int section, Qt::Orientation orientation, int role) const { if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { @@ -95,7 +122,7 @@ QVariant PakDirModel::headerData(int section, return QVariant{}; } -QModelIndex PakDirModel::index(int row, +QModelIndex ArcModel::index(int row, int col, QModelIndex const &parent) const { if(!hasIndex(row, col, parent) || row > m_root->size()) { @@ -103,21 +130,20 @@ QModelIndex PakDirModel::index(int row, } else { // despite index data being const, this function does not take a const // pointer, which is very annoying! - return createIndex(row, col, const_cast(&m_root->at(row))); + return createIndex(row, col, const_cast(&m_root->at(row))); } } -QModelIndex PakDirModel::parent(QModelIndex const &) const { +QModelIndex ArcModel::parent(QModelIndex const &) const { return QModelIndex{}; } -int PakDirModel::rowCount(QModelIndex const &) const { +int ArcModel::rowCount(QModelIndex const &) const { return m_root->size(); } -int PakDirModel::columnCount(QModelIndex const &) const { +int ArcModel::columnCount(QModelIndex const &) const { return Arc::ColumnMax; } -*/ // EOF diff --git a/source/quam/archive.h b/source/quam/archive.h index 02e2d47..96f7022 100644 --- a/source/quam/archive.h +++ b/source/quam/archive.h @@ -2,8 +2,11 @@ #include "common.h" -#include +#include #include +#include + +struct ArcDir; namespace Arc { Q_NAMESPACE @@ -27,7 +30,17 @@ namespace Arc { }; Q_ENUM_NS(FileType) + enum ArchiveType { + ArcNone, + ArcPack, + ArcWad2, + }; + Q_ENUM_NS(ArchiveType) + Arc::FileType getFileType(int n); + Arc::ArchiveType getArchiveType(std::istream &st) noexcept; + + ArcDir readArchive(std::istream &st); } struct ArcNode; @@ -60,4 +73,25 @@ struct ArcNode : public std::variant { }; Q_DECLARE_METATYPE(ArcNode) +class ArcModel : public QAbstractItemModel { + Q_OBJECT + +public: + explicit ArcModel(ArcDir const *root, QObject *parent); + ~ArcModel(); + + QVariant data(QModelIndex const &index, int role) const override; + Qt::ItemFlags flags(QModelIndex const &index) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) + const override; + QModelIndex index(int row, int col, QModelIndex const &parent) + const override; + QModelIndex parent(QModelIndex const &) const override; + int rowCount(QModelIndex const &) const override; + int columnCount(QModelIndex const &) const override; + +private: + ArcDir const *const m_root; +}; + // EOF diff --git a/source/quam/main.cc b/source/quam/main.cc index 6300763..aabad62 100644 --- a/source/quam/main.cc +++ b/source/quam/main.cc @@ -1,6 +1,5 @@ -#include "common.h" +#include "quam/archive.h" #include "quam/main_window.h" -#include "quam/pak.h" #include #include @@ -44,9 +43,9 @@ static int modeText(int argc, char *argv[]) { auto fileName = par.value(fileNameOpt).toStdString(); auto st = openReadBin(fileName); - auto pak = readPak(st); + auto arc = Arc::readArchive(st); - qDebug() << pak; + qDebug() << arc; return 0; } diff --git a/source/quam/main_window.cc b/source/quam/main_window.cc index 806c32a..f5831d9 100644 --- a/source/quam/main_window.cc +++ b/source/quam/main_window.cc @@ -1,4 +1,6 @@ #include "quam/main_window.h" + +#include "quam/archive.h" #include "quam/project.h" #include @@ -25,16 +27,16 @@ void MainWindow::fileOpen() { this, tr("Open Archive"), QString{}, - tr("Quake PACK file (*.pak);;" + tr("Quake archive file (*.pak *.wad);;" + "Quake PACK file (*.pak);;" "Quake WAD2 file (*.wad);;" "All files (*)")); if(!fileName.isEmpty()) { try { auto st = openReadBin(fileName.toStdString()); - auto pak = readPak(st); - Q_UNUSED(pak); - //new Project{std::move(pak), m_errors, mdiArea}; + auto arc = Arc::readArchive(st); + new Project{std::move(arc), m_errors, mdiArea}; } catch(std::exception const &exc) { m_errors->showMessage(tr(exc.what())); } diff --git a/source/quam/main_window.h b/source/quam/main_window.h index a7347d0..40acbc5 100644 --- a/source/quam/main_window.h +++ b/source/quam/main_window.h @@ -1,7 +1,7 @@ #pragma once #include "common.h" -#include "quam/pak.h" + #include "quam/ui_main_window.h" #include diff --git a/source/quam/pak.cc b/source/quam/pack.cc similarity index 69% rename from source/quam/pak.cc rename to source/quam/pack.cc index f8aaddc..39da959 100644 --- a/source/quam/pak.cc +++ b/source/quam/pack.cc @@ -1,38 +1,38 @@ -#include "quam/pak.h" +#include "quam/pack.h" -struct PakHeader { +struct PackHeader { quint32 dirOffset; quint32 dirNum; }; -struct PakEntry { +struct PackEntry { std::string name; ArcFile file; }; -static constexpr quint32 sizeOfPakEntry = 64; +static constexpr quint32 sizeOfPackEntry = 64; -static PakHeader readPakHeader(std::istream &st) { +static PackHeader readPackHeader(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)"); + throw FileFormatError("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"); + if(dirSize % sizeOfPackEntry != 0) { + throw FileFormatError("invalid directory size"); } - PakHeader hdr; + PackHeader hdr; hdr.dirOffset = dirOffset; - hdr.dirNum = dirSize / sizeOfPakEntry; + hdr.dirNum = dirSize / sizeOfPackEntry; return hdr; } -static PakEntry readPakEntry(std::istream &st) { +static PackEntry readPackEntry(std::istream &st) { auto entName = readBytes<56>(st); auto entOffset = readLE(st); auto entSize = readLE(st); @@ -50,10 +50,10 @@ static PakEntry readPakEntry(std::istream &st) { std::string name = ntbsToString(entName); if(name.front() == '/') { - throw std::runtime_error("empty root directory name"); + throw FileFormatError("empty root directory name"); } - PakEntry ent; + PackEntry ent; ent.name = std::move(name); ent.file = std::move(file); return ent; @@ -77,20 +77,20 @@ static void insertFile(ArcDir &dir, std::string name, ArcFile file) { insertFile(std::get(ref), *std::move(next), std::move(file)); } else { if(existing != dir.end()) { - throw std::runtime_error("duplicate file"); + throw FileFormatError("duplicate file"); } dir.emplace_back(std::move(file), std::move(name)); } } -ArcDir readPak(std::istream &st) { - auto hdr = readPakHeader(st); +ArcDir readPack(std::istream &st) { + auto hdr = readPackHeader(st); st.seekg(hdr.dirOffset); ArcDir root; for(quint32 i = 0; i < hdr.dirNum; i++) { - auto ent = readPakEntry(st); + auto ent = readPackEntry(st); insertFile(root, std::move(ent.name), std::move(ent.file)); } diff --git a/source/quam/pak.h b/source/quam/pack.h similarity index 65% rename from source/quam/pak.h rename to source/quam/pack.h index af1505a..52b6c5e 100644 --- a/source/quam/pak.h +++ b/source/quam/pack.h @@ -1,8 +1,9 @@ #pragma once #include "common.h" + #include "quam/archive.h" -ArcDir readPak(std::istream &st); +ArcDir readPack(std::istream &st); // EOF diff --git a/source/quam/project.cc b/source/quam/project.cc index 96e9db6..601e978 100644 --- a/source/quam/project.cc +++ b/source/quam/project.cc @@ -5,16 +5,22 @@ #include #include -Project::Project(QErrorMessage *errors, QMdiArea *parent) : +Project::Project(ArcDir &&arc, QErrorMessage *errors, QMdiArea *parent) : QMdiSubWindow{parent}, Ui::Project{}, - m_errors{errors} + m_arc{std::move(arc)}, + m_errors{errors}, + m_model{new ArcModel{&m_arc, this}}, + m_sorter{new QSortFilterProxyModel{this}} { auto widget = new QWidget(this); setupUi(widget); setWidget(widget); setAttribute(Qt::WA_DeleteOnClose); showMaximized(); + + m_sorter->setSourceModel(m_model); + tableView->setModel(m_sorter); } Project::~Project() { diff --git a/source/quam/project.h b/source/quam/project.h index c41c597..2780e5b 100644 --- a/source/quam/project.h +++ b/source/quam/project.h @@ -1,7 +1,8 @@ #pragma once #include "common.h" -#include "quam/pak.h" + +#include "quam/archive.h" #include "quam/ui_project.h" #include @@ -16,10 +17,11 @@ class Project : public QMdiSubWindow, private Ui::Project { Q_OBJECT public: - explicit Project(QErrorMessage *errors, QMdiArea *parent); + explicit Project(ArcDir &&arc, QErrorMessage *errors, QMdiArea *parent); virtual ~Project(); private: + ArcDir m_arc; QErrorMessage *m_errors; QAbstractItemModel *m_model; QSortFilterProxyModel *m_sorter; diff --git a/source/quam/wad.cc b/source/quam/wad2.cc similarity index 66% rename from source/quam/wad.cc rename to source/quam/wad2.cc index 6061a65..bd04b74 100644 --- a/source/quam/wad.cc +++ b/source/quam/wad2.cc @@ -1,37 +1,37 @@ -#include "quam/wad.h" +#include "quam/wad2.h" -namespace Wad { +namespace Wad2 { enum Compression { CompressNone, CompressLZSS, }; } -struct WadHeader { - quint32 dirOffset; +struct Wad2Header { quint32 dirNum; + quint32 dirOffset; }; -struct WadEntry { +struct Wad2Entry { std::string name; Arc::FileType type; ArcFile file; }; -static WadHeader readWadHeader(std::istream &st) { +static Wad2Header readWad2Header(std::istream &st) { auto magic = readBytes<4>(st); if(magic != std::array{'W', 'A', 'D', '2'}) { - throw std::runtime_error("not a wad2 file (invalid magic number)"); + throw FileFormatError("not a wad2 file (invalid magic number)"); } - WadHeader hdr; - hdr.dirOffset = readLE(st); + Wad2Header hdr; hdr.dirNum = readLE(st); + hdr.dirOffset = readLE(st); return hdr; } -static WadEntry readWadEntry(std::istream &st) { +static Wad2Entry readWad2Entry(std::istream &st) { auto entOffset = readLE(st); auto entSize = readLE(st); auto entCSize = readLE(st); @@ -40,8 +40,8 @@ static WadEntry readWadEntry(std::istream &st) { /* padding */ readBytes<2>(st); auto entName = readBytes<16>(st); - if(entSize != entCSize || entCompr != Wad::CompressNone) { - throw std::runtime_error("compressed files not implemented"); + if(entSize != entCSize || entCompr != Wad2::CompressNone) { + throw UnimplementedError("compressed files not supported"); } auto pos = st.tellg(); @@ -54,24 +54,26 @@ static WadEntry readWadEntry(std::istream &st) { st.seekg(pos); - WadEntry ent; + Wad2Entry ent; ent.name = ntbsToString(entName); ent.file = std::move(file); ent.type = Arc::getFileType(entType); return ent; } -ArcDir readWad(std::istream &st) { - auto hdr = readWadHeader(st); +ArcDir readWad2(std::istream &st) { + auto hdr = readWad2Header(st); st.seekg(hdr.dirOffset); ArcDir root; for(quint32 i = 0; i < hdr.dirNum; i++) { - auto ent = readWadEntry(st); + auto ent = readWad2Entry(st); + /* if(root.findNode(ent.name) != root.end()) { - throw std::runtime_error("duplicate file"); + throw FileFormatError("duplicate file"); } + */ root.emplace_back(std::move(ent.file), std::move(ent.name), ent.type); } diff --git a/source/quam/wad.h b/source/quam/wad2.h similarity index 65% rename from source/quam/wad.h rename to source/quam/wad2.h index c99236b..d58e925 100644 --- a/source/quam/wad.h +++ b/source/quam/wad2.h @@ -1,8 +1,9 @@ #pragma once #include "common.h" + #include "quam/archive.h" -ArcDir readWad(std::istream &st); +ArcDir readWad2(std::istream &st); // EOF