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

Improve Sagasu design & indexing

Now the hashes are collected in a central database (location to be
improved) similar to FFXIV Explorer. This database needs to be generated
once and doesn't have to be regen every time Sagasu is opened like
before. This indexer currently is a separate program.

Also adds a feature to extract files from the file tree window.
This commit is contained in:
Joshua Goins 2023-10-12 19:04:19 -04:00
parent 7b59ed7f28
commit 4b19b7aeba
11 changed files with 579 additions and 141 deletions

View file

@ -35,7 +35,7 @@ ecm_setup_version(${PROJECT_VERSION}
VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/novus-version.h VERSION_HEADER ${CMAKE_CURRENT_BINARY_DIR}/novus-version.h
) )
find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Core Widgets Concurrent Core5Compat CONFIG REQUIRED) find_package(Qt6 ${QT_MIN_VERSION} COMPONENTS Core Widgets Concurrent Core5Compat Sql CONFIG REQUIRED)
find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS CoreAddons Config XmlGui) find_package(KF6 ${KF_MIN_VERSION} REQUIRED COMPONENTS CoreAddons Config XmlGui)
find_package(Vulkan REQUIRED) find_package(Vulkan REQUIRED)
find_package(glm REQUIRED) find_package(glm REQUIRED)

2
extern/libphysis vendored

@ -1 +1 @@
Subproject commit 5a5234d4c0b288caf80dfdfa07612f1a38d16532 Subproject commit aff19962aeb5d5ab7365c3e610a47ce5ab74d7cd

View file

@ -1,13 +1,27 @@
# SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com> # SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
# SPDX-License-Identifier: CC0-1.0 # SPDX-License-Identifier: CC0-1.0
add_library(novus-sagasu-static STATIC)
target_sources(novus-sagasu-static PRIVATE
include/hashdatabase.h
src/hashdatabase.cpp)
target_link_libraries(novus-sagasu-static PUBLIC physis z Qt6::Core Qt6::Sql novus-common)
target_include_directories(novus-sagasu-static PRIVATE include)
add_executable(novus-sagasu-indexer)
target_sources(novus-sagasu-indexer PRIVATE
src/indexer.cpp)
target_link_libraries(novus-sagasu-indexer PRIVATE novus-sagasu-static)
target_include_directories(novus-sagasu-indexer PRIVATE include)
add_executable(novus-sagasu) add_executable(novus-sagasu)
target_sources(novus-sagasu PRIVATE target_sources(novus-sagasu PRIVATE
src/main.cpp src/main.cpp
src/mainwindow.cpp src/mainwindow.cpp
src/filetreewindow.cpp src/filetreewindow.cpp
src/filepropertieswindow.cpp) src/filepropertieswindow.cpp
src/filetreemodel.cpp)
target_include_directories(novus-sagasu PRIVATE include) target_include_directories(novus-sagasu PRIVATE include)
target_link_libraries(novus-sagasu PRIVATE physis z Qt6::Core Qt6::Widgets novus-common) target_link_libraries(novus-sagasu PRIVATE Qt6::Concurrent novus-sagasu-static)
install(TARGETS novus-sagasu ${KF${QT_MAJOR_VERSION}_INSTALL_TARGETS_DEFAULT_ARGS}) install(TARGETS novus-sagasu ${KF${QT_MAJOR_VERSION}_INSTALL_TARGETS_DEFAULT_ARGS})

View file

@ -0,0 +1,51 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include "hashdatabase.h"
#include <QAbstractItemModel>
#include <QFutureWatcher>
struct GameData;
enum class TreeType { Root, Folder, File };
struct TreeInformation {
TreeType type;
TreeInformation *parent = nullptr;
QString name;
int row = 0;
uint32_t hash = 0;
std::vector<TreeInformation *> children;
};
class FileTreeModel : public QAbstractItemModel
{
Q_OBJECT
public:
explicit FileTreeModel(GameData *data);
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
int columnCount(const QModelIndex &parent = QModelIndex()) const override;
QModelIndex index(int row, int column = 0, const QModelIndex &parent = QModelIndex()) const override;
QModelIndex parent(const QModelIndex &child) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override;
private:
GameData *gameData = nullptr;
TreeInformation *rootItem = nullptr;
void addKnownFolder(QString string);
void addFile(TreeInformation *parentItem, uint32_t filenameHash, QString name);
void addFolder(TreeInformation *parentItem, uint32_t filenameHash);
QHash<uint32_t, TreeInformation *> knownDirHashes;
HashDatabase m_database;
};

View file

@ -6,10 +6,7 @@
#include <QMdiSubWindow> #include <QMdiSubWindow>
#include <physis.hpp> #include <physis.hpp>
struct PathPart { #include "filetreemodel.h"
uint32_t crcHash;
QMap<QString, PathPart> children;
};
class FileTreeWindow : public QWidget class FileTreeWindow : public QWidget
{ {
@ -17,20 +14,11 @@ class FileTreeWindow : public QWidget
public: public:
explicit FileTreeWindow(GameData *data, QWidget *parent = nullptr); 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);
Q_SIGNALS: Q_SIGNALS:
void openFileProperties(QString path); void openFileProperties(QString path);
void extractFile(QString path);
private:
GameData *data = nullptr;
FileTreeModel *m_fileModel;
}; };

View file

@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#pragma once
#include <QObject>
#include <QSqlDatabase>
#include <QSqlQuery>
class HashDatabase : public QObject
{
public:
HashDatabase(QObject *parent = nullptr);
void addFolder(QString folder);
void addFile(QString file);
QVector<QString> getKnownFolders();
bool knowsFile(const uint32_t i);
QString getFilename(const uint32_t i);
private:
QSqlDatabase m_db;
};

View file

@ -0,0 +1,180 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "filetreemodel.h"
#include "physis.hpp"
#include <QtConcurrent>
FileTreeModel::FileTreeModel(GameData *data)
: gameData(data)
, QAbstractItemModel()
{
rootItem = new TreeInformation();
rootItem->type = TreeType::Root;
for (auto knownFolder : m_database.getKnownFolders()) {
addKnownFolder(knownFolder);
}
auto indexEntries = physis_index_parse("/home/josh/.local/share/astra/game/{1973dab7-aa23-4af1-8a48-bfec78dd6c8e}/game/sqpack/ffxiv/000000.win32.index");
for (int i = 0; i < indexEntries.num_entries; i++) {
if (knownDirHashes.contains(indexEntries.dir_entries[i])) {
QString name;
if (m_database.knowsFile(indexEntries.filename_entries[i])) {
name = m_database.getFilename(indexEntries.filename_entries[i]);
}
addFile(knownDirHashes[indexEntries.dir_entries[i]], indexEntries.filename_entries[i], name);
} else {
addFolder(rootItem, indexEntries.dir_entries[i]);
}
}
}
int FileTreeModel::rowCount(const QModelIndex &parent) const
{
TreeInformation *parentItem;
if (parent.column() > 0)
return 0;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<TreeInformation *>(parent.internalPointer());
return parentItem->children.size();
}
int FileTreeModel::columnCount(const QModelIndex &parent) const
{
return 1;
}
QModelIndex FileTreeModel::index(int row, int column, const QModelIndex &parent) const
{
if (!hasIndex(row, column, parent))
return {};
TreeInformation *parentItem;
if (!parent.isValid())
parentItem = rootItem;
else
parentItem = static_cast<TreeInformation *>(parent.internalPointer());
TreeInformation *childItem = parentItem->children[row];
if (childItem)
return createIndex(row, column, childItem);
return {};
}
QModelIndex FileTreeModel::parent(const QModelIndex &index) const
{
if (!index.isValid())
return {};
auto childItem = static_cast<TreeInformation *>(index.internalPointer());
TreeInformation *parentItem = childItem->parent;
if (parentItem == rootItem)
return {};
return createIndex(parentItem->row, 0, parentItem);
}
QVariant FileTreeModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid())
return {};
auto item = static_cast<TreeInformation *>(index.internalPointer());
if (role == Qt::UserRole) {
// build the full path
QString path;
TreeInformation *parent = item;
while (parent != rootItem) {
if (path.isEmpty()) {
path = parent->name;
} else {
path = parent->name + QStringLiteral("/") + path;
}
parent = parent->parent;
}
return path;
} else if (role == Qt::DisplayRole) {
if (item->type == TreeType::Folder) {
if (item->name.isEmpty()) {
return QStringLiteral("Unknown Folder (%1)").arg(item->hash);
} else {
return item->name;
}
} else if (item->type == TreeType::File) {
if (item->name.isEmpty()) {
return QStringLiteral("Unknown File (%1)").arg(item->hash);
} else {
return item->name;
}
}
}
return {};
}
QVariant FileTreeModel::headerData(int section, Qt::Orientation orientation, int role) const
{
if (role == Qt::DisplayRole && orientation == Qt::Horizontal) {
if (section == 0) {
return QStringLiteral("Name");
}
}
return QAbstractItemModel::headerData(section, orientation, role);
}
void FileTreeModel::addKnownFolder(QString string)
{
auto children = string.split(QStringLiteral("/"));
QString conct = children[0];
TreeInformation *parentItem = rootItem;
for (int i = 0; i < children.size(); i++) {
if (i > 0) {
conct += QStringLiteral("/") + children[i];
}
std::string conctStd = conct.toStdString();
auto folderItem = new TreeInformation();
folderItem->name = children[i];
folderItem->type = TreeType::Folder;
folderItem->parent = parentItem;
folderItem->row = i + 1;
folderItem->hash = physis_generate_partial_hash(conctStd.c_str());
parentItem->children.push_back(folderItem);
parentItem = folderItem;
knownDirHashes[folderItem->hash] = folderItem;
}
}
void FileTreeModel::addFile(TreeInformation *parentItem, uint32_t name, QString realName)
{
auto fileItem = new TreeInformation();
fileItem->hash = name;
fileItem->name = realName;
fileItem->type = TreeType::File;
fileItem->parent = parentItem;
parentItem->children.push_back(fileItem);
}
void FileTreeModel::addFolder(TreeInformation *parentItem, uint32_t name)
{
auto fileItem = new TreeInformation();
fileItem->hash = name;
fileItem->type = TreeType::Folder;
fileItem->parent = parentItem;
parentItem->children.push_back(fileItem);
}
#include "moc_filetreemodel.cpp"

View file

@ -1,13 +1,12 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com> // SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later // SPDX-License-Identifier: GPL-3.0-or-later
#include <QDebug> #include "filetreewindow.h"
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QMenu> #include <QMenu>
#include <QTreeWidget> #include <QTreeWidget>
#include "filetreewindow.h"
FileTreeWindow::FileTreeWindow(GameData *data, QWidget *parent) FileTreeWindow::FileTreeWindow(GameData *data, QWidget *parent)
: QWidget(parent) : QWidget(parent)
, data(data) , data(data)
@ -17,129 +16,34 @@ FileTreeWindow::FileTreeWindow(GameData *data, QWidget *parent)
auto layout = new QHBoxLayout(); auto layout = new QHBoxLayout();
setLayout(layout); setLayout(layout);
auto treeWidget = new QTreeWidget(); m_fileModel = new FileTreeModel(data);
treeWidget->setHeaderLabel(QStringLiteral("Name"));
auto treeWidget = new QTreeView();
treeWidget->setModel(m_fileModel);
layout->addWidget(treeWidget); layout->addWidget(treeWidget);
addPath(QStringLiteral("common/font/AXIS_12.fdt"));
addPath(QStringLiteral("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::fromStdString(sheetName).toLower().toStdString();
addPath(QStringLiteral("exd/") + QLatin1String(nameLowercase.c_str()) + QStringLiteral(".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(QStringLiteral("exd/") + QString::fromStdString(path));
}
}
}*/
treeWidget->setContextMenuPolicy(Qt::CustomContextMenu); treeWidget->setContextMenuPolicy(Qt::CustomContextMenu);
connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, [this, treeWidget](const QPoint &pos) { connect(treeWidget, &QTreeWidget::customContextMenuRequested, this, [this, treeWidget](const QPoint &pos) {
auto *item = treeWidget->itemAt(pos); auto index = treeWidget->indexAt(pos);
if (item != nullptr) { if (index.isValid()) {
auto path = item->data(0, Qt::UserRole).toString(); auto path = m_fileModel->data(index, Qt::UserRole).toString();
qInfo() << path;
auto menu = new QMenu(); auto menu = new QMenu();
QAction *propertiesAction = menu->addAction(QStringLiteral("Properties")); auto propertiesAction = menu->addAction(QStringLiteral("Properties"));
connect(propertiesAction, &QAction::triggered, this, [=] { connect(propertiesAction, &QAction::triggered, this, [=] {
Q_EMIT openFileProperties(path); Q_EMIT openFileProperties(path);
}); });
QPoint pt(pos); auto extractAction = menu->addAction(QStringLiteral("Extract.."));
connect(extractAction, &QAction::triggered, this, [=] {
Q_EMIT extractFile(path);
});
menu->exec(treeWidget->mapToGlobal(pos)); menu->exec(treeWidget->mapToGlobal(pos));
} }
}); });
addPaths(treeWidget);
}
void FileTreeWindow::addPath(QString path)
{
auto tokens = path.split(QStringLiteral("/"));
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 + QStringLiteral("/") + 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), QStringLiteral(""));
pWidget->addTopLevelItem(item);
}
}
QTreeWidgetItem *FileTreeWindow::addPartAndChildren(const QString &qString, const PathPart &part, const QString &pathSoFar)
{
QString newPath = pathSoFar.isEmpty() ? qString : pathSoFar + QStringLiteral("/") + 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 + QStringLiteral("/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(QStringLiteral("Unknown")))
continue;
auto [childFound, newPath] = traverseUnknownPath(crcHash, part.children[path], pathSoFar + QStringLiteral("/") + path);
found |= childFound;
if (childFound)
childPath = newPath;
}
return {found, childPath};
} }
#include "moc_filetreewindow.cpp" #include "moc_filetreewindow.cpp"

View file

@ -0,0 +1,94 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "hashdatabase.h"
#include <QSqlError>
#include <physis.hpp>
HashDatabase::HashDatabase(QObject *parent)
: QObject(parent)
{
m_db = QSqlDatabase::addDatabase(QStringLiteral("QSQLITE"));
m_db.setDatabaseName(QStringLiteral("customdb.db"));
if (!m_db.open()) {
qFatal() << "Failed to open custom db!";
}
QSqlQuery query;
query.exec(QStringLiteral("CREATE TABLE IF NOT EXISTS folder_hashes (hash INTEGER PRIMARY KEY, name TEXT NOT NULL)"));
query.exec(QStringLiteral("CREATE TABLE IF NOT EXISTS file_hashes (hash INTEGER PRIMARY KEY, name TEXT NOT NULL)"));
}
void HashDatabase::addFolder(QString folder)
{
std::string folderStd = folder.toStdString();
QSqlQuery query;
query.prepare(
QStringLiteral("REPLACE INTO folder_hashes (hash, name) "
"VALUES (?, ?)"));
query.addBindValue(physis_generate_partial_hash(folderStd.c_str()));
query.addBindValue(folder);
query.exec();
}
void HashDatabase::addFile(QString file)
{
QString filename = file;
if (file.contains(QStringLiteral("/"))) {
int lastSlash = file.lastIndexOf(QStringLiteral("/"));
filename = file.sliced(lastSlash + 1, file.length() - lastSlash - 1);
}
qInfo() << filename;
std::string folderStd = filename.toStdString();
QSqlQuery query;
query.prepare(
QStringLiteral("REPLACE INTO file_hashes (hash, name) "
"VALUES (?, ?)"));
query.addBindValue(physis_generate_partial_hash(folderStd.c_str()));
query.addBindValue(filename);
query.exec();
}
QVector<QString> HashDatabase::getKnownFolders()
{
QSqlQuery query;
query.exec(QStringLiteral("SELECT name FROM folder_hashes"));
QVector<QString> folders;
while (query.next()) {
QString country = query.value(0).toString();
folders.push_back(country);
}
return folders;
}
bool HashDatabase::knowsFile(const uint32_t i)
{
QSqlQuery query;
query.prepare(QStringLiteral("SELECT COUNT(1) FROM file_hashes WHERE hash = ?;"));
query.addBindValue(i);
query.exec();
query.next();
return query.value(0) == 1;
}
QString HashDatabase::getFilename(const uint32_t i)
{
QSqlQuery query;
query.prepare(QStringLiteral("SELECT name FROM file_hashes WHERE hash = ?;"));
query.addBindValue(i);
query.exec();
query.next();
return query.value(0).toString();
}

173
sagasu/src/indexer.cpp Normal file
View file

@ -0,0 +1,173 @@
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
// SPDX-License-Identifier: GPL-3.0-or-later
#include "hashdatabase.h"
#include "settings.h"
const std::array known_folders{"common", "common/font", "exd"};
const std::array common_font{"common/font/fontIcon_Xinput.tex",
"common/font/gfdata.gfd",
"common/font/fontIcon_Ps3.tex",
"common/font/gfdata.gfd",
"common/font/fontIcon_Ps4.tex",
"common/font/gfdata.gfd",
"common/font/fontIcon_Ps5.tex",
"common/font/gfdata.gfd",
"common/font/MiedingerMid_36.fdt",
"common/font/MiedingerMid_18.fdt",
"common/font/MiedingerMid_14.fdt",
"common/font/MiedingerMid_12.fdt",
"common/font/MiedingerMid_10.fdt",
"common/font/Meidinger_40.fdt",
"common/font/Meidinger_20.fdt",
"common/font/Meidinger_16.fdt",
"common/font/=TrumpGothic_68.fdt",
"common/font/TrumpGothic_34.fdt",
"common/font/TrumpGothic_23.fdt",
"common/font/TrumpGothic_184.fdt",
"common/font/Jupiter_46.fdt",
"common/font/Jupiter_23.fdt",
"common/font/Jupiter_20.fdt",
"common/font/Jupiter_16.fdt",
"common/font/Jupiter_90.fdt",
"common/font/Jupiter_45.fdt",
"common/font/MiedingerMid_36.fdt",
"common/font/MiedingerMid_18.fdt",
"common/font/MiedingerMid_14.fdt",
"common/font/MiedingerMid_12.fdt",
"common/font/MiedingerMid_10.fdt",
"common/font/Meidinger_40.fdt",
"common/font/Meidinger_20.fdt",
"common/font/Meidinger_16.fdt",
"common/font/TrumpGothic_68.fdt",
"common/font/TrumpGothic_34.fdt",
"common/font/TrumpGothic_23.fdt",
"common/font/TrumpGothic_184.fdt",
"common/font/Jupiter_46.fdt",
"common/font/Jupiter_23.fdt",
"common/font/Jupiter_20.fdt",
"common/font/Jupiter_16.fdt",
"common/font/Jupiter_90.fdt",
"common/font/Jupiter_45.fdt",
"common/font/AXIS_18_lobby.fdt",
"common/font/AXIS_14_lobby.fdt",
"common/font/AXIS_12_lobby.fdt",
"common/font/AXIS_12_lobby.fdt",
"common/font/>MiedingerMid_18_lobby.fdt",
"common/font/L=MiedingerMid_18_lobby.fdt",
"common/font/>MiedingerMid_14_lobby.fdt",
"common/font/MiedingerMid_12_lobby.fdt",
"common/font/MiedingerMid_10_lobby.fdt",
"common/font/Meidinger_20_lobby.fdt",
"common/font/Meidinger_20_lobby.fdt",
"common/font/Meidinger_16_lobby.fdt",
"common/font/TrumpGothic_34_lobby.fdt",
"common/font/TrumpGothic_34_lobby.fdt",
"common/font/TrumpGothic_23_lobby.fdt",
"common/font/TrumpGothic_184_lobby.fdt",
"common/font/Jupiter_23_lobby.fdt",
"common/font/Jupiter_23_lobby.fdt",
"common/font/Jupiter_20_lobby.fdt",
"common/font/Jupiter_16_lobby.fdt",
"common/font/Jupiter_45_lobby.fdt",
"common/font/Jupiter_45_lobby.fdt",
"common/font/AXIS_18_lobby.fdt",
"common/font/MiedingerMid_18_lobby.fdt",
"common/font/MiedingerMid_18_lobby.fdt",
"common/font/MiedingerMid_14_lobby.fdt",
"common/font/MiedingerMid_12_lobby.fdt",
"common/font/MiedingerMid_10_lobby.fdt",
"common/font/Meidinger_20_lobby.fdt",
"common/font/Meidinger_20_lobby.fdt",
"common/font/Meidinger_16_lobby.fdt",
"common/font/TrumpGothic_34_lobby.fdt",
"common/font/TrumpGothic_34_lobby.fdt",
"common/font/TrumpGothic_23_lobby.fdt",
"common/font/TrumpGothic_184_lobby.fdt",
"common/font/Jupiter_23_lobby.fdt",
"common/font/Jupiter_23_lobby.fdt",
"common/font/Jupiter_20_lobby.fdt",
"common/font/Jupiter_16_lobby.fdt",
"common/font/Jupiter_45_lobby.fdt",
"common/font/Jupiter_45_lobby.fdt",
"common/font/AXIS_18_lobby.fdt",
"common/font/AXIS_14_lobby.fdt",
"common/font/AXIS_12_lobby.fdt",
"common/font/AXIS_12_lobby.fdt",
"common/font/MiedingerMid_36_lobby.fdt",
"common/font/MiedingerMid_18_lobby.fdt",
"common/font/MiedingerMid_14_lobby.fdt",
"common/font/MiedingerMid_12_lobby.fdt",
"common/font/MiedingerMid_10_lobby.fdt",
"common/font/Meidinger_40_lobby.fdt",
"common/font/Meidinger_20_lobby.fdt",
"common/font/Meidinger_16_lobby.fdt",
"common/font/TrumpGothic_68_lobby.fdt",
"common/font/TrumpGothic_34_lobby.fdt",
"common/font/TrumpGothic_23_lobby.fdt",
"common/font/TrumpGothic_184_lobby.fdt",
"common/font/Jupiter_46_lobby.fdt",
"common/font/Jupiter_23_lobby.fdt",
"common/font/Jupiter_20_lobby.fdt",
"common/font/Jupiter_16_lobby.fdt",
"common/font/Jupiter_90_lobby.fdt",
"common/font/Jupiter_45_lobby.fdt",
"common/font/AXIS_36_lobby.fdt",
"common/font/MiedingerMid_36_lobby.fdt",
"common/font/MiedingerMid_18_lobby.fdt",
"common/font/MiedingerMid_14_lobby.fdt",
"common/font/MiedingerMid_12_lobby.fdt",
"common/font/MiedingerMid_10_lobby.fdt",
"common/font/Meidinger_40_lobby.fdt",
"common/font/Meidinger_20_lobby.fdt",
"common/font/Meidinger_16_lobby.fdt",
"common/font/TrumpGothic_68_lobby.fdt",
"common/font/TrumpGothic_34_lobby.fdt",
"common/font/TrumpGothic_23_lobby.fdt",
"common/font/TrumpGothic_184_lobby.fdt",
"common/font/Jupiter_46_lobby.fdt",
"common/font/Jupiter_23_lobby.fdt",
"common/font/Jupiter_20_lobby.fdt",
"common/font/Jupiter_16_lobby.fdt",
"common/font/Jupiter_90_lobby.fdt",
"common/font/Jupiter_45_lobby.fdt"};
int main(int argc, char *argv[])
{
HashDatabase database;
for (auto &folder : known_folders) {
database.addFolder(QLatin1String(folder));
}
for (auto &file : common_font) {
database.addFile(QLatin1String(file));
}
/*const QString gameDir{getGameDirectory()};
const std::string gameDirStd{gameDir.toStdString()};
auto data = physis_gamedata_initialize(gameDirStd.c_str());
addPath(QStringLiteral("common/font/AXIS_12.fdt"));
addPath(QStringLiteral("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::fromStdString(sheetName).toLower().toStdString();
addPath(QStringLiteral("exd/") + QLatin1String(nameLowercase.c_str()) + QStringLiteral(".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(QStringLiteral("exd/") + QString::fromStdString(path));
}
}
}*/
}

View file

@ -3,17 +3,11 @@
#include "mainwindow.h" #include "mainwindow.h"
#include <KAboutApplicationDialog>
#include <KAboutData>
#include <QAction>
#include <QApplication> #include <QApplication>
#include <QDebug>
#include <QDesktopServices> #include <QDesktopServices>
#include <QFileDialog>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QMenuBar> #include <QMenuBar>
#include <QTableWidget>
#include <QTreeWidget>
#include <QUrl>
#include "filepropertieswindow.h" #include "filepropertieswindow.h"
#include "filetreewindow.h" #include "filetreewindow.h"
@ -29,10 +23,24 @@ MainWindow::MainWindow(GameData *data)
auto tree = new FileTreeWindow(data); auto tree = new FileTreeWindow(data);
connect(tree, &FileTreeWindow::openFileProperties, this, [=](QString path) { connect(tree, &FileTreeWindow::openFileProperties, this, [=](QString path) {
qInfo() << "opening properties window for " << path;
auto window = mdiArea->addSubWindow(new FilePropertiesWindow(data, path)); auto window = mdiArea->addSubWindow(new FilePropertiesWindow(data, path));
window->show(); window->show();
}); });
connect(tree, &FileTreeWindow::extractFile, this, [this, data](QString path) {
const QFileInfo info(path);
const QString savePath = QFileDialog::getSaveFileName(this, tr("Save File"), info.fileName(), QStringLiteral("*.%1").arg(info.completeSuffix()));
if (!savePath.isEmpty()) {
qInfo() << "Saving to" << savePath;
std::string savePathStd = path.toStdString();
auto fileData = physis_gamedata_extract_file(data, savePathStd.c_str());
QFile file(savePath);
file.open(QIODevice::WriteOnly);
file.write(reinterpret_cast<const char *>(fileData.data), fileData.size);
}
});
mdiArea->addSubWindow(tree); mdiArea->addSubWindow(tree);
} }