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}; -}