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:
parent
dd1f22ab31
commit
aa2a50b50b
6 changed files with 102 additions and 57 deletions
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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,29 +78,37 @@ 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] {
|
||||
QFile file(patchesDir + "/" + name + ".patch");
|
||||
file.open(QIODevice::WriteOnly);
|
||||
file.write(patchReply->readAll());
|
||||
file.close();
|
||||
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());
|
||||
file.close();
|
||||
|
||||
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--;
|
||||
checkIfDone();
|
||||
});
|
||||
remainingPatches--;
|
||||
checkIfDone();
|
||||
});
|
||||
} 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 {
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
Loading…
Add table
Reference in a new issue