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

Adapt patcher to the new Kirigami interface, and perform hash checks

This commit is contained in:
Joshua Goins 2023-07-30 14:56:24 -04:00
parent dd1f22ab31
commit aa2a50b50b
6 changed files with 102 additions and 57 deletions

View file

@ -149,6 +149,8 @@ signals:
void showNewsListChanged();
void loginError(QString message);
void stageChanged(QString message);
void stageIndeterminate();
void stageDeterminate(int min, int max, int value);
void newsChanged();
private:

View file

@ -4,14 +4,16 @@
#include <QString>
#include <physis.hpp>
class LauncherCore;
// General-purpose patcher routine. It opens a nice dialog box, handles downloading
// and processing patches.
class Patcher : public QObject
{
Q_OBJECT
public:
Patcher(QString baseDirectory, GameData *game_data, QObject *parent = nullptr);
Patcher(QString baseDirectory, BootData *game_data, QObject *parent = nullptr);
Patcher(LauncherCore &launcher, QString baseDirectory, GameData *game_data, QObject *parent = nullptr);
Patcher(LauncherCore &launcher, QString baseDirectory, BootData *game_data, QObject *parent = nullptr);
void processPatchList(QNetworkAccessManager &mgr, const QString &patchList);
@ -28,6 +30,9 @@ private:
struct QueuedPatch {
QString name, repository, version, path;
QStringList hashes;
long hashBlockSize;
long length;
};
void processPatch(const QueuedPatch &patch);
@ -39,4 +44,6 @@ private:
GameData *game_data = nullptr;
int remainingPatches = -1;
LauncherCore &m_launcher;
};

View file

@ -1,5 +1,6 @@
#include "patcher.h"
#include <KLocalizedString>
#include <QDir>
#include <QFile>
#include <QNetworkReply>
@ -9,78 +10,63 @@
#include <physis.hpp>
#include <utility>
Patcher::Patcher(QString baseDirectory, BootData *boot_data, QObject *parent)
#include "launchercore.h"
Patcher::Patcher(LauncherCore &launcher, QString baseDirectory, BootData *boot_data, QObject *parent)
: QObject(parent)
, baseDirectory(std::move(baseDirectory))
, boot_data(boot_data)
, m_launcher(launcher)
{
/*dialog = new QProgressDialog();
dialog->setLabelText("Checking the FINAL FANTASY XIV Updater/Launcher version.");
dialog->show();*/
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Updater/Launcher version."));
}
Patcher::Patcher(QString baseDirectory, GameData *game_data, QObject *parent)
Patcher::Patcher(LauncherCore &launcher, QString baseDirectory, GameData *game_data, QObject *parent)
: QObject(parent)
, baseDirectory(std::move(baseDirectory))
, game_data(game_data)
, m_launcher(launcher)
{
/*dialog = new QProgressDialog();
dialog->setLabelText("Checking the FINAL FANTASY XIV Game version.");
dialog->show();*/
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Game version."));
}
void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchList)
{
if (patchList.isEmpty()) {
// dialog->hide();
emit done();
} else {
if (isBoot()) {
// dialog->setLabelText("Updating the FINAL FANTASY XIV Updater/Launcher version.");
Q_EMIT m_launcher.stageIndeterminate();
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Update/Launcher version."));
} else {
// dialog->setLabelText("Updating the FINAL FANTASY XIV Game version.");
Q_EMIT m_launcher.stageIndeterminate();
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Game version."));
}
const QStringList parts = patchList.split("\r\n");
remainingPatches = parts.size() - 7;
patchQueue.resize(remainingPatches);
int patchIndex = 0;
for (int i = 5; i < parts.size() - 2; i++) {
const QStringList patchParts = parts[i].split("\t");
const int length = patchParts[0].toInt();
Q_UNUSED(length)
int ourIndex = patchIndex++;
QString name, url, version, repository;
const QString version = patchParts[4];
const long hashBlockSize = patchParts.size() == 9 ? patchParts[6].toLong() : 0;
if (isBoot()) {
name = patchParts[4];
url = patchParts[5];
version = name;
} else {
url = patchParts[8];
version = patchParts[4];
name = url.split('/').last().remove(".patch");
}
const QString name = version;
const QStringList hashes = patchParts.size() == 9 ? (patchParts[7].split(',')) : QStringList();
const QString url = patchParts[patchParts.size() == 9 ? 8 : 5];
qDebug() << "Parsed patch name: " << name;
auto url_parts = url.split('/');
repository = url_parts[url_parts.size() - 3];
/*if (isBoot()) {
dialog->setLabelText(
"Updating the FINAL FANTASY XIV Updater/Launcher version.\nDownloading ffxivboot - " + version);
} else {
dialog->setLabelText(
"Updating the FINAL FANTASY XIV Game version.\nDownloading " + repository + " - " + version);
}
dialog->setMinimum(0);
dialog->setMaximum(length);*/
const QString repository = url_parts[url_parts.size() - 3];
const QString patchesDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patches/" + repository;
@ -92,13 +78,21 @@ void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchL
QNetworkRequest patchRequest(url);
auto patchReply = mgr.get(patchRequest);
connect(patchReply, &QNetworkReply::downloadProgress, [=](int recieved, int total) {
Q_UNUSED(recieved)
connect(patchReply, &QNetworkReply::downloadProgress, [this, repository, version, length](int recieved, int total) {
Q_UNUSED(total)
// dialog->setValue(recieved);
if (isBoot()) {
Q_EMIT m_launcher.stageChanged(i18n("Updating the FINAL FANTASY XIV Updater/Launcher version.\nDownloading ffxivboot - %1", version));
} else {
Q_EMIT m_launcher.stageChanged(i18n("Updating the FINAL FANTASY XIV Game version.\nDownloading %1 - %2", repository, version));
}
Q_EMIT m_launcher.stageDeterminate(0, length, recieved);
});
connect(patchReply, &QNetworkReply::finished, [this, patchesDir, name, patchReply, repository, version] {
connect(patchReply,
&QNetworkReply::finished,
[this, ourIndex, patchesDir, name, patchReply, repository, version, hashes, hashBlockSize, length] {
QFile file(patchesDir + "/" + name + ".patch");
file.open(QIODevice::WriteOnly);
file.write(patchReply->readAll());
@ -106,7 +100,7 @@ void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchL
auto patch_path = patchesDir + "/" + name + ".patch";
patchQueue.push_back({name, repository, version, patch_path});
patchQueue[ourIndex] = {name, repository, version, patch_path, hashes, hashBlockSize, length};
remainingPatches--;
checkIfDone();
@ -114,7 +108,7 @@ void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchL
} else {
qDebug() << "Found existing patch: " << name;
patchQueue.push_back({name, repository, version, patchesDir + "/" + name + ".patch"});
patchQueue[ourIndex] = {name, repository, version, patchesDir + "/" + name + ".patch", hashes, hashBlockSize, length};
remainingPatches--;
checkIfDone();
@ -126,20 +120,51 @@ void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchL
void Patcher::checkIfDone()
{
if (remainingPatches <= 0) {
if (isBoot()) {
Q_EMIT m_launcher.stageChanged(i18n("Applying updates to the FINAL FANTASY XIV Updater/Launcher."));
} else {
Q_EMIT m_launcher.stageChanged(i18n("Applying updates to the FINAL FANTASY XIV Game."));
}
int i = 0;
for (const auto &patch : patchQueue) {
Q_EMIT m_launcher.stageDeterminate(0, patchQueue.size(), i++);
processPatch(patch);
}
patchQueue.clear();
// dialog->hide();
emit done();
}
}
void Patcher::processPatch(const QueuedPatch &patch)
{
// Perform hash checking
if (!patch.hashes.isEmpty()) {
auto f = QFile(patch.path);
f.open(QIODevice::ReadOnly);
Q_ASSERT(patch.length == f.size());
const int parts = std::ceil(static_cast<double>(patch.length) / static_cast<double>(patch.hashBlockSize));
QByteArray block;
block.resize(patch.hashBlockSize);
for (int i = 0; i < parts; i++) {
const auto read = f.read(patch.hashBlockSize);
if (read.length() <= patch.hashBlockSize) {
block = read;
}
QCryptographicHash hash(QCryptographicHash::Sha1);
hash.addData(block);
Q_ASSERT(hash.result().toHex() == patch.hashes[i]);
}
}
if (isBoot()) {
physis_bootdata_apply_patch(boot_data, patch.path.toStdString().c_str());
} else {

View file

@ -22,7 +22,7 @@ void SquareBoot::bootCheck(const LoginInformation &info)
{
Q_EMIT window.stageChanged(i18n("Checking for launcher updates..."));
patcher = new Patcher(info.profile->gamePath() + "/boot", info.profile->bootData, this);
patcher = new Patcher(window, info.profile->gamePath() + "/boot", info.profile->bootData, this);
connect(patcher, &Patcher::done, [this, &info] {
info.profile->readGameVersion();

View file

@ -171,7 +171,7 @@ void SquareLauncher::registerSession(const LoginInformation &info)
if (reply->rawHeaderList().contains("X-Patch-Unique-Id")) {
QString body = reply->readAll();
patcher = new Patcher(info.profile->gamePath() + "/game", info.profile->gameData, this);
patcher = new Patcher(window, info.profile->gamePath() + "/game", info.profile->gameData, this);
connect(patcher, &Patcher::done, [this, &info, reply] {
info.profile->readGameVersion();

View file

@ -37,6 +37,17 @@ Kirigami.Page {
placeholder.text = message
}
function onStageIndeterminate() {
placeholder.determinate = false
}
function onStageDeterminate(min, max, value) {
placeholder.determinate = true
placeholder.progressBar.value = value
placeholder.progressBar.from = min
placeholder.progressBar.to = max
}
function onLoginError(message) {
errorDialog.subtitle = message
errorDialog.open()