diff --git a/sagasu/CMakeLists.txt b/sagasu/CMakeLists.txt index 24f41a5..98c7ab1 100644 --- a/sagasu/CMakeLists.txt +++ b/sagasu/CMakeLists.txt @@ -33,6 +33,7 @@ target_sources(novus-sagasu target_include_directories(novus-sagasu PRIVATE include) target_link_libraries(novus-sagasu PRIVATE + KF6::Archive novus-sagasu-static Novus::SklbPart Novus::CmpPart @@ -43,7 +44,8 @@ target_link_libraries(novus-sagasu Novus::ExdPart Novus::TexPart Physis::Logger - Qt6::Concurrent) + Qt6::Concurrent + Qt6::Network) install(FILES zone.xiv.sagasu.desktop DESTINATION ${KDE_INSTALL_APPDIR}) install(FILES zone.xiv.sagasu.svg DESTINATION ${KDE_INSTALL_FULL_ICONDIR}/hicolor/scalable/apps) diff --git a/sagasu/include/filetreewindow.h b/sagasu/include/filetreewindow.h index 2114d9c..5e1bd08 100644 --- a/sagasu/include/filetreewindow.h +++ b/sagasu/include/filetreewindow.h @@ -16,13 +16,13 @@ class FileTreeWindow : public QWidget public: explicit FileTreeWindow(HashDatabase &database, const QString &gamePath, GameData *data, QWidget *parent = nullptr); + void refreshModel(); + Q_SIGNALS: void extractFile(const QString &path); void pathSelected(const QString &path); private: - void refreshModel(); - GameData *data = nullptr; FileTreeModel *m_fileModel = nullptr; QSortFilterProxyModel *m_searchModel = nullptr; diff --git a/sagasu/include/hashdatabase.h b/sagasu/include/hashdatabase.h index 88b5208..1f237eb 100644 --- a/sagasu/include/hashdatabase.h +++ b/sagasu/include/hashdatabase.h @@ -16,7 +16,7 @@ public: void addFolder(const QString &folder); void addFile(const QString &file); - void importFileList(const QString &path); + void importFileList(const QByteArray &file); QVector getKnownFolders(); diff --git a/sagasu/include/mainwindow.h b/sagasu/include/mainwindow.h index 50b91bc..9004a73 100644 --- a/sagasu/include/mainwindow.h +++ b/sagasu/include/mainwindow.h @@ -5,9 +5,11 @@ #include #include +#include #include #include "filecache.h" +#include "filetreewindow.h" #include "hashdatabase.h" #include "novusmainwindow.h" @@ -26,6 +28,8 @@ private: QTabWidget *partHolder = nullptr; FileCache fileCache; HashDatabase m_database; + QNetworkAccessManager *m_mgr = nullptr; + FileTreeWindow *m_tree = nullptr; void refreshParts(const QString &path); }; \ No newline at end of file diff --git a/sagasu/src/hashdatabase.cpp b/sagasu/src/hashdatabase.cpp index aeaf2f1..aa5cce0 100644 --- a/sagasu/src/hashdatabase.cpp +++ b/sagasu/src/hashdatabase.cpp @@ -95,11 +95,8 @@ QString HashDatabase::getFilename(const uint32_t i) 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 fileNames, fileHashes; @@ -115,7 +112,7 @@ void HashDatabase::importFileList(const QString &path) QStringLiteral("REPLACE INTO file_hashes (hash, name) " "VALUES (?, ?)")); - QTextStream stream(&file); + QTextStream stream(file); stream.readLine(); // skip header while (!stream.atEnd()) { const QStringList parts = stream.readLine().split(QLatin1Char(',')); diff --git a/sagasu/src/mainwindow.cpp b/sagasu/src/mainwindow.cpp index d8116af..b571477 100644 --- a/sagasu/src/mainwindow.cpp +++ b/sagasu/src/mainwindow.cpp @@ -3,6 +3,7 @@ #include "mainwindow.h" +#include #include #include #include @@ -10,12 +11,13 @@ #include #include #include +#include +#include #include "cmppart.h" #include "exdpart.h" #include "exlpart.h" #include "filepropertieswindow.h" -#include "filetreewindow.h" #include "hexpart.h" #include "mdlpart.h" #include "shpkpart.h" @@ -29,14 +31,16 @@ MainWindow::MainWindow(const QString &gamePath, GameData *data) { setupMenubar(); + m_mgr = new QNetworkAccessManager(this); + auto dummyWidget = new QWidget(); setCentralWidget(dummyWidget); auto layout = new QHBoxLayout(); dummyWidget->setLayout(layout); - auto tree = new FileTreeWindow(m_database, gamePath, data); - connect(tree, &FileTreeWindow::extractFile, this, [this, data](const QString &path) { + m_tree = new FileTreeWindow(m_database, gamePath, data); + connect(m_tree, &FileTreeWindow::extractFile, this, [this, data](const QString &path) { const QFileInfo info(path); 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(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); }); - tree->setMaximumWidth(200); - layout->addWidget(tree); + m_tree->setMaximumWidth(200); + layout->addWidget(m_tree); partHolder = new QTabWidget(); partHolder->setMinimumWidth(800); @@ -132,7 +136,11 @@ void MainWindow::setupFileMenu(QMenu *menu) 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); }); @@ -140,8 +148,54 @@ void MainWindow::setupFileMenu(QMenu *menu) auto downloadList = menu->addAction(QStringLiteral("Download Path List...")); downloadList->setIcon(QIcon::fromTheme(QStringLiteral("document-open"))); 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 ResLogger.this process usually takes a few minutes. The program " + "may freeze. Please keep it open until the operation is finished.

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(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); + }); }); }