From dab41d87ef7a2d3b33f675f6b2a630ce5193f267 Mon Sep 17 00:00:00 2001 From: Alison Watson Date: Sat, 5 Oct 2019 19:58:19 -0400 Subject: [PATCH] yes I did just move everything into a namespace just so I could get a better print of the FileType enum --- source/common.h | 45 +++++++++++++ source/quam/archive.cc | 106 ++++++++++++++++--------------- source/quam/archive.h | 140 +++++++++++++++++++++-------------------- source/quam/pack.cc | 16 ++--- source/quam/pack.h | 2 +- source/quam/project.cc | 11 +++- source/quam/project.h | 9 ++- source/quam/wad2.cc | 23 +++++-- source/quam/wad2.h | 2 +- 9 files changed, 216 insertions(+), 138 deletions(-) diff --git a/source/common.h b/source/common.h index 38a04b6..545220b 100644 --- a/source/common.h +++ b/source/common.h @@ -5,6 +5,7 @@ #include #include +#include #include #include @@ -19,6 +20,11 @@ #include #include +inline constexpr std::nullopt_t None{std::nullopt}; + +template +using Option = std::optional; + struct Error : public std::runtime_error { using std::runtime_error::runtime_error; }; @@ -27,6 +33,35 @@ struct EnumError : public Error {using Error::Error;}; struct FileFormatError : public Error {using Error::Error;}; struct UnimplementedError : public Error {using Error::Error;}; +struct MemoryStreamBuf : public std::streambuf { + MemoryStreamBuf(char *base, std::size_t size) { + setg(base, base, base + size); + } +}; + +struct IMemoryStream : public virtual MemoryStreamBuf, public std::istream { + IMemoryStream(char *base, std::size_t size) : + MemoryStreamBuf{base, size}, + std::istream(static_cast(this)) + { + } + + IMemoryStream(char const *base, std::size_t size) : + IMemoryStream{const_cast(base), size} + { + } + + IMemoryStream(QByteArray &bytes) : + IMemoryStream{bytes.data(), std::size_t(bytes.size())} + { + } + + IMemoryStream(QByteArray const &bytes) : + IMemoryStream{bytes.data(), std::size_t(bytes.size())} + { + } +}; + static inline QString trMain(char const *sourceText, char const *disambiguation = nullptr, int n = -1) { @@ -72,6 +107,16 @@ static inline std::string ntbsToString(std::array const &ntbs) { return str; } +template +static inline char const *enumToString(T const t) { + return QMetaEnum::fromType().valueToKey(int(t)); +} + +template +static inline int enumMax() { + return QMetaEnum::fromType().keyCount(); +} + static inline QDebug operator<<(QDebug debug, std::string const &t) { debug << QString::fromStdString(t); return debug; diff --git a/source/quam/archive.cc b/source/quam/archive.cc index ec1437e..0cb0e8f 100644 --- a/source/quam/archive.cc +++ b/source/quam/archive.cc @@ -4,93 +4,84 @@ #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 EnumError("invalid file type"); - } -} +namespace Arc { -Arc::ArchiveType Arc::getArchiveType(std::istream &st) noexcept { - Arc::ArchiveType ret = Arc::ArcNone; +Option getArchiveType(std::istream &st) noexcept { 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); + if(magic == std::array{'P', 'A', 'C', 'K'}) {return ArcPack;} + if(magic == std::array{'W', 'A', 'D', '2'}) {return ArcWad2;} } catch(...) { } - return ret; + return None; } -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"); +Dir readArchive(std::istream &st) { + switch(auto v = getArchiveType(st); + v.has_value() ? *v : throw FileFormatError("not an archive")) { + case ArcPack: return readPack(st); + case ArcWad2: return readWad2(st); } } -ArcDir::iterator ArcDir::findNode(std::string const &name) { - return std::find_if(begin(), end(), [&name](ArcNode const &node) { +Dir::iterator Dir::findNode(std::string const &name) { + return std::find_if(begin(), end(), [&name](Node const &node) { return node.name == name; }); } -ArcNode::ArcNode(ArcDir &&f, std::string &&n, Arc::FileType t) : +Node::Node(Dir &&f, std::string &&n, FileType t) : super_type(std::move(f)), name(std::move(n)), type(t) { } -ArcNode::ArcNode(ArcFile &&f, std::string &&n, Arc::FileType t) : +Node::Node(File &&f, std::string &&n, FileType t) : super_type(std::move(f)), name(std::move(n)), type(t) { } -ArcModel::ArcModel(ArcDir const *root, QObject *parent) : +Model::Model(QObject *parent) : QAbstractItemModel{parent}, - m_root{root} + m_dir{nullptr} { } -ArcModel::~ArcModel() { +Model::~Model() { } -QVariant ArcModel::data(QModelIndex const &index, int role) const { +QVariant Model::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) { + if(index.column() == ColumnName) { auto icon = - std::holds_alternative(*node) ? "folder" + std::holds_alternative(*node) ? "folder" : "text-x-generic"; return QVariant{QIcon::fromTheme(icon)}; } break; case Qt::DisplayRole: switch(index.column()) { - case Arc::ColumnSize: - if(auto file = std::get_if(node)) { + case ColumnSize: + if(auto file = std::get_if(node)) { return QVariant{QString::number(file->size())}; } break; - case Arc::ColumnName: + case ColumnType: + return QVariant{tr(enumToString(node->type))}; + case ColumnName: return QVariant{tr(node->name.data())}; } default: @@ -99,7 +90,7 @@ QVariant ArcModel::data(QModelIndex const &index, int role) const { return QVariant{}; } -Qt::ItemFlags ArcModel::flags(QModelIndex const &index) const { +Qt::ItemFlags Model::flags(QModelIndex const &index) const { if(!index.isValid()) { return Qt::NoItemFlags; } else { @@ -110,40 +101,53 @@ Qt::ItemFlags ArcModel::flags(QModelIndex const &index) const { } } -QVariant ArcModel::headerData(int section, - Qt::Orientation orientation, - int role) const { +QVariant Model::headerData(int section, + Qt::Orientation orientation, + int role) const { if(orientation == Qt::Horizontal && role == Qt::DisplayRole) { switch(section) { - case Arc::ColumnSize: return QVariant{tr("Size")}; - case Arc::ColumnName: return QVariant{tr("Name")}; + case ColumnSize: return QVariant{tr("Size")}; + case ColumnType: return QVariant{tr("Type")}; + case ColumnName: return QVariant{tr("Name")}; } } return QVariant{}; } -QModelIndex ArcModel::index(int row, - int col, - QModelIndex const &parent) const { - if(!hasIndex(row, col, parent) || row > m_root->size()) { +QModelIndex Model::index(int row, int col, QModelIndex const &parent) const { + if(!m_dir || !hasIndex(row, col, parent) || row > m_dir->size()) { return QModelIndex{}; } 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_dir->at(row))); } } -QModelIndex ArcModel::parent(QModelIndex const &) const { +QModelIndex Model::parent(QModelIndex const &) const { return QModelIndex{}; } -int ArcModel::rowCount(QModelIndex const &) const { - return m_root->size(); +int Model::rowCount(QModelIndex const &) const { + return m_dir ? m_dir->size() : 0; +} + +int Model::columnCount(QModelIndex const &) const { + return enumMax(); +} + +Dir *Model::dir() const { + return m_dir; +} + +void Model::setDir(Dir *dir) { + if(dir != m_dir) { + m_dir = dir; + emit layoutChanged(); + emit dirChanged(dir); + } } -int ArcModel::columnCount(QModelIndex const &) const { - return Arc::ColumnMax; } // EOF diff --git a/source/quam/archive.h b/source/quam/archive.h index 96f7022..c4eeb9a 100644 --- a/source/quam/archive.h +++ b/source/quam/archive.h @@ -6,92 +6,98 @@ #include #include -struct ArcDir; - namespace Arc { Q_NAMESPACE + struct Node; + struct Dir; + enum Column { ColumnSize, + ColumnType, ColumnName, - ColumnMax, }; Q_ENUM_NS(Column) - enum FileType { - FileNormal, - FileLabel, - - FilePalette = 64, - FileTexture, - FilePicture, - FileSound, - FileMipTexture, + enum class FileType { + Label, + MipTexture, + Normal, + Palette, + Picture, + Sound, + Texture, }; 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; + Option getArchiveType(std::istream &st) noexcept; - ArcDir readArchive(std::istream &st); + Dir readArchive(std::istream &st); + + struct Dir : public std::vector { + using std::vector::vector; + + Dir::iterator findNode(std::string const &name); + }; + + struct File : public QByteArray { + using QByteArray::QByteArray; + }; + + struct Node : public std::variant { + using super_type = std::variant; + + Node() = default; + + Node(Dir &&f, std::string &&n, FileType t = FileType::Normal); + Node(File &&f, std::string &&n, FileType t = FileType::Normal); + + Node(Node const &) = default; + Node(Node &&) = default; + + std::string name; + FileType type; + }; + + class Model : public QAbstractItemModel { + Q_OBJECT + + public: + explicit Model(QObject *parent); + ~Model(); + + 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 = QModelIndex()) + const override; + QModelIndex parent(QModelIndex const &index = QModelIndex()) const override; + int rowCount(QModelIndex const &index = QModelIndex()) const override; + int columnCount(QModelIndex const &index = QModelIndex()) const override; + + Dir *dir() const; + + public slots: + void setDir(Dir *dir); + + signals: + void dirChanged(Dir *dir); + + private: + Dir *m_dir; + }; } - -struct ArcNode; - -struct ArcDir : public std::vector { - using std::vector::vector; - - ArcDir::iterator findNode(std::string const &name); -}; -Q_DECLARE_METATYPE(ArcDir) - -struct ArcFile : public QByteArray { - using QByteArray::QByteArray; -}; -Q_DECLARE_METATYPE(ArcFile) - -struct ArcNode : public std::variant { - using super_type = std::variant; - - ArcNode() = default; - - ArcNode(ArcDir &&f, std::string &&n, Arc::FileType t = Arc::FileNormal); - ArcNode(ArcFile &&f, std::string &&n, Arc::FileType t = Arc::FileNormal); - - ArcNode(ArcNode const &) = default; - ArcNode(ArcNode &&) = default; - - std::string name; - Arc::FileType type; -}; -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; -}; +Q_DECLARE_METATYPE(Arc::Dir) +Q_DECLARE_METATYPE(Arc::File) +Q_DECLARE_METATYPE(Arc::Node) // EOF diff --git a/source/quam/pack.cc b/source/quam/pack.cc index 39da959..b2cc772 100644 --- a/source/quam/pack.cc +++ b/source/quam/pack.cc @@ -7,7 +7,7 @@ struct PackHeader { struct PackEntry { std::string name; - ArcFile file; + Arc::File file; }; static constexpr quint32 sizeOfPackEntry = 64; @@ -41,7 +41,7 @@ static PackEntry readPackEntry(std::istream &st) { st.seekg(entOffset); - ArcFile file; + Arc::File file; file.resize(entSize); st.read(file.data(), entSize); @@ -59,8 +59,8 @@ static PackEntry readPackEntry(std::istream &st) { return ent; } -static void insertFile(ArcDir &dir, std::string name, ArcFile file) { - std::optional next; +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); @@ -73,8 +73,8 @@ static void insertFile(ArcDir &dir, std::string name, ArcFile file) { auto ref = existing != dir.end() ? *existing - : dir.emplace_back(ArcDir{}, std::move(name)); - insertFile(std::get(ref), *std::move(next), std::move(file)); + : 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"); @@ -83,11 +83,11 @@ static void insertFile(ArcDir &dir, std::string name, ArcFile file) { } } -ArcDir readPack(std::istream &st) { +Arc::Dir readPack(std::istream &st) { auto hdr = readPackHeader(st); st.seekg(hdr.dirOffset); - ArcDir root; + Arc::Dir root; for(quint32 i = 0; i < hdr.dirNum; i++) { auto ent = readPackEntry(st); diff --git a/source/quam/pack.h b/source/quam/pack.h index 52b6c5e..63b26e6 100644 --- a/source/quam/pack.h +++ b/source/quam/pack.h @@ -4,6 +4,6 @@ #include "quam/archive.h" -ArcDir readPack(std::istream &st); +Arc::Dir readPack(std::istream &st); // EOF diff --git a/source/quam/project.cc b/source/quam/project.cc index 601e978..0eaa21f 100644 --- a/source/quam/project.cc +++ b/source/quam/project.cc @@ -5,12 +5,12 @@ #include #include -Project::Project(ArcDir &&arc, QErrorMessage *errors, QMdiArea *parent) : +Project::Project(Arc::Dir &&arc, QErrorMessage *errors, QMdiArea *parent) : QMdiSubWindow{parent}, Ui::Project{}, m_arc{std::move(arc)}, m_errors{errors}, - m_model{new ArcModel{&m_arc, this}}, + m_model{new Arc::Model{this}}, m_sorter{new QSortFilterProxyModel{this}} { auto widget = new QWidget(this); @@ -19,11 +19,18 @@ Project::Project(ArcDir &&arc, QErrorMessage *errors, QMdiArea *parent) : setAttribute(Qt::WA_DeleteOnClose); showMaximized(); + connect(m_model, &Arc::Model::dirChanged, this, &Project::dirChanged); + m_sorter->setSourceModel(m_model); tableView->setModel(m_sorter); + m_model->setDir(&m_arc); } Project::~Project() { } +void Project::dirChanged(Arc::Dir *) { + tableView->resizeColumnsToContents(); +} + // EOF diff --git a/source/quam/project.h b/source/quam/project.h index 2780e5b..8ffcc52 100644 --- a/source/quam/project.h +++ b/source/quam/project.h @@ -17,13 +17,16 @@ class Project : public QMdiSubWindow, private Ui::Project { Q_OBJECT public: - explicit Project(ArcDir &&arc, QErrorMessage *errors, QMdiArea *parent); + explicit Project(Arc::Dir &&arc, QErrorMessage *errors, QMdiArea *parent); virtual ~Project(); +private slots: + void dirChanged(Arc::Dir *dir); + private: - ArcDir m_arc; + Arc::Dir m_arc; QErrorMessage *m_errors; - QAbstractItemModel *m_model; + Arc::Model *m_model; QSortFilterProxyModel *m_sorter; }; diff --git a/source/quam/wad2.cc b/source/quam/wad2.cc index bd04b74..6ec9d21 100644 --- a/source/quam/wad2.cc +++ b/source/quam/wad2.cc @@ -15,9 +15,22 @@ struct Wad2Header { struct Wad2Entry { std::string name; Arc::FileType type; - ArcFile file; + Arc::File file; }; +static Arc::FileType getFileType(int n) { + switch(n) { + case 0: return Arc::FileType::Normal; + case 1: return Arc::FileType::Label; + case 64: return Arc::FileType::Palette; + case 65: return Arc::FileType::Texture; + case 66: return Arc::FileType::Picture; + case 67: return Arc::FileType::Sound; + case 68: return Arc::FileType::MipTexture; + default: throw EnumError("invalid file type"); + } +} + static Wad2Header readWad2Header(std::istream &st) { auto magic = readBytes<4>(st); @@ -48,7 +61,7 @@ static Wad2Entry readWad2Entry(std::istream &st) { st.seekg(entOffset); - ArcFile file; + Arc::File file; file.resize(entSize); st.read(file.data(), entSize); @@ -57,15 +70,15 @@ static Wad2Entry readWad2Entry(std::istream &st) { Wad2Entry ent; ent.name = ntbsToString(entName); ent.file = std::move(file); - ent.type = Arc::getFileType(entType); + ent.type = getFileType(entType); return ent; } -ArcDir readWad2(std::istream &st) { +Arc::Dir readWad2(std::istream &st) { auto hdr = readWad2Header(st); st.seekg(hdr.dirOffset); - ArcDir root; + Arc::Dir root; for(quint32 i = 0; i < hdr.dirNum; i++) { auto ent = readWad2Entry(st); diff --git a/source/quam/wad2.h b/source/quam/wad2.h index d58e925..01d0acd 100644 --- a/source/quam/wad2.h +++ b/source/quam/wad2.h @@ -4,6 +4,6 @@ #include "quam/archive.h" -ArcDir readWad2(std::istream &st); +Arc::Dir readWad2(std::istream &st); // EOF