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:
parent
7407d26247
commit
05dfd81581
8 changed files with 236 additions and 131 deletions
|
@ -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}")
|
||||
|
|
13
explorer/include/filepropertieswindow.h
Normal file
13
explorer/include/filepropertieswindow.h
Normal 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;
|
||||
};
|
33
explorer/include/filetreewindow.h
Normal file
33
explorer/include/filetreewindow.h
Normal 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);
|
||||
};
|
|
@ -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;
|
||||
};
|
27
explorer/src/filepropertieswindow.cpp
Normal file
27
explorer/src/filepropertieswindow.cpp
Normal 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"
|
133
explorer/src/filetreewindow.cpp
Normal file
133
explorer/src/filetreewindow.cpp
Normal 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"
|
|
@ -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();
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue