1
Fork 0
mirror of https://github.com/redstrate/Novus.git synced 2025-04-22 20:17:46 +00:00

Add explorer tool

This is similar to FFXIVExplorer, where you can view a list of known
files in a nice tree view.
This commit is contained in:
Joshua Goins 2022-04-14 08:24:06 -04:00
parent de2b6f6307
commit 1c77a0b06e
7 changed files with 207 additions and 3 deletions

View file

@ -37,4 +37,5 @@ endif()
add_subdirectory(renderer)
add_subdirectory(exdviewer)
add_subdirectory(mdlviewer)
add_subdirectory(mdlviewer)
add_subdirectory(explorer)

View file

@ -45,4 +45,14 @@ You must pass the path to your `sqpack` directory as the first argument.
The viewport uses Vulkan, so it must be supported on your system in order to work.
If you're running mdlviewer on macOS (where Qt builds usually don't ship with MoltenVK unfortunatey)
mdlviewer will automatically reconfigure itself to use a standalone SDL2 window.
mdlviewer will automatically reconfigure itself to use a standalone SDL2 window.
## explorer
This tool can list known files by libxiv, such as excel sheets.
### Usage
You must pass the path to your `sqpack` directory as the first argument.
`explorer.exe C:\Program Files (x86)\SquareEnix\Final Fantasy XIV\game\sqpack`

24
explorer/CMakeLists.txt Normal file
View file

@ -0,0 +1,24 @@
add_executable(explorer
src/main.cpp
src/mainwindow.cpp)
target_include_directories(explorer
PUBLIC
include)
target_link_libraries(explorer PUBLIC libxiv ${LIBRARIES} Qt5::Core Qt5::Widgets)
install(TARGETS explorer
DESTINATION "${INSTALL_BIN_PATH}")
if(WIN32)
get_target_property(QMAKE_EXE Qt5::qmake IMPORTED_LOCATION)
get_filename_component(QT_BIN_DIR "${QMAKE_EXE}" DIRECTORY)
find_program(WINDEPLOYQT_ENV_SETUP qtenv2.bat HINTS "${QT_BIN_DIR}")
find_program(WINDEPLOYQT_EXECUTABLE windeployqt HINTS "${QT_BIN_DIR}")
# Run windeployqt immediately after build
add_custom_command(TARGET explorer
POST_BUILD
COMMAND "${WINDEPLOYQT_ENV_SETUP}" && "${WINDEPLOYQT_EXECUTABLE}" \"$<TARGET_FILE:explorer>\"
)
endif()

View file

@ -0,0 +1,31 @@
#pragma once
#include <QMainWindow>
#include <QMap>
#include <QTreeWidget>
struct PathPart {
uint32_t crcHash;
QMap<QString, PathPart> children;
};
class GameData;
class MainWindow : public QMainWindow {
public:
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;
GameData& data;
void addPaths(QTreeWidget *pWidget);
QTreeWidgetItem* addPartAndChildren(const QString& qString, const PathPart& part);
};

15
explorer/src/main.cpp Normal file
View file

@ -0,0 +1,15 @@
#include <QApplication>
#include "mainwindow.h"
#include "gamedata.h"
int main(int argc, char* argv[]) {
QApplication app(argc, argv);
GameData data(argv[1]);
MainWindow w(data);
w.show();
return app.exec();
}

123
explorer/src/mainwindow.cpp Normal file
View file

@ -0,0 +1,123 @@
#include "mainwindow.h"
#include <QHBoxLayout>
#include <QTableWidget>
#include <QTreeWidget>
#include <QDebug>
#include "gamedata.h"
#include "exhparser.h"
#include "exdparser.h"
MainWindow::MainWindow(GameData& data) : data(data) {
setWindowTitle("explorer");
addPath("exd/root.exl");
for(auto sheetName : data.getAllSheetNames()) {
auto nameLowercase = QString(sheetName.c_str()).toLower().toStdString();
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);
}
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};
}

2
libxiv

@ -1 +1 @@
Subproject commit a7751a50bb71fdce2c3db9ba2d18f49f3d754300
Subproject commit ce7c99c3de4853758bd37d46b3e582b27681797a