1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-04-24 13:07:44 +00:00

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.
This commit is contained in:
Joshua Goins 2023-04-09 15:32:09 -04:00
parent 7407d26247
commit 05dfd81581
8 changed files with 236 additions and 131 deletions

View file

@ -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}")

View file

@ -0,0 +1,13 @@
#pragma once
#include <QMdiSubWindow>
#include <physis.hpp>
class FilePropertiesWindow : public QWidget {
Q_OBJECT
public:
explicit FilePropertiesWindow(GameData* data, QString path, QWidget *parent = nullptr);
private:
GameData* data = nullptr;
};

View file

@ -0,0 +1,33 @@
#pragma once
#include <QMdiSubWindow>
#include <physis.hpp>
struct PathPart {
uint32_t crcHash;
QMap<QString, PathPart> 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<QString> tokens, PathPart& part, QString pathSoFar);
std::tuple<bool, QString> traverseUnknownPath(uint32_t crcHash, PathPart& part, QString pathSoFar);
QMap<QString, PathPart> rootParts;
void addPaths(QTreeWidget *pWidget);
QTreeWidgetItem* addPartAndChildren(const QString& qString, const PathPart& part, const QString& pathSoFar);
signals:
void openFileProperties(QString path);
};

View file

@ -3,29 +3,18 @@
#include <QMainWindow>
#include <QMap>
#include <QTreeWidget>
#include <QMdiArea>
struct PathPart {
uint32_t crcHash;
QMap<QString, PathPart> 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<QString> tokens, PathPart& part, QString pathSoFar);
std::tuple<bool, QString> traverseUnknownPath(uint32_t crcHash, PathPart& part, QString pathSoFar);
QMap<QString, PathPart> rootParts;
QMdiArea* mdiArea = nullptr;
GameData& data;
void addPaths(QTreeWidget *pWidget);
QTreeWidgetItem* addPartAndChildren(const QString& qString, const PathPart& part);
GameData* data;
};

View file

@ -0,0 +1,27 @@
#include <QHBoxLayout>
#include <QTreeWidget>
#include <QLabel>
#include <QFormLayout>
#include <QDebug>
#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"

View file

@ -0,0 +1,133 @@
#include <QHBoxLayout>
#include <QTreeWidget>
#include <QDebug>
#include <QMenu>
#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<QString> 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<bool, QString> 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"

View file

@ -1,14 +1,18 @@
#include <QApplication>
#include <physis.hpp>
#include <QStyle>
#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();

View file

@ -1,123 +1,25 @@
#include "mainwindow.h"
#include "filetreewindow.h"
#include "filepropertieswindow.h"
#include <QHBoxLayout>
#include <QTableWidget>
#include <QTreeWidget>
#include <QDebug>
#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<QString> 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<bool, QString> 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};
}