mirror of
https://github.com/redstrate/Novus.git
synced 2025-04-23 20:47:45 +00:00
sagasu: Allow downloading path lists directly from ResLogger
The path list is now refreshed when importing too, which makes it even easier to get started.
This commit is contained in:
parent
ecfe13cade
commit
5d455c4ce0
6 changed files with 75 additions and 18 deletions
|
@ -33,6 +33,7 @@ target_sources(novus-sagasu
|
||||||
target_include_directories(novus-sagasu PRIVATE include)
|
target_include_directories(novus-sagasu PRIVATE include)
|
||||||
target_link_libraries(novus-sagasu
|
target_link_libraries(novus-sagasu
|
||||||
PRIVATE
|
PRIVATE
|
||||||
|
KF6::Archive
|
||||||
novus-sagasu-static
|
novus-sagasu-static
|
||||||
Novus::SklbPart
|
Novus::SklbPart
|
||||||
Novus::CmpPart
|
Novus::CmpPart
|
||||||
|
@ -43,7 +44,8 @@ target_link_libraries(novus-sagasu
|
||||||
Novus::ExdPart
|
Novus::ExdPart
|
||||||
Novus::TexPart
|
Novus::TexPart
|
||||||
Physis::Logger
|
Physis::Logger
|
||||||
Qt6::Concurrent)
|
Qt6::Concurrent
|
||||||
|
Qt6::Network)
|
||||||
|
|
||||||
install(FILES zone.xiv.sagasu.desktop DESTINATION ${KDE_INSTALL_APPDIR})
|
install(FILES zone.xiv.sagasu.desktop DESTINATION ${KDE_INSTALL_APPDIR})
|
||||||
install(FILES zone.xiv.sagasu.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/hicolor/scalable/apps)
|
install(FILES zone.xiv.sagasu.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/hicolor/scalable/apps)
|
||||||
|
|
|
@ -16,13 +16,13 @@ class FileTreeWindow : public QWidget
|
||||||
public:
|
public:
|
||||||
explicit FileTreeWindow(HashDatabase &database, const QString &gamePath, GameData *data, QWidget *parent = nullptr);
|
explicit FileTreeWindow(HashDatabase &database, const QString &gamePath, GameData *data, QWidget *parent = nullptr);
|
||||||
|
|
||||||
|
void refreshModel();
|
||||||
|
|
||||||
Q_SIGNALS:
|
Q_SIGNALS:
|
||||||
void extractFile(const QString &path);
|
void extractFile(const QString &path);
|
||||||
void pathSelected(const QString &path);
|
void pathSelected(const QString &path);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void refreshModel();
|
|
||||||
|
|
||||||
GameData *data = nullptr;
|
GameData *data = nullptr;
|
||||||
FileTreeModel *m_fileModel = nullptr;
|
FileTreeModel *m_fileModel = nullptr;
|
||||||
QSortFilterProxyModel *m_searchModel = nullptr;
|
QSortFilterProxyModel *m_searchModel = nullptr;
|
||||||
|
|
|
@ -16,7 +16,7 @@ public:
|
||||||
|
|
||||||
void addFolder(const QString &folder);
|
void addFolder(const QString &folder);
|
||||||
void addFile(const QString &file);
|
void addFile(const QString &file);
|
||||||
void importFileList(const QString &path);
|
void importFileList(const QByteArray &file);
|
||||||
|
|
||||||
QVector<QString> getKnownFolders();
|
QVector<QString> getKnownFolders();
|
||||||
|
|
||||||
|
|
|
@ -5,9 +5,11 @@
|
||||||
|
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
#include <QMdiArea>
|
#include <QMdiArea>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
#include <QTreeWidget>
|
#include <QTreeWidget>
|
||||||
|
|
||||||
#include "filecache.h"
|
#include "filecache.h"
|
||||||
|
#include "filetreewindow.h"
|
||||||
#include "hashdatabase.h"
|
#include "hashdatabase.h"
|
||||||
#include "novusmainwindow.h"
|
#include "novusmainwindow.h"
|
||||||
|
|
||||||
|
@ -26,6 +28,8 @@ private:
|
||||||
QTabWidget *partHolder = nullptr;
|
QTabWidget *partHolder = nullptr;
|
||||||
FileCache fileCache;
|
FileCache fileCache;
|
||||||
HashDatabase m_database;
|
HashDatabase m_database;
|
||||||
|
QNetworkAccessManager *m_mgr = nullptr;
|
||||||
|
FileTreeWindow *m_tree = nullptr;
|
||||||
|
|
||||||
void refreshParts(const QString &path);
|
void refreshParts(const QString &path);
|
||||||
};
|
};
|
|
@ -95,11 +95,8 @@ QString HashDatabase::getFilename(const uint32_t i)
|
||||||
return query.value(0).toString();
|
return query.value(0).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
void HashDatabase::importFileList(const QString &path)
|
void HashDatabase::importFileList(const QByteArray &file)
|
||||||
{
|
{
|
||||||
QFile file(path);
|
|
||||||
file.open(QIODevice::ReadOnly);
|
|
||||||
|
|
||||||
QVariantList folderNames, folderHashes;
|
QVariantList folderNames, folderHashes;
|
||||||
QVariantList fileNames, fileHashes;
|
QVariantList fileNames, fileHashes;
|
||||||
|
|
||||||
|
@ -115,7 +112,7 @@ void HashDatabase::importFileList(const QString &path)
|
||||||
QStringLiteral("REPLACE INTO file_hashes (hash, name) "
|
QStringLiteral("REPLACE INTO file_hashes (hash, name) "
|
||||||
"VALUES (?, ?)"));
|
"VALUES (?, ?)"));
|
||||||
|
|
||||||
QTextStream stream(&file);
|
QTextStream stream(file);
|
||||||
stream.readLine(); // skip header
|
stream.readLine(); // skip header
|
||||||
while (!stream.atEnd()) {
|
while (!stream.atEnd()) {
|
||||||
const QStringList parts = stream.readLine().split(QLatin1Char(','));
|
const QStringList parts = stream.readLine().split(QLatin1Char(','));
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include "mainwindow.h"
|
#include "mainwindow.h"
|
||||||
|
|
||||||
|
#include <KZip>
|
||||||
#include <QApplication>
|
#include <QApplication>
|
||||||
#include <QDesktopServices>
|
#include <QDesktopServices>
|
||||||
#include <QFileDialog>
|
#include <QFileDialog>
|
||||||
|
@ -10,12 +11,13 @@
|
||||||
#include <QLabel>
|
#include <QLabel>
|
||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
#include <QMessageBox>
|
#include <QMessageBox>
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include <QTemporaryDir>
|
||||||
|
|
||||||
#include "cmppart.h"
|
#include "cmppart.h"
|
||||||
#include "exdpart.h"
|
#include "exdpart.h"
|
||||||
#include "exlpart.h"
|
#include "exlpart.h"
|
||||||
#include "filepropertieswindow.h"
|
#include "filepropertieswindow.h"
|
||||||
#include "filetreewindow.h"
|
|
||||||
#include "hexpart.h"
|
#include "hexpart.h"
|
||||||
#include "mdlpart.h"
|
#include "mdlpart.h"
|
||||||
#include "shpkpart.h"
|
#include "shpkpart.h"
|
||||||
|
@ -29,14 +31,16 @@ MainWindow::MainWindow(const QString &gamePath, GameData *data)
|
||||||
{
|
{
|
||||||
setupMenubar();
|
setupMenubar();
|
||||||
|
|
||||||
|
m_mgr = new QNetworkAccessManager(this);
|
||||||
|
|
||||||
auto dummyWidget = new QWidget();
|
auto dummyWidget = new QWidget();
|
||||||
setCentralWidget(dummyWidget);
|
setCentralWidget(dummyWidget);
|
||||||
|
|
||||||
auto layout = new QHBoxLayout();
|
auto layout = new QHBoxLayout();
|
||||||
dummyWidget->setLayout(layout);
|
dummyWidget->setLayout(layout);
|
||||||
|
|
||||||
auto tree = new FileTreeWindow(m_database, gamePath, data);
|
m_tree = new FileTreeWindow(m_database, gamePath, data);
|
||||||
connect(tree, &FileTreeWindow::extractFile, this, [this, data](const QString &path) {
|
connect(m_tree, &FileTreeWindow::extractFile, this, [this, data](const QString &path) {
|
||||||
const QFileInfo info(path);
|
const QFileInfo info(path);
|
||||||
|
|
||||||
const QString savePath = QFileDialog::getSaveFileName(this, tr("Save File"), info.fileName(), QStringLiteral("*.%1").arg(info.completeSuffix()));
|
const QString savePath = QFileDialog::getSaveFileName(this, tr("Save File"), info.fileName(), QStringLiteral("*.%1").arg(info.completeSuffix()));
|
||||||
|
@ -51,11 +55,11 @@ MainWindow::MainWindow(const QString &gamePath, GameData *data)
|
||||||
file.write(reinterpret_cast<const char *>(fileData.data), fileData.size);
|
file.write(reinterpret_cast<const char *>(fileData.data), fileData.size);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
connect(tree, &FileTreeWindow::pathSelected, this, [this](const QString &path) {
|
connect(m_tree, &FileTreeWindow::pathSelected, this, [this](const QString &path) {
|
||||||
refreshParts(path);
|
refreshParts(path);
|
||||||
});
|
});
|
||||||
tree->setMaximumWidth(200);
|
m_tree->setMaximumWidth(200);
|
||||||
layout->addWidget(tree);
|
layout->addWidget(m_tree);
|
||||||
|
|
||||||
partHolder = new QTabWidget();
|
partHolder = new QTabWidget();
|
||||||
partHolder->setMinimumWidth(800);
|
partHolder->setMinimumWidth(800);
|
||||||
|
@ -132,7 +136,11 @@ void MainWindow::setupFileMenu(QMenu *menu)
|
||||||
QMessageBox::Ok,
|
QMessageBox::Ok,
|
||||||
QMessageBox::Ok);
|
QMessageBox::Ok);
|
||||||
|
|
||||||
m_database.importFileList(fileName);
|
QFile file(fileName);
|
||||||
|
file.open(QIODevice::ReadOnly);
|
||||||
|
|
||||||
|
m_database.importFileList(file.readAll());
|
||||||
|
m_tree->refreshModel();
|
||||||
|
|
||||||
QMessageBox::information(this, QStringLiteral("Import Complete"), QStringLiteral("Successfully imported path list!"), QMessageBox::Ok, QMessageBox::Ok);
|
QMessageBox::information(this, QStringLiteral("Import Complete"), QStringLiteral("Successfully imported path list!"), QMessageBox::Ok, QMessageBox::Ok);
|
||||||
});
|
});
|
||||||
|
@ -140,8 +148,54 @@ void MainWindow::setupFileMenu(QMenu *menu)
|
||||||
auto downloadList = menu->addAction(QStringLiteral("Download Path List..."));
|
auto downloadList = menu->addAction(QStringLiteral("Download Path List..."));
|
||||||
downloadList->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
|
downloadList->setIcon(QIcon::fromTheme(QStringLiteral("document-open")));
|
||||||
connect(downloadList, &QAction::triggered, [this] {
|
connect(downloadList, &QAction::triggered, [this] {
|
||||||
auto fileName = QFileDialog::getOpenFileName(nullptr, QStringLiteral("Open Path List"), QStringLiteral("~"));
|
const int ret =
|
||||||
|
QMessageBox::information(this,
|
||||||
|
QStringLiteral("Download Confirmation"),
|
||||||
|
QStringLiteral("This will download the path list from <a "
|
||||||
|
"href=\"https://rl2.perchbird.dev/\">ResLogger</a>.this process usually takes a few minutes. The program "
|
||||||
|
"may freeze. Please keep it open until the operation is finished.<br><br>Continue?"),
|
||||||
|
QMessageBox::Ok | QMessageBox::Cancel,
|
||||||
|
QMessageBox::Ok);
|
||||||
|
|
||||||
m_database.importFileList(fileName);
|
if (ret != QMessageBox::Ok) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QUrl url;
|
||||||
|
url.setScheme(QStringLiteral("https"));
|
||||||
|
url.setHost(QStringLiteral("rl2.perchbird.dev"));
|
||||||
|
url.setPath(QStringLiteral("/download/export/CurrentPathListWithHashes.zip"));
|
||||||
|
|
||||||
|
// TODO: Use Qcoro?
|
||||||
|
auto reply = m_mgr->get(QNetworkRequest(url));
|
||||||
|
connect(reply, &QNetworkReply::finished, this, [this, reply] {
|
||||||
|
qInfo() << "Finished downloading path list!";
|
||||||
|
|
||||||
|
QTemporaryDir tempDir;
|
||||||
|
|
||||||
|
QFile file(tempDir.filePath(QStringLiteral("CurrentPathListWithHashes.zip")));
|
||||||
|
file.open(QIODevice::WriteOnly);
|
||||||
|
file.write(reply->readAll());
|
||||||
|
file.close();
|
||||||
|
|
||||||
|
KZip archive(file.fileName());
|
||||||
|
if (!archive.open(QIODevice::ReadOnly)) {
|
||||||
|
// TODO: these should show as message boxes
|
||||||
|
qFatal() << "Failed to open path list zip!" << archive.errorString();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KArchiveFile *root = dynamic_cast<const KArchiveFile *>(archive.directory()->entry(QStringLiteral("CurrentPathListWithHashes.csv")));
|
||||||
|
m_database.importFileList(root->data());
|
||||||
|
m_tree->refreshModel();
|
||||||
|
|
||||||
|
archive.close();
|
||||||
|
|
||||||
|
QMessageBox::information(this,
|
||||||
|
QStringLiteral("Import Complete"),
|
||||||
|
QStringLiteral("Successfully downloaded and imported path list!"),
|
||||||
|
QMessageBox::Ok,
|
||||||
|
QMessageBox::Ok);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue