From f5d408f02fb37234928e349d12c5237d48e34cec Mon Sep 17 00:00:00 2001 From: Alison Watson Date: Thu, 10 Oct 2019 17:29:08 -0400 Subject: [PATCH] more crime spaghetti --- source/common.h | 18 +++++++- source/quam/archive.cc | 81 +++++++++++++++++++++++------------ source/quam/archive.h | 87 ++++++++++++++++++++++++++++---------- source/quam/main_window.ui | 42 ++++++++++++++++++ source/quam/pack.cc | 57 ++++++++++--------------- source/quam/project.cc | 7 ++- source/quam/project.h | 5 ++- source/quam/project.ui | 16 +------ source/quam/wad2.cc | 43 ++++++------------- 9 files changed, 222 insertions(+), 134 deletions(-) diff --git a/source/common.h b/source/common.h index 5aba29a..e50dcae 100644 --- a/source/common.h +++ b/source/common.h @@ -28,12 +28,18 @@ using Option = std::optional; template using Variant = std::variant; +using Path = std::filesystem::path; + +template +using UniquePtr = std::unique_ptr; + 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 FileSystemError : public Error {using Error::Error;}; struct UnimplementedError : public Error {using Error::Error;}; struct MemoryStreamBuf : public std::streambuf { @@ -98,7 +104,7 @@ static inline std::array readBytes(std::istream &st) { return std::move(b); } -static inline std::ifstream openReadBin(std::filesystem::path path) { +static inline std::ifstream openReadBin(Path path) { return std::ifstream{path, std::ios_base::in | std::ios_base::binary}; } @@ -150,4 +156,14 @@ static inline QDebug operator<<(QDebug debug, Option const &t) { return debug; } +template +static inline QDebug operator<<(QDebug debug, UniquePtr const &t) { + if(t) { + debug << *t; + } else { + debug << "nullptr"; + } + return debug; +} + // EOF diff --git a/source/quam/archive.cc b/source/quam/archive.cc index 6a5b3e9..648e4c7 100644 --- a/source/quam/archive.cc +++ b/source/quam/archive.cc @@ -18,6 +18,24 @@ namespace Arc { return None; } + Node::Node(std::string _name, Dir *_parent) : + name(_name), + parent(_parent) + { + } + + Node::~Node() { + } + + Dir::Dir(std::string _name, Dir *_parent) : + Node(_name, _parent), + baseType() + { + } + + Dir::~Dir() { + } + Dir Dir::readArchive(std::istream &st, ArcType type) { switch(type) { case ArcType::Pack: return readPack(st); @@ -25,6 +43,23 @@ namespace Arc { } } + Node *Dir::findNode(std::string const &name) { + auto it = std::find_if(begin(), end(), + [&name](auto const &node) { + return node->name == name; + }); + return it != end() ? &**it : nullptr; + } + + File::File(std::string _name, Dir *_parent) : + Node(_name, _parent), + baseType() + { + } + + File::~File() { + } + ArcInfo::ArcInfo(ArcType arcType) noexcept : type{arcType} { @@ -54,27 +89,6 @@ namespace Arc { Arc::~Arc() { } - Node *Dir::findNode(std::string const &name) { - auto it = std::find_if(begin(), end(), [&name](Node &node) { - return node.name == name; - }); - return it != end() ? &*it : nullptr; - } - - Node::Node(Dir &&f, std::string &&n, FileType t) : - superType(std::move(f)), - name(std::move(n)), - type(t) - { - } - - Node::Node(File &&f, std::string &&n, FileType t) : - superType(std::move(f)), - name(std::move(n)), - type(t) - { - } - Model::Model(QObject *parent) : QAbstractItemModel{parent}, m_dir{nullptr} @@ -95,21 +109,23 @@ namespace Arc { switch(role) { case Qt::DecorationRole: if(col == Column::Name) { - auto icon = - std::holds_alternative(*node) ? "folder" - : "text-x-generic"; + auto icon = node->getDir() ? "folder" : "text-x-generic"; return QVariant{QIcon::fromTheme(icon)}; } break; case Qt::DisplayRole: switch(col) { case Column::Size: - if(auto file = std::get_if(node)) { + if(auto file = node->getFile()) { return QVariant{QString::number(file->size())}; } break; case Column::Type: - return QVariant{tr(enumToString(node->type))}; + if(auto file = node->getFile()) { + return QVariant{tr(enumToString(file->type))}; + } else { + return QVariant{tr("Directory")}; + } case Column::Name: return QVariant{tr(node->name.data())}; } @@ -151,7 +167,7 @@ namespace Arc { if(!m_dir || !hasIndex(row, col, parent) || row > m_dir->size()) { return QModelIndex{}; } else { - return createIndex(row, col, &m_dir->at(row)); + return createIndex(row, col, &*m_dir->at(row)); } } @@ -188,10 +204,19 @@ namespace Arc { void Model::setDirToIndex(QModelIndex const &ind) { auto node = static_cast(ind.data(Qt::UserRole).value()); - if(auto dir = std::get_if(node)) { + if(auto dir = node->getDir()) { setDir(dir); } } + + bool Model::dirUp() { + if(m_dir && m_dir->parent) { + setDir(m_dir->parent); + return true; + } else { + return false; + } + } } // EOF diff --git a/source/quam/archive.h b/source/quam/archive.h index de67f4c..6c1d184 100644 --- a/source/quam/archive.h +++ b/source/quam/archive.h @@ -35,33 +35,62 @@ namespace Arc { }; Q_ENUM_NS(ArcType) - struct Node; + class Dir; + class File; - struct Dir : public std::vector { - using std::vector::vector; + class Node { + public: + Node() = delete; + Node(Node const &) = delete; + Node(Node &&) = default; + virtual ~Node(); + + virtual Dir const *getDir() const = 0; + virtual Dir *getDir() = 0; + virtual File const *getFile() const = 0; + virtual File *getFile() = 0; + + Dir *parent{nullptr}; + std::string name; + + protected: + Node(std::string name, Dir *parent); + }; + + class Dir : public Node, public std::vector> { + public: + using baseType = std::vector>; + + Dir(std::string name, Dir *parent = nullptr); + Dir(Dir const &) = delete; + Dir(Dir &&) = default; + virtual ~Dir(); + + Dir const *getDir() const override {return this;} + Dir *getDir() override {return this;} + File const *getFile() const override {return nullptr;} + File *getFile() override {return nullptr;} Node *findNode(std::string const &name); static Dir readArchive(std::istream &st, ArcType type); }; - struct File : public QByteArray { - using QByteArray::QByteArray; - }; + class File : public Node, public QByteArray { + public: + using baseType = QByteArray; - struct Node : public Variant { - using superType = Variant; + File(std::string name, Dir *parent = nullptr); + File(File const &) = delete; + File(File &&) = default; + virtual ~File(); - Node() = default; + Dir const *getDir() const override {return nullptr;} + Dir *getDir() override {return nullptr;} + File const *getFile() const override {return this;} + File *getFile() override {return this;} - 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; + FileType type{FileType::Normal}; }; class Model : public QAbstractItemModel { @@ -89,6 +118,7 @@ namespace Arc { public slots: void setDir(Dir *dir); void setDirToIndex(QModelIndex const &ind); + bool dirUp(); signals: void dirChanged(Dir *from, Dir *to); @@ -97,7 +127,8 @@ namespace Arc { Dir *m_dir; }; - struct ArcInfo { + class ArcInfo { + public: ArcInfo(ArcType arcType) noexcept; ArcType type; @@ -119,14 +150,24 @@ namespace Arc { Option getArchiveType(std::istream &st) noexcept; } -Q_DECLARE_METATYPE(Arc::Dir) -Q_DECLARE_METATYPE(Arc::File) -Q_DECLARE_METATYPE(Arc::Node) + +static inline QDebug operator<<(QDebug debug, Arc::Dir const &t) { + debug << static_cast(t); + return debug; +} + +static inline QDebug operator<<(QDebug debug, Arc::File const &t) { + debug << static_cast(t); + return debug; +} static inline QDebug operator<<(QDebug debug, Arc::Node const &t) { debug << "Arc::Node(" << t.name << ", "; - std::visit([&](auto &&arg) {debug << arg;}, - static_cast>(t)); + if(auto v = t.getDir()) { + debug << *v; + } else if(auto v = t.getFile()) { + debug << *v; + } debug << ")"; return debug; } diff --git a/source/quam/main_window.ui b/source/quam/main_window.ui index 1145642..d340dc5 100644 --- a/source/quam/main_window.ui +++ b/source/quam/main_window.ui @@ -51,7 +51,17 @@ + + + Go + + + + + + + @@ -81,6 +91,38 @@ &Close + + + + + + Up + + + + + + + + Back + + + + + + + + Forward + + + + + + + + Top + + diff --git a/source/quam/pack.cc b/source/quam/pack.cc index 1cbc08c..b5426a3 100644 --- a/source/quam/pack.cc +++ b/source/quam/pack.cc @@ -1,18 +1,8 @@ #include "quam/pack.h" -struct PackHeader { - quint32 dirOffset; - quint32 dirNum; -}; - -struct PackEntry { - std::string name; - Arc::File file; -}; - static constexpr quint32 sizeOfPackEntry = 64; -static PackHeader readPackHeader(std::istream &st) { +static std::pair readPackHeader(std::istream &st) { auto magic = readBytes<4>(st); if(magic != std::array{'P', 'A', 'C', 'K'}) { @@ -26,13 +16,10 @@ static PackHeader readPackHeader(std::istream &st) { throw FileFormatError("invalid directory size"); } - PackHeader hdr; - hdr.dirOffset = dirOffset; - hdr.dirNum = dirSize / sizeOfPackEntry; - return hdr; + return std::make_pair(dirOffset, dirSize / sizeOfPackEntry); } -static PackEntry readPackEntry(std::istream &st) { +static Arc::File readPackEntry(std::istream &st) { auto entName = readBytes<56>(st); auto entOffset = readLE(st); auto entSize = readLE(st); @@ -41,25 +28,20 @@ static PackEntry readPackEntry(std::istream &st) { st.seekg(entOffset); - Arc::File file; + Arc::File file{ntbsToString(entName)}; + file.resize(entSize); st.read(file.data(), entSize); - st.seekg(pos); - std::string name = ntbsToString(entName); - - if(name.front() == '/') { + if(file.name.front() == '/') { throw FileFormatError("empty root directory name"); } - PackEntry ent; - ent.name = std::move(name); - ent.file = std::move(file); - return ent; + return file; } -static void insertFile(Arc::Dir &dir, std::string &name, Arc::File &file) { +static void insertFile(Arc::Dir &dir, Arc::File &file, std::string name) { Option next; if(auto slash = name.find('/'); slash != std::string::npos) { @@ -69,11 +51,16 @@ static void insertFile(Arc::Dir &dir, std::string &name, Arc::File &file) { auto existing = dir.findNode(name); if(next) { - auto &ref = existing ? *existing - : dir.emplace_back(Arc::Dir{}, std::move(name)); - insertFile(std::get(ref), *next, file); + Arc::Node *ref; + if(existing) { + ref = existing; + } else { + ref = &*dir.emplace_back(new Arc::Dir(name, &dir)); + } + insertFile(*ref->getDir(), file, *next); } else if(!existing) { - dir.emplace_back(std::move(file), std::move(name)); + file.name = name; + dir.emplace_back(new Arc::File(std::move(file))); } else { throw FileFormatError("duplicate file"); } @@ -81,13 +68,13 @@ static void insertFile(Arc::Dir &dir, std::string &name, Arc::File &file) { Arc::Dir readPack(std::istream &st) { auto hdr = readPackHeader(st); - st.seekg(hdr.dirOffset); + st.seekg(hdr.first); - Arc::Dir root; + Arc::Dir root{std::string{}}; - for(quint32 i = 0; i < hdr.dirNum; i++) { - auto ent = readPackEntry(st); - insertFile(root, ent.name, ent.file); + for(quint32 i = 0; i < hdr.second; i++) { + auto file = readPackEntry(st); + insertFile(root, file, file.name); } return root; diff --git a/source/quam/project.cc b/source/quam/project.cc index 01d7131..d916c0b 100644 --- a/source/quam/project.cc +++ b/source/quam/project.cc @@ -10,7 +10,6 @@ Project::Project(std::istream &st, QErrorMessage *errors, QMdiArea *parent) : Ui::Project{}, m_arc{new Arc::Arc{st, this}}, m_root{&m_arc->root}, - m_lastDir{nullptr}, m_errors{errors}, m_model{new Arc::Model{this}}, m_sorter{new QSortFilterProxyModel{this}} @@ -26,6 +25,7 @@ Project::Project(std::istream &st, QErrorMessage *errors, QMdiArea *parent) : connect(tableView, &QAbstractItemView::doubleClicked, m_model, &Arc::Model::setDirToIndex); + dirName->setValidator(m_arc->validator); m_sorter->setSourceModel(m_model); tableView->setModel(m_sorter); m_model->setDir(m_root); @@ -35,8 +35,11 @@ Project::~Project() { } void Project::dirChanged(Arc::Dir *from, Arc::Dir *to) { - m_lastDir = from; tableView->resizeColumnsToContents(); } +bool Project::dirUp() { + return m_model->dirUp(); +} + // EOF diff --git a/source/quam/project.h b/source/quam/project.h index ece5a7a..b2fd4c1 100644 --- a/source/quam/project.h +++ b/source/quam/project.h @@ -20,12 +20,15 @@ public: explicit Project(std::istream &st, QErrorMessage *errors, QMdiArea *parent); virtual ~Project(); +public slots: + bool dirUp(); + private slots: void dirChanged(Arc::Dir *from, Arc::Dir *to); private: Arc::Arc *m_arc; - Arc::Dir *m_root, *m_lastDir; + Arc::Dir *m_root; QErrorMessage *m_errors; Arc::Model *m_model; QSortFilterProxyModel *m_sorter; diff --git a/source/quam/project.ui b/source/quam/project.ui index e646aff..021d00e 100644 --- a/source/quam/project.ui +++ b/source/quam/project.ui @@ -22,21 +22,7 @@ - - - - - - - - - - - true - - - - + diff --git a/source/quam/wad2.cc b/source/quam/wad2.cc index e2dec57..009227f 100644 --- a/source/quam/wad2.cc +++ b/source/quam/wad2.cc @@ -7,17 +7,6 @@ namespace Wad2 { }; } -struct Wad2Header { - quint32 dirNum; - quint32 dirOffset; -}; - -struct Wad2Entry { - std::string name; - Arc::FileType type; - Arc::File file; -}; - static Arc::FileType getFileType(int n) { switch(n) { case 0: return Arc::FileType::Normal; @@ -31,20 +20,19 @@ static Arc::FileType getFileType(int n) { } } -static Wad2Header readWad2Header(std::istream &st) { +static std::pair readWad2Header(std::istream &st) { auto magic = readBytes<4>(st); if(magic != std::array{'W', 'A', 'D', '2'}) { throw FileFormatError("not a wad2 file (invalid magic number)"); } - Wad2Header hdr; - hdr.dirNum = readLE(st); - hdr.dirOffset = readLE(st); - return hdr; + auto dirNum = readLE(st); + auto dirOffset = readLE(st); + return std::make_pair(dirOffset, dirNum); } -static Wad2Entry readWad2Entry(std::istream &st) { +static Arc::File readWad2Entry(std::istream &st) { auto entOffset = readLE(st); auto entSize = readLE(st); auto entCSize = readLE(st); @@ -61,28 +49,25 @@ static Wad2Entry readWad2Entry(std::istream &st) { st.seekg(entOffset); - Arc::File file; + Arc::File file{ntbsToString(entName)}; + file.resize(entSize); st.read(file.data(), entSize); - st.seekg(pos); - Wad2Entry ent; - ent.name = ntbsToString(entName); - ent.file = std::move(file); - ent.type = getFileType(entType); - return ent; + file.type = getFileType(entType); + + return file; } Arc::Dir readWad2(std::istream &st) { auto hdr = readWad2Header(st); - st.seekg(hdr.dirOffset); + st.seekg(hdr.first); - Arc::Dir root; + Arc::Dir root{std::string{}}; - for(quint32 i = 0; i < hdr.dirNum; i++) { - auto ent = readWad2Entry(st); - root.emplace_back(std::move(ent.file), std::move(ent.name), ent.type); + for(quint32 i = 0; i < hdr.second; i++) { + root.emplace_back(new Arc::File(readWad2Entry(st))); } return root;