mirror of
https://github.com/redstrate/Astra.git
synced 2025-04-21 04:07:46 +00:00
Adapt patcher to the new Kirigami interface, and perform hash checks
This commit is contained in:
parent
dd1f22ab31
commit
aa2a50b50b
6 changed files with 102 additions and 57 deletions
|
@ -149,6 +149,8 @@ signals:
|
||||||
void showNewsListChanged();
|
void showNewsListChanged();
|
||||||
void loginError(QString message);
|
void loginError(QString message);
|
||||||
void stageChanged(QString message);
|
void stageChanged(QString message);
|
||||||
|
void stageIndeterminate();
|
||||||
|
void stageDeterminate(int min, int max, int value);
|
||||||
void newsChanged();
|
void newsChanged();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -4,14 +4,16 @@
|
||||||
#include <QString>
|
#include <QString>
|
||||||
#include <physis.hpp>
|
#include <physis.hpp>
|
||||||
|
|
||||||
|
class LauncherCore;
|
||||||
|
|
||||||
// General-purpose patcher routine. It opens a nice dialog box, handles downloading
|
// General-purpose patcher routine. It opens a nice dialog box, handles downloading
|
||||||
// and processing patches.
|
// and processing patches.
|
||||||
class Patcher : public QObject
|
class Patcher : public QObject
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
Patcher(QString baseDirectory, GameData *game_data, QObject *parent = nullptr);
|
Patcher(LauncherCore &launcher, QString baseDirectory, GameData *game_data, QObject *parent = nullptr);
|
||||||
Patcher(QString baseDirectory, BootData *game_data, QObject *parent = nullptr);
|
Patcher(LauncherCore &launcher, QString baseDirectory, BootData *game_data, QObject *parent = nullptr);
|
||||||
|
|
||||||
void processPatchList(QNetworkAccessManager &mgr, const QString &patchList);
|
void processPatchList(QNetworkAccessManager &mgr, const QString &patchList);
|
||||||
|
|
||||||
|
@ -28,6 +30,9 @@ private:
|
||||||
|
|
||||||
struct QueuedPatch {
|
struct QueuedPatch {
|
||||||
QString name, repository, version, path;
|
QString name, repository, version, path;
|
||||||
|
QStringList hashes;
|
||||||
|
long hashBlockSize;
|
||||||
|
long length;
|
||||||
};
|
};
|
||||||
|
|
||||||
void processPatch(const QueuedPatch &patch);
|
void processPatch(const QueuedPatch &patch);
|
||||||
|
@ -39,4 +44,6 @@ private:
|
||||||
GameData *game_data = nullptr;
|
GameData *game_data = nullptr;
|
||||||
|
|
||||||
int remainingPatches = -1;
|
int remainingPatches = -1;
|
||||||
|
|
||||||
|
LauncherCore &m_launcher;
|
||||||
};
|
};
|
|
@ -1,5 +1,6 @@
|
||||||
#include "patcher.h"
|
#include "patcher.h"
|
||||||
|
|
||||||
|
#include <KLocalizedString>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
|
@ -9,78 +10,63 @@
|
||||||
#include <physis.hpp>
|
#include <physis.hpp>
|
||||||
#include <utility>
|
#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)
|
: QObject(parent)
|
||||||
, baseDirectory(std::move(baseDirectory))
|
, baseDirectory(std::move(baseDirectory))
|
||||||
, boot_data(boot_data)
|
, boot_data(boot_data)
|
||||||
|
, m_launcher(launcher)
|
||||||
{
|
{
|
||||||
/*dialog = new QProgressDialog();
|
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Updater/Launcher version."));
|
||||||
dialog->setLabelText("Checking the FINAL FANTASY XIV Updater/Launcher version.");
|
|
||||||
|
|
||||||
dialog->show();*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Patcher::Patcher(QString baseDirectory, GameData *game_data, QObject *parent)
|
Patcher::Patcher(LauncherCore &launcher, QString baseDirectory, GameData *game_data, QObject *parent)
|
||||||
: QObject(parent)
|
: QObject(parent)
|
||||||
, baseDirectory(std::move(baseDirectory))
|
, baseDirectory(std::move(baseDirectory))
|
||||||
, game_data(game_data)
|
, game_data(game_data)
|
||||||
|
, m_launcher(launcher)
|
||||||
{
|
{
|
||||||
/*dialog = new QProgressDialog();
|
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Game version."));
|
||||||
dialog->setLabelText("Checking the FINAL FANTASY XIV Game version.");
|
|
||||||
|
|
||||||
dialog->show();*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchList)
|
void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchList)
|
||||||
{
|
{
|
||||||
if (patchList.isEmpty()) {
|
if (patchList.isEmpty()) {
|
||||||
// dialog->hide();
|
|
||||||
|
|
||||||
emit done();
|
emit done();
|
||||||
} else {
|
} else {
|
||||||
if (isBoot()) {
|
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 {
|
} 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");
|
const QStringList parts = patchList.split("\r\n");
|
||||||
|
|
||||||
remainingPatches = parts.size() - 7;
|
remainingPatches = parts.size() - 7;
|
||||||
|
patchQueue.resize(remainingPatches);
|
||||||
|
|
||||||
|
int patchIndex = 0;
|
||||||
|
|
||||||
for (int i = 5; i < parts.size() - 2; i++) {
|
for (int i = 5; i < parts.size() - 2; i++) {
|
||||||
const QStringList patchParts = parts[i].split("\t");
|
const QStringList patchParts = parts[i].split("\t");
|
||||||
|
|
||||||
const int length = patchParts[0].toInt();
|
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()) {
|
const QString name = version;
|
||||||
name = patchParts[4];
|
const QStringList hashes = patchParts.size() == 9 ? (patchParts[7].split(',')) : QStringList();
|
||||||
url = patchParts[5];
|
const QString url = patchParts[patchParts.size() == 9 ? 8 : 5];
|
||||||
version = name;
|
|
||||||
} else {
|
|
||||||
url = patchParts[8];
|
|
||||||
version = patchParts[4];
|
|
||||||
name = url.split('/').last().remove(".patch");
|
|
||||||
}
|
|
||||||
|
|
||||||
qDebug() << "Parsed patch name: " << name;
|
qDebug() << "Parsed patch name: " << name;
|
||||||
|
|
||||||
auto url_parts = url.split('/');
|
auto url_parts = url.split('/');
|
||||||
repository = url_parts[url_parts.size() - 3];
|
const QString 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 patchesDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patches/" + repository;
|
const QString patchesDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patches/" + repository;
|
||||||
|
|
||||||
|
@ -92,13 +78,21 @@ void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchL
|
||||||
|
|
||||||
QNetworkRequest patchRequest(url);
|
QNetworkRequest patchRequest(url);
|
||||||
auto patchReply = mgr.get(patchRequest);
|
auto patchReply = mgr.get(patchRequest);
|
||||||
connect(patchReply, &QNetworkReply::downloadProgress, [=](int recieved, int total) {
|
connect(patchReply, &QNetworkReply::downloadProgress, [this, repository, version, length](int recieved, int total) {
|
||||||
Q_UNUSED(recieved)
|
|
||||||
Q_UNUSED(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");
|
QFile file(patchesDir + "/" + name + ".patch");
|
||||||
file.open(QIODevice::WriteOnly);
|
file.open(QIODevice::WriteOnly);
|
||||||
file.write(patchReply->readAll());
|
file.write(patchReply->readAll());
|
||||||
|
@ -106,7 +100,7 @@ void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchL
|
||||||
|
|
||||||
auto patch_path = patchesDir + "/" + name + ".patch";
|
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--;
|
remainingPatches--;
|
||||||
checkIfDone();
|
checkIfDone();
|
||||||
|
@ -114,7 +108,7 @@ void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchL
|
||||||
} else {
|
} else {
|
||||||
qDebug() << "Found existing patch: " << name;
|
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--;
|
remainingPatches--;
|
||||||
checkIfDone();
|
checkIfDone();
|
||||||
|
@ -126,20 +120,51 @@ void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchL
|
||||||
void Patcher::checkIfDone()
|
void Patcher::checkIfDone()
|
||||||
{
|
{
|
||||||
if (remainingPatches <= 0) {
|
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) {
|
for (const auto &patch : patchQueue) {
|
||||||
|
Q_EMIT m_launcher.stageDeterminate(0, patchQueue.size(), i++);
|
||||||
processPatch(patch);
|
processPatch(patch);
|
||||||
}
|
}
|
||||||
|
|
||||||
patchQueue.clear();
|
patchQueue.clear();
|
||||||
|
|
||||||
// dialog->hide();
|
|
||||||
|
|
||||||
emit done();
|
emit done();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Patcher::processPatch(const QueuedPatch &patch)
|
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()) {
|
if (isBoot()) {
|
||||||
physis_bootdata_apply_patch(boot_data, patch.path.toStdString().c_str());
|
physis_bootdata_apply_patch(boot_data, patch.path.toStdString().c_str());
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -22,7 +22,7 @@ void SquareBoot::bootCheck(const LoginInformation &info)
|
||||||
{
|
{
|
||||||
Q_EMIT window.stageChanged(i18n("Checking for launcher updates..."));
|
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] {
|
connect(patcher, &Patcher::done, [this, &info] {
|
||||||
info.profile->readGameVersion();
|
info.profile->readGameVersion();
|
||||||
|
|
||||||
|
|
|
@ -171,7 +171,7 @@ void SquareLauncher::registerSession(const LoginInformation &info)
|
||||||
if (reply->rawHeaderList().contains("X-Patch-Unique-Id")) {
|
if (reply->rawHeaderList().contains("X-Patch-Unique-Id")) {
|
||||||
QString body = reply->readAll();
|
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] {
|
connect(patcher, &Patcher::done, [this, &info, reply] {
|
||||||
info.profile->readGameVersion();
|
info.profile->readGameVersion();
|
||||||
|
|
||||||
|
|
|
@ -37,6 +37,17 @@ Kirigami.Page {
|
||||||
placeholder.text = message
|
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) {
|
function onLoginError(message) {
|
||||||
errorDialog.subtitle = message
|
errorDialog.subtitle = message
|
||||||
errorDialog.open()
|
errorDialog.open()
|
||||||
|
|
Loading…
Add table
Reference in a new issue