From 511763e33fdd6449eb017a0463dde0cd2736da9e Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Fri, 25 Feb 2022 18:08:57 -0500 Subject: [PATCH] Add Dalamud asset updating support Note: Our Dalamud injection DOES not use this yet. --- src/assetupdater.cpp | 153 +++++++++++++++++++++++++++++++++++++---- src/assetupdater.h | 10 +++ src/launchercore.cpp | 7 ++ src/launchercore.h | 1 + src/settingswindow.cpp | 9 +++ src/settingswindow.h | 1 + 6 files changed, 168 insertions(+), 13 deletions(-) diff --git a/src/assetupdater.cpp b/src/assetupdater.cpp index 505940d..034e417 100644 --- a/src/assetupdater.cpp +++ b/src/assetupdater.cpp @@ -5,14 +5,20 @@ #include #include #include +#include #include #include "launchercore.h" -const QString dalamudRemotePath = "https://goatcorp.github.io/dalamud-distrib/"; -const QString dalamudVersion = "latest"; -const QString dalamudVersionPath = dalamudRemotePath + "version"; +const QString baseGoatcorpDomain = "https://goatcorp.github.io"; + +const QString dalamudRemotePath = baseGoatcorpDomain + "/dalamud-distrib"; +const QString dalamudVersion = "/latest"; +const QString dalamudVersionPath = dalamudRemotePath + "/version"; + +const QString dalamudAssetRemotePath = baseGoatcorpDomain + "/DalamudAssets"; +const QString dalamudAssetManifestPath = dalamudAssetRemotePath + "/asset.json"; const QString nativeLauncherRemotePath = "https://github.com/redstrate/nativelauncher/releases/download/"; @@ -26,6 +32,12 @@ AssetUpdater::AssetUpdater(LauncherCore& launcher) : launcher(launcher) { } void AssetUpdater::update(const ProfileSettings& profile) { + // non-dalamud users can bypass this process since it's not needed + if(!profile.enableDalamud) { + finishedUpdating(); + return; + } + const QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); @@ -54,6 +66,99 @@ void AssetUpdater::update(const ProfileSettings& profile) { } } + { + qInfo() << "Checking Dalamud assets..."; + + // we want to prevent logging in before we actually check the version + dalamudAssetNeededFilenames.append("dummy"); + + // first we want to fetch the list of assets required + QNetworkRequest request(dalamudAssetManifestPath); + + auto reply = launcher.mgr->get(request); + + connect(reply, &QNetworkReply::finished, [reply, this, profile] { + // lol, they actually provide invalid json. let's fix it if it's borked + QString badJson = reply->readAll(); + + qInfo() << reply->errorString(); + qInfo() << "Got asset manifest: " << badJson; + + auto lastCommaLoc = badJson.lastIndexOf(','); + auto lastBracketLoc = badJson.lastIndexOf('{'); + + qInfo() << "Location of last comma: " << lastCommaLoc; + qInfo() << "Location of last bracket: " << lastBracketLoc; + + // basically, if { supersedes the last , + if (lastCommaLoc > lastBracketLoc) { + qInfo() << "Dalamud server gave bad json, attempting to fix..."; + badJson.remove(lastCommaLoc, 1); + } else { + qInfo() << "Got valid json."; + } + + QJsonDocument doc = QJsonDocument::fromJson(badJson.toUtf8()); + + qInfo() << "Dalamud asset remote version" << doc.object()["Version"].toInt(); + qInfo() << "Dalamud asset local version" << profile.dalamudAssetVersion; + + remoteDalamudAssetVersion = doc.object()["Version"].toInt(); + + if(remoteDalamudAssetVersion != profile.dalamudAssetVersion) { + qInfo() << "Dalamud assets out of date."; + + dalamudAssetNeededFilenames.clear(); + + for(auto assetObject : doc.object()["Assets"].toArray()) { + { + qInfo() << "Starting download for " << assetObject.toObject()["FileName"]; + + dalamudAssetNeededFilenames.append(assetObject.toObject()["FileName"].toString()); + + QNetworkRequest assetRequest(assetObject.toObject()["Url"].toString()); + auto assetReply = launcher.mgr->get(assetRequest); + + connect(assetReply, &QNetworkReply::finished, [this, assetReply, assetObject = assetObject.toObject()] { + const QString dataDir = + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/DalamudAssets/"; + + if (!QDir().exists(dataDir)) + QDir().mkdir(dataDir); + + const QString fileName = assetObject["FileName"].toString(); + const QList dirPath = fileName.left(fileName.lastIndexOf("/")).split('/'); + + qInfo() << "Needed directories: " << dirPath; + + QString build = dataDir; + for(auto dir : dirPath) { + if (!QDir().exists(build + dir)) + QDir().mkdir(build + dir); + + build += dir + "/"; + } + + QFile file(dataDir + assetObject["FileName"].toString()); + file.open(QIODevice::WriteOnly); + file.write(assetReply->readAll()); + file.close(); + + dalamudAssetNeededFilenames.removeOne(assetObject["FileName"].toString()); + checkIfDalamudAssetsDone(); + }); + } + } + } else { + dalamudAssetNeededFilenames.clear(); + + qInfo() << "Dalamud assets up to date."; + + checkIfFinished(); + } + }); + } + // first we determine if we need dalamud const bool needsDalamud = profile.enableDalamud && (!hasDalamud || !isDalamudUpdated); @@ -62,6 +167,8 @@ void AssetUpdater::update(const ProfileSettings& profile) { // ACLs) { qInfo() << "Downloading NativeLauncher..."; + doneDownloadingNativelauncher = false; + needsInstall = true; QNetworkRequest request(nativeLauncherRemotePath + nativeLauncherVersion + @@ -74,6 +181,8 @@ void AssetUpdater::update(const ProfileSettings& profile) { // download dalamud (... duh) { qInfo() << "Downloading Dalamud..."; + doneDownloadingDalamud = false; + needsInstall = true; QNetworkRequest request(dalamudRemotePath + dalamudVersion + ".zip"); @@ -81,20 +190,10 @@ void AssetUpdater::update(const ProfileSettings& profile) { auto reply = launcher.mgr->get(request); reply->setObjectName("Dalamud"); } - } else { - // non-dalamud users can bypass this process since it's not needed - finishedUpdating(); } } void AssetUpdater::finishDownload(QNetworkReply* reply) { - const auto checkIfFinished = [=] { - if (QFile::exists(tempDir.path() + "/NativeLauncher.exe") && - QFile::exists(tempDir.path() + "/latest.zip")) { - beginInstall(); - } - }; - if (reply->objectName() == "Dalamud") { qInfo() << "Dalamud finished downloading!"; @@ -103,6 +202,7 @@ void AssetUpdater::finishDownload(QNetworkReply* reply) { file.write(reply->readAll()); file.close(); + doneDownloadingDalamud = true; checkIfFinished(); } else if (reply->objectName() == "NativeLauncher") { qInfo() << "NativeLauncher finished downloading!"; @@ -112,6 +212,7 @@ void AssetUpdater::finishDownload(QNetworkReply* reply) { file.write(reply->readAll()); file.close(); + doneDownloadingNativelauncher = true; checkIfFinished(); } else if (reply->objectName() == "DalamudVersionCheck") { QByteArray str = reply->readAll(); @@ -151,3 +252,29 @@ void AssetUpdater::beginInstall() { // STUB: install failure } } + +void AssetUpdater::checkIfDalamudAssetsDone() { + if(dalamudAssetNeededFilenames.empty()) { + qInfo() << "Finished downloading Dalamud assets."; + + const QString dataDir = + QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/DalamudAssets/"; + + QFile file(dataDir + "asset.ver"); + file.open(QIODevice::WriteOnly | QIODevice::Text); + file.write(QString::number(remoteDalamudAssetVersion).toUtf8()); + file.close(); + + checkIfFinished(); + } +} +void AssetUpdater::checkIfFinished() { + if (doneDownloadingDalamud && + doneDownloadingNativelauncher && + dalamudAssetNeededFilenames.empty()) { + if(needsInstall) + beginInstall(); + else + finishedUpdating(); + } +} diff --git a/src/assetupdater.h b/src/assetupdater.h index 1ad6b4c..11052be 100644 --- a/src/assetupdater.h +++ b/src/assetupdater.h @@ -16,6 +16,9 @@ public: void finishDownload(QNetworkReply* reply); void beginInstall(); + void checkIfDalamudAssetsDone(); + void checkIfFinished(); + signals: void finishedUpdating(); @@ -27,4 +30,11 @@ private: QString remoteDalamudVersion; QTemporaryDir tempDir; + + bool doneDownloadingDalamud = true; + bool doneDownloadingNativelauncher = true; + bool needsInstall = false; + + int remoteDalamudAssetVersion; + QList dalamudAssetNeededFilenames; }; diff --git a/src/launchercore.cpp b/src/launchercore.cpp index a5adc55..e07fe6b 100755 --- a/src/launchercore.cpp +++ b/src/launchercore.cpp @@ -323,6 +323,13 @@ void LauncherCore::readInitialInformation() { .filter("Dalamud")[0]; profile.dalamudVersion = versionString.remove("Dalamud/"); } + + if(QFile::exists(dataDir + "/DalamudAssets/asset.ver")) { + QFile assetJson(dataDir + "/DalamudAssets/asset.ver"); + assetJson.open(QFile::ReadOnly | QFile::Text); + + profile.dalamudAssetVersion = QString(assetJson.readAll()).toInt(); + } } if(settings.contains("gamePath") && settings.value("gamePath").canConvert() && !settings.value("gamePath").toString().isEmpty()) { diff --git a/src/launchercore.h b/src/launchercore.h index c343a16..0824308 100755 --- a/src/launchercore.h +++ b/src/launchercore.h @@ -48,6 +48,7 @@ struct ProfileSettings { } gamescope; QString dalamudVersion; // TODO: move out of profile settings + int dalamudAssetVersion = -1; // login bool encryptArguments = true; diff --git a/src/settingswindow.cpp b/src/settingswindow.cpp index a8605a5..e074812 100644 --- a/src/settingswindow.cpp +++ b/src/settingswindow.cpp @@ -372,6 +372,9 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, LauncherCore& core, QWidg dalamudVersionLabel = new QLabel(); dalamudBoxLayout->addRow("Dalamud Version", dalamudVersionLabel); + + dalamudAssetVersionLabel = new QLabel(); + dalamudBoxLayout->addRow("Dalamud Asset Version", dalamudAssetVersionLabel); } reloadControls(); @@ -478,6 +481,12 @@ void SettingsWindow::reloadControls() { dalamudVersionLabel->setText(profile.dalamudVersion); } + if(profile.dalamudAssetVersion == -1) { + dalamudAssetVersionLabel->setText("Dalamud assets are not installed."); + } else { + dalamudAssetVersionLabel->setText(QString::number(profile.dalamudAssetVersion)); + } + window.reloadControls(); currentlyReloadingControls = false; diff --git a/src/settingswindow.h b/src/settingswindow.h index 94df0ec..1679e32 100644 --- a/src/settingswindow.h +++ b/src/settingswindow.h @@ -54,6 +54,7 @@ private: // dalamud QCheckBox* enableDalamudBox = nullptr; QLabel* dalamudVersionLabel = nullptr; + QLabel* dalamudAssetVersionLabel = nullptr; bool currentlyReloadingControls = false;