From 05dfd81581bd4a6f233876789b6d8e379bf4d90a Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sun, 9 Apr 2023 15:32:09 -0400 Subject: [PATCH] Overhaul file explorer File Explorer is still in functionality limbo, but this at least removes the libxiv dependency and will make it easier to use the new GUI parts system in the future. --- explorer/CMakeLists.txt | 8 +- explorer/include/filepropertieswindow.h | 13 +++ explorer/include/filetreewindow.h | 33 ++++++ explorer/include/mainwindow.h | 21 +--- explorer/src/filepropertieswindow.cpp | 27 +++++ explorer/src/filetreewindow.cpp | 133 ++++++++++++++++++++++++ explorer/src/main.cpp | 10 +- explorer/src/mainwindow.cpp | 122 +++------------------- 8 files changed, 236 insertions(+), 131 deletions(-) create mode 100644 explorer/include/filepropertieswindow.h create mode 100644 explorer/include/filetreewindow.h create mode 100644 explorer/src/filepropertieswindow.cpp create mode 100644 explorer/src/filetreewindow.cpp diff --git a/explorer/CMakeLists.txt b/explorer/CMakeLists.txt index 22c291a..b64d5d5 100644 --- a/explorer/CMakeLists.txt +++ b/explorer/CMakeLists.txt @@ -1,10 +1,14 @@ +set(CMAKE_INCLUDE_CURRENT_DIR ON) + add_executable(explorer src/main.cpp - src/mainwindow.cpp) + src/mainwindow.cpp + src/filetreewindow.cpp + src/filepropertieswindow.cpp) target_include_directories(explorer PUBLIC include) -target_link_libraries(explorer PUBLIC libxiv ${LIBRARIES} Qt5::Core Qt5::Widgets) +target_link_libraries(explorer PUBLIC physis z ${LIBRARIES} Qt5::Core Qt5::Widgets) install(TARGETS explorer DESTINATION "${INSTALL_BIN_PATH}") diff --git a/explorer/include/filepropertieswindow.h b/explorer/include/filepropertieswindow.h new file mode 100644 index 0000000..f451a42 --- /dev/null +++ b/explorer/include/filepropertieswindow.h @@ -0,0 +1,13 @@ +#pragma once + +#include +#include + +class FilePropertiesWindow : public QWidget { + Q_OBJECT +public: + explicit FilePropertiesWindow(GameData* data, QString path, QWidget *parent = nullptr); + +private: + GameData* data = nullptr; +}; \ No newline at end of file diff --git a/explorer/include/filetreewindow.h b/explorer/include/filetreewindow.h new file mode 100644 index 0000000..b9ab171 --- /dev/null +++ b/explorer/include/filetreewindow.h @@ -0,0 +1,33 @@ +#pragma once + +#include +#include + +struct PathPart { + uint32_t crcHash; + QMap children; +}; + + +class FileTreeWindow : public QWidget { + Q_OBJECT +public: + explicit FileTreeWindow(GameData* data, QWidget *parent = nullptr); + +private: + GameData* data = nullptr; + + void addPath(QString path); + void addUnknownPath(QString knownDirectory, uint32_t crcHash); + void traversePart(QList tokens, PathPart& part, QString pathSoFar); + std::tuple traverseUnknownPath(uint32_t crcHash, PathPart& part, QString pathSoFar); + + QMap rootParts; + + void addPaths(QTreeWidget *pWidget); + + QTreeWidgetItem* addPartAndChildren(const QString& qString, const PathPart& part, const QString& pathSoFar); + +signals: + void openFileProperties(QString path); +}; \ No newline at end of file diff --git a/explorer/include/mainwindow.h b/explorer/include/mainwindow.h index 0bd5d50..b735c1b 100644 --- a/explorer/include/mainwindow.h +++ b/explorer/include/mainwindow.h @@ -3,29 +3,18 @@ #include #include #include +#include -struct PathPart { - uint32_t crcHash; - QMap children; -}; - -class GameData; +struct GameData; class MainWindow : public QMainWindow { public: - MainWindow(GameData& data); + MainWindow(GameData* data); private: - void addPath(QString path); - void addUnknownPath(QString knownDirectory, uint32_t crcHash); - void traversePart(QList tokens, PathPart& part, QString pathSoFar); - std::tuple traverseUnknownPath(uint32_t crcHash, PathPart& part, QString pathSoFar); - QMap rootParts; + QMdiArea* mdiArea = nullptr; - GameData& data; - void addPaths(QTreeWidget *pWidget); - - QTreeWidgetItem* addPartAndChildren(const QString& qString, const PathPart& part); + GameData* data; }; \ No newline at end of file diff --git a/explorer/src/filepropertieswindow.cpp b/explorer/src/filepropertieswindow.cpp new file mode 100644 index 0000000..f14d7c3 --- /dev/null +++ b/explorer/src/filepropertieswindow.cpp @@ -0,0 +1,27 @@ +#include +#include +#include +#include +#include + +#include "filepropertieswindow.h" + +FilePropertiesWindow::FilePropertiesWindow(GameData *data, QString path, QWidget *parent) : QWidget(parent), data(data) { + setWindowTitle("Properties for " + path); + + auto layout = new QFormLayout(); + setLayout(layout); + + auto pathLabel = new QLabel(path); + layout->addRow("Path", pathLabel); + + auto typeLabel = new QLabel("Unknown type"); + layout->addRow("Type", typeLabel); + + auto file = physis_gamedata_extract_file(data, path.toStdString().c_str()); + + auto sizeLabel = new QLabel(QString::number(file.size)); + layout->addRow("Size (in bytes)", sizeLabel); +} + +#include "moc_filepropertieswindow.cpp" \ No newline at end of file diff --git a/explorer/src/filetreewindow.cpp b/explorer/src/filetreewindow.cpp new file mode 100644 index 0000000..6726272 --- /dev/null +++ b/explorer/src/filetreewindow.cpp @@ -0,0 +1,133 @@ +#include +#include +#include +#include + +#include "filetreewindow.h" + +FileTreeWindow::FileTreeWindow(GameData *data, QWidget *parent) : QWidget(parent), data(data) { + setWindowTitle("File Tree"); + + auto layout = new QHBoxLayout(); + setLayout(layout); + + auto treeWidget = new QTreeWidget(); + treeWidget->setHeaderLabel("Name"); + layout->addWidget(treeWidget); + + addPath("common/font/AXIS_12.fdt"); + + addPath("exd/root.exl"); + + auto sheetNames = physis_gamedata_get_all_sheet_names(data); + + for(int i = 0; i < sheetNames.name_count; i++) { + auto sheetName = sheetNames.names[i]; + auto nameLowercase = QString(sheetName).toLower().toStdString(); + + addPath("exd/" + QString(nameLowercase.c_str()) + ".exh"); + + auto exh = physis_gamedata_read_excel_sheet_header(data, sheetName); + for (int j = 0; j < exh.page_count; j++) { + for (int z = 0; z < exh.language_count; z++) { + std::string path = physis_gamedata_get_exd_filename(nameLowercase.c_str(), &exh, exh.languages[z], j); + + addPath(("exd/" + path).c_str()); + } + } + } + + treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); + connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, [this, treeWidget](const QPoint& pos) { + auto *item = treeWidget->itemAt(pos); + + if (item != nullptr) { + auto path = item->data(0, Qt::UserRole).toString(); + qInfo() << path; + + auto menu = new QMenu(); + + QAction *propertiesAction = menu->addAction("Properties"); + connect(propertiesAction, &QAction::triggered, this, [=] { + emit openFileProperties(path); + }); + + QPoint pt(pos); + menu->exec(treeWidget->mapToGlobal(pos)); + } + }); + + addPaths(treeWidget); +} + +void FileTreeWindow::addPath(QString path) { + auto tokens = path.split('/'); + auto nextToken = tokens[0]; + tokens.pop_front(); + + traversePart(tokens, rootParts[nextToken], nextToken); +} + +void FileTreeWindow::traversePart(QList tokens, PathPart& part, QString pathSoFar) { + if(tokens.empty()) + return; + + auto nextToken = tokens[0]; + tokens.pop_front(); + + pathSoFar = pathSoFar + "/" + nextToken; + part.children[nextToken].crcHash = physis_calculate_hash(pathSoFar.toStdString().c_str()); + + traversePart(tokens, part.children[nextToken], pathSoFar); +} + +void FileTreeWindow::addPaths(QTreeWidget *pWidget) { + for(const auto& name : rootParts.keys()) { + auto item = addPartAndChildren(name, rootParts.value(name), ""); + pWidget->addTopLevelItem(item); + } +} + +QTreeWidgetItem* FileTreeWindow::addPartAndChildren(const QString& qString, const PathPart& part, const QString& pathSoFar) { + QString newPath = pathSoFar.isEmpty() ? qString : pathSoFar + "/" + qString; + + auto item = new QTreeWidgetItem(); + item->setData(0, Qt::UserRole, newPath); + item->setText(0, qString); + + for(const auto& name : part.children.keys()) { + auto childItem = addPartAndChildren(name, part.children.value(name), newPath); + item->addChild(childItem); + } + + return item; +} + +void FileTreeWindow::addUnknownPath(QString knownDirectory, uint32_t crcHash) { + auto [found, path] = traverseUnknownPath(crcHash, rootParts[knownDirectory], knownDirectory); + if(found) + addPath(path); + else + addPath(knownDirectory + "/Unknown File Hash " + QString::number(crcHash)); +} + +std::tuple FileTreeWindow::traverseUnknownPath(uint32_t crcHash, PathPart &part, QString pathSoFar) { + if(part.crcHash == crcHash) + return {true, pathSoFar}; + + bool found = false; + QString childPath = pathSoFar; + for(auto path : part.children.keys()) { + if(path.contains("Unknown")) + continue; + + auto [childFound, newPath] = traverseUnknownPath(crcHash, part.children[path], pathSoFar + "/" + path); + found |= childFound; + if(childFound) + childPath = newPath; + } + + return {found, childPath}; +} + +#include "moc_filetreewindow.cpp" \ No newline at end of file diff --git a/explorer/src/main.cpp b/explorer/src/main.cpp index 63e494d..3b494dc 100644 --- a/explorer/src/main.cpp +++ b/explorer/src/main.cpp @@ -1,14 +1,18 @@ #include +#include +#include + #include "mainwindow.h" -#include "gamedata.h" int main(int argc, char* argv[]) { QApplication app(argc, argv); - GameData data(argv[1]); + physis_initialize_logging(); - MainWindow w(data); + app.setStyle("Windows"); + + MainWindow w(physis_gamedata_initialize(argv[1])); w.show(); return app.exec(); diff --git a/explorer/src/mainwindow.cpp b/explorer/src/mainwindow.cpp index 1b72398..6a4d507 100644 --- a/explorer/src/mainwindow.cpp +++ b/explorer/src/mainwindow.cpp @@ -1,123 +1,25 @@ #include "mainwindow.h" +#include "filetreewindow.h" +#include "filepropertieswindow.h" #include #include #include #include -#include "gamedata.h" -#include "exhparser.h" -#include "exdparser.h" - -MainWindow::MainWindow(GameData& data) : data(data) { +MainWindow::MainWindow(GameData* data) : data(data) { setWindowTitle("explorer"); - addPath("exd/root.exl"); + mdiArea = new QMdiArea(); + setCentralWidget(mdiArea); - for(auto sheetName : data.getAllSheetNames()) { - auto nameLowercase = QString(sheetName.c_str()).toLower().toStdString(); + auto tree = new FileTreeWindow(data); + connect(tree, &FileTreeWindow::openFileProperties, this, [=](QString path) { + qInfo() << "opening properties window for " << path; + auto window = mdiArea->addSubWindow(new FilePropertiesWindow(data, path)); + window->show(); + }); - addPath("exd/" + QString(nameLowercase.c_str()) + ".exh"); - - auto exh = *data.readExcelSheet(sheetName); - for(auto page : exh.pages) { - for(auto language : exh.language) { - std::string path; - if (language == Language::None) { - path = getEXDFilename(exh, nameLowercase, "", page); - } else { - path = getEXDFilename(exh, nameLowercase, getLanguageCode(language), page); - } - - addPath(("exd/" + path).c_str()); - } - } - } - - addPath("common/font/AXIS_12.fdt"); - - auto commonIndex = data.getIndexListing("common"); - for(auto entry : commonIndex.entries) { - addUnknownPath("common", entry.hash); - } - - auto dummyWidget = new QWidget(); - setCentralWidget(dummyWidget); - - auto layout = new QHBoxLayout(); - dummyWidget->setLayout(layout); - - auto treeWidget = new QTreeWidget(); - treeWidget->setHeaderLabel("Name"); - - addPaths(treeWidget); - - layout->addWidget(treeWidget); + mdiArea->addSubWindow(tree); } -void MainWindow::addPath(QString path) { - auto tokens = path.split('/'); - auto nextToken = tokens[0]; - tokens.pop_front(); - - traversePart(tokens, rootParts[nextToken], nextToken); -} - -void MainWindow::traversePart(QList tokens, PathPart& part, QString pathSoFar) { - if(tokens.empty()) - return; - - auto nextToken = tokens[0]; - tokens.pop_front(); - - pathSoFar = pathSoFar + "/" + nextToken; - part.children[nextToken].crcHash = data.calculateHash(pathSoFar.toStdString()); - - traversePart(tokens, part.children[nextToken], pathSoFar); -} - -void MainWindow::addPaths(QTreeWidget *pWidget) { - for(const auto& name : rootParts.keys()) { - auto item = addPartAndChildren(name, rootParts.value(name)); - pWidget->addTopLevelItem(item); - } -} - -QTreeWidgetItem* MainWindow::addPartAndChildren(const QString& qString, const PathPart& part) { - auto item = new QTreeWidgetItem(); - item->setText(0, qString); - - for(const auto& name : part.children.keys()) { - auto childItem = addPartAndChildren(name, part.children.value(name)); - item->addChild(childItem); - } - - return item; -} - -void MainWindow::addUnknownPath(QString knownDirectory, uint32_t crcHash) { - auto [found, path] = traverseUnknownPath(crcHash, rootParts[knownDirectory], knownDirectory); - if(found) - addPath(path); - else - addPath(knownDirectory + "/Unknown File Hash " + QString::number(crcHash)); -} - -std::tuple MainWindow::traverseUnknownPath(uint32_t crcHash, PathPart &part, QString pathSoFar) { - if(part.crcHash == crcHash) - return {true, pathSoFar}; - - bool found = false; - QString childPath = pathSoFar; - for(auto path : part.children.keys()) { - if(path.contains("Unknown")) - continue; - - auto [childFound, newPath] = traverseUnknownPath(crcHash, part.children[path], pathSoFar + "/" + path); - found |= childFound; - if(childFound) - childPath = newPath; - } - - return {found, childPath}; -}