2023-08-05 22:14:05 -04:00
|
|
|
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
#include "squareenixlogin.h"
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-07-30 08:49:34 -04:00
|
|
|
#include <KLocalizedString>
|
2022-08-15 11:14:37 -04:00
|
|
|
#include <QDesktopServices>
|
2021-11-01 09:54:58 -04:00
|
|
|
#include <QFile>
|
2022-08-15 11:14:37 -04:00
|
|
|
#include <QNetworkReply>
|
|
|
|
#include <QRegularExpressionMatch>
|
|
|
|
#include <QUrlQuery>
|
2023-09-17 18:43:58 -04:00
|
|
|
#include <QtConcurrentMap>
|
|
|
|
#include <qcorofuture.h>
|
2023-09-16 18:52:12 -04:00
|
|
|
#include <qcoronetworkreply.h>
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-07-30 08:49:34 -04:00
|
|
|
#include "account.h"
|
2023-10-11 14:13:42 -04:00
|
|
|
#include "astra_log.h"
|
2021-11-23 15:34:23 -05:00
|
|
|
#include "launchercore.h"
|
2023-10-08 18:02:02 -04:00
|
|
|
#include "utility.h"
|
2022-01-27 09:25:23 -05:00
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
SquareEnixLogin::SquareEnixLogin(LauncherCore &window, QObject *parent)
|
2023-07-30 08:49:34 -04:00
|
|
|
: QObject(parent)
|
2023-10-11 13:39:10 -04:00
|
|
|
, m_launcher(window)
|
2023-07-30 08:49:34 -04:00
|
|
|
{
|
|
|
|
}
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
QCoro::Task<std::optional<LoginAuth>> SquareEnixLogin::login(LoginInformation *info)
|
2023-07-30 08:49:34 -04:00
|
|
|
{
|
2023-10-11 14:13:42 -04:00
|
|
|
Q_ASSERT(info != nullptr);
|
|
|
|
m_info = info;
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
if (!co_await checkGateStatus()) {
|
|
|
|
co_return std::nullopt;
|
|
|
|
}
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
co_await checkBootUpdates();
|
|
|
|
|
2023-10-11 14:15:49 -04:00
|
|
|
if (!co_await loginOAuth()) {
|
2023-10-11 14:13:42 -04:00
|
|
|
co_return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!co_await registerSession()) {
|
|
|
|
co_return std::nullopt;
|
|
|
|
}
|
|
|
|
|
|
|
|
co_return m_auth;
|
|
|
|
}
|
|
|
|
|
|
|
|
QCoro::Task<bool> SquareEnixLogin::checkGateStatus()
|
|
|
|
{
|
|
|
|
Q_EMIT m_launcher.stageChanged(i18n("Checking gate..."));
|
|
|
|
qInfo(ASTRA_LOG) << "Checking if the gate is open...";
|
|
|
|
|
|
|
|
QUrl url;
|
|
|
|
url.setScheme(m_launcher.settings()->preferredProtocol());
|
|
|
|
url.setHost(QStringLiteral("frontier.%1").arg(m_launcher.settings()->squareEnixServer()));
|
|
|
|
url.setPath(QStringLiteral("/worldStatus/gate_status.json"));
|
|
|
|
url.setQuery(QString::number(QDateTime::currentMSecsSinceEpoch()));
|
|
|
|
|
|
|
|
QNetworkRequest request(url);
|
|
|
|
|
|
|
|
// TODO: really?
|
|
|
|
m_launcher.buildRequest(*m_info->profile, request);
|
|
|
|
|
|
|
|
Utility::printRequest(QStringLiteral("GET"), request);
|
|
|
|
|
|
|
|
const auto reply = m_launcher.mgr()->get(request);
|
|
|
|
m_launcher.setupIgnoreSSL(reply);
|
|
|
|
co_await reply;
|
|
|
|
|
|
|
|
const QJsonDocument document = QJsonDocument::fromJson(reply->readAll());
|
|
|
|
const bool isGateOpen = !document.isEmpty() && document.object()[QLatin1String("status")].toInt() != 0;
|
|
|
|
|
|
|
|
if (isGateOpen) {
|
|
|
|
qInfo(ASTRA_LOG) << "Gate is open!";
|
|
|
|
co_return true;
|
|
|
|
} else {
|
|
|
|
qInfo(ASTRA_LOG) << "Gate is closed!";
|
|
|
|
Q_EMIT m_launcher.loginError(i18n("The login gate is closed, the game may be under maintenance.\n\n%1", reply->errorString()));
|
|
|
|
co_return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QCoro::Task<> SquareEnixLogin::checkBootUpdates()
|
|
|
|
{
|
|
|
|
Q_EMIT m_launcher.stageChanged(i18n("Checking for launcher updates..."));
|
|
|
|
qInfo(ASTRA_LOG) << "Checking for updates to boot components...";
|
|
|
|
|
2023-10-11 17:45:02 -04:00
|
|
|
QString formattedDate = QDateTime::currentDateTimeUtc().toString(QStringLiteral("yyyy-MM-dd-HH-mm"));
|
2023-12-17 10:09:01 -05:00
|
|
|
formattedDate[15] = QLatin1Char('0');
|
2023-10-11 17:45:02 -04:00
|
|
|
|
|
|
|
const QUrlQuery query{{QStringLiteral("time"), formattedDate}};
|
2023-10-11 14:13:42 -04:00
|
|
|
|
|
|
|
QUrl url;
|
|
|
|
url.setScheme(QStringLiteral("http"));
|
|
|
|
url.setHost(QStringLiteral("patch-bootver.%1").arg(m_launcher.settings()->squareEnixServer()));
|
2023-10-11 17:45:02 -04:00
|
|
|
url.setPath(QStringLiteral("/http/win32/ffxivneo_release_boot/%1/").arg(m_info->profile->bootVersion()));
|
2023-10-11 14:13:42 -04:00
|
|
|
url.setQuery(query);
|
|
|
|
|
|
|
|
auto request = QNetworkRequest(url);
|
|
|
|
if (m_info->profile->account()->license() == Account::GameLicense::macOS) {
|
|
|
|
request.setRawHeader(QByteArrayLiteral("User-Agent"), QByteArrayLiteral("FFXIV-MAC PATCH CLIENT"));
|
|
|
|
} else {
|
|
|
|
request.setRawHeader(QByteArrayLiteral("User-Agent"), QByteArrayLiteral("FFXIV PATCH CLIENT"));
|
|
|
|
}
|
|
|
|
|
|
|
|
request.setRawHeader(QByteArrayLiteral("Host"), QStringLiteral("patch-bootver.%1").arg(m_launcher.settings()->squareEnixServer()).toUtf8());
|
|
|
|
Utility::printRequest(QStringLiteral("GET"), request);
|
|
|
|
|
|
|
|
const auto reply = m_launcher.mgr()->get(request);
|
|
|
|
co_await reply;
|
|
|
|
|
2023-12-17 10:09:01 -05:00
|
|
|
const QString patchList = QString::fromUtf8(reply->readAll());
|
2023-10-11 14:13:42 -04:00
|
|
|
if (!patchList.isEmpty()) {
|
|
|
|
m_patcher = new Patcher(m_launcher, m_info->profile->gamePath() + QStringLiteral("/boot"), *m_info->profile->bootData(), this);
|
|
|
|
const bool hasPatched = co_await m_patcher->patch(PatchList(patchList));
|
|
|
|
if (hasPatched) {
|
|
|
|
// update game version information
|
|
|
|
m_info->profile->readGameVersion();
|
|
|
|
}
|
|
|
|
m_patcher->deleteLater();
|
|
|
|
}
|
|
|
|
|
|
|
|
co_return;
|
2021-11-01 09:54:58 -04:00
|
|
|
}
|
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
QCoro::Task<std::optional<SquareEnixLogin::StoredInfo>> SquareEnixLogin::getStoredValue()
|
2023-07-30 08:49:34 -04:00
|
|
|
{
|
2023-10-11 14:13:42 -04:00
|
|
|
qInfo(ASTRA_LOG) << "Getting the STORED value...";
|
|
|
|
|
2023-10-11 13:39:10 -04:00
|
|
|
Q_EMIT m_launcher.stageChanged(i18n("Logging in..."));
|
2023-07-30 08:49:34 -04:00
|
|
|
|
2021-11-01 09:54:58 -04:00
|
|
|
QUrlQuery query;
|
2022-03-16 14:33:04 -04:00
|
|
|
// en is always used to the top url
|
2023-09-16 19:02:59 -04:00
|
|
|
query.addQueryItem(QStringLiteral("lng"), QStringLiteral("en"));
|
2022-03-16 14:33:04 -04:00
|
|
|
// for some reason, we always use region 3. the actual region is acquired later
|
2023-09-16 19:02:59 -04:00
|
|
|
query.addQueryItem(QStringLiteral("rgn"), QStringLiteral("3"));
|
2023-10-11 14:13:42 -04:00
|
|
|
query.addQueryItem(QStringLiteral("isft"), m_info->profile->account()->isFreeTrial() ? QStringLiteral("1") : QStringLiteral("0"));
|
2023-09-16 19:02:59 -04:00
|
|
|
query.addQueryItem(QStringLiteral("cssmode"), QStringLiteral("1"));
|
|
|
|
query.addQueryItem(QStringLiteral("isnew"), QStringLiteral("1"));
|
|
|
|
query.addQueryItem(QStringLiteral("launchver"), QStringLiteral("3"));
|
2022-03-16 14:33:04 -04:00
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
if (m_info->profile->account()->license() == Account::GameLicense::WindowsSteam) {
|
2023-09-16 19:02:59 -04:00
|
|
|
query.addQueryItem(QStringLiteral("issteam"), QStringLiteral("1"));
|
2022-03-16 14:33:04 -04:00
|
|
|
|
|
|
|
// TODO: get steam ticket information from steam api
|
2023-09-16 19:02:59 -04:00
|
|
|
query.addQueryItem(QStringLiteral("session_ticket"), QStringLiteral("1"));
|
|
|
|
query.addQueryItem(QStringLiteral("ticket_size"), QStringLiteral("1"));
|
2022-03-16 14:33:04 -04:00
|
|
|
}
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-08-18 21:36:29 -04:00
|
|
|
QUrl url;
|
2023-10-11 13:39:10 -04:00
|
|
|
url.setScheme(m_launcher.settings()->preferredProtocol());
|
|
|
|
url.setHost(QStringLiteral("ffxiv-login.%1").arg(m_launcher.settings()->squareEnixLoginServer()));
|
2023-09-16 19:02:59 -04:00
|
|
|
url.setPath(QStringLiteral("/oauth/ffxivarr/login/top"));
|
2021-11-01 09:54:58 -04:00
|
|
|
url.setQuery(query);
|
|
|
|
|
|
|
|
auto request = QNetworkRequest(url);
|
2023-10-11 14:13:42 -04:00
|
|
|
m_launcher.buildRequest(*m_info->profile, request);
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-10-08 18:02:02 -04:00
|
|
|
Utility::printRequest(QStringLiteral("GET"), request);
|
|
|
|
|
2023-10-11 13:39:10 -04:00
|
|
|
const auto reply = m_launcher.mgr()->get(request);
|
2023-09-16 18:52:12 -04:00
|
|
|
co_await reply;
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-12-17 10:09:01 -05:00
|
|
|
const QString str = QString::fromUtf8(reply->readAll());
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-09-16 18:52:12 -04:00
|
|
|
// fetches Steam username
|
2023-10-11 14:13:42 -04:00
|
|
|
if (m_info->profile->account()->license() == Account::GameLicense::WindowsSteam) {
|
2023-09-16 19:02:59 -04:00
|
|
|
const QRegularExpression re(QStringLiteral(R"lit(<input name=""sqexid"" type=""hidden"" value=""(?<sqexid>.*)""\/>)lit"));
|
|
|
|
const QRegularExpressionMatch match = re.match(str);
|
2023-09-16 18:52:12 -04:00
|
|
|
|
2021-11-01 09:54:58 -04:00
|
|
|
if (match.hasMatch()) {
|
2023-10-11 13:39:10 -04:00
|
|
|
m_username = match.captured(1);
|
2021-11-01 09:54:58 -04:00
|
|
|
} else {
|
2023-10-11 13:39:10 -04:00
|
|
|
Q_EMIT m_launcher.loginError(i18n("Could not get Steam username, have you attached your account?"));
|
2021-11-01 09:54:58 -04:00
|
|
|
}
|
2023-09-16 18:52:12 -04:00
|
|
|
} else {
|
2023-10-11 14:13:42 -04:00
|
|
|
m_username = m_info->username;
|
2023-09-16 18:52:12 -04:00
|
|
|
}
|
|
|
|
|
2023-09-16 19:02:59 -04:00
|
|
|
const QRegularExpression re(QStringLiteral(R"lit(\t<\s*input .* name="_STORED_" value="(?<stored>.*)">)lit"));
|
|
|
|
const QRegularExpressionMatch match = re.match(str);
|
2023-09-16 18:52:12 -04:00
|
|
|
if (match.hasMatch()) {
|
|
|
|
co_return StoredInfo{match.captured(1), url};
|
|
|
|
} else {
|
2023-10-11 13:39:10 -04:00
|
|
|
Q_EMIT m_launcher.loginError(
|
2023-09-16 18:52:12 -04:00
|
|
|
i18n("Square Enix servers refused to confirm session information. The game may be under maintenance, try the official launcher."));
|
|
|
|
co_return {};
|
|
|
|
}
|
2021-11-01 09:54:58 -04:00
|
|
|
}
|
|
|
|
|
2023-10-11 14:15:49 -04:00
|
|
|
QCoro::Task<bool> SquareEnixLogin::loginOAuth()
|
2023-07-30 08:49:34 -04:00
|
|
|
{
|
2023-10-11 14:13:42 -04:00
|
|
|
const auto storedResult = co_await getStoredValue();
|
2023-09-16 18:52:12 -04:00
|
|
|
if (storedResult == std::nullopt) {
|
2023-10-11 14:13:42 -04:00
|
|
|
co_return false;
|
2023-09-16 18:52:12 -04:00
|
|
|
}
|
2023-09-17 18:43:58 -04:00
|
|
|
|
2023-09-16 18:52:12 -04:00
|
|
|
const auto [stored, referer] = *storedResult;
|
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
qInfo(ASTRA_LOG) << "Logging in...";
|
2023-09-17 19:20:41 -04:00
|
|
|
|
2021-11-01 09:54:58 -04:00
|
|
|
QUrlQuery postData;
|
2023-09-16 19:02:59 -04:00
|
|
|
postData.addQueryItem(QStringLiteral("_STORED_"), stored);
|
2023-10-11 14:13:42 -04:00
|
|
|
postData.addQueryItem(QStringLiteral("sqexid"), m_info->username);
|
|
|
|
postData.addQueryItem(QStringLiteral("password"), m_info->password);
|
|
|
|
postData.addQueryItem(QStringLiteral("otppw"), m_info->oneTimePassword);
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-08-18 21:36:29 -04:00
|
|
|
QUrl url;
|
2023-09-16 19:02:59 -04:00
|
|
|
url.setScheme(QStringLiteral("https"));
|
2023-10-11 13:39:10 -04:00
|
|
|
url.setHost(QStringLiteral("ffxiv-login.%1").arg(m_launcher.settings()->squareEnixLoginServer()));
|
2023-09-16 19:02:59 -04:00
|
|
|
url.setPath(QStringLiteral("/oauth/ffxivarr/login/login.send"));
|
2023-08-18 21:36:29 -04:00
|
|
|
|
|
|
|
QNetworkRequest request(url);
|
2023-10-11 14:13:42 -04:00
|
|
|
m_launcher.buildRequest(*m_info->profile, request);
|
2023-09-16 19:02:59 -04:00
|
|
|
request.setHeader(QNetworkRequest::ContentTypeHeader, QByteArrayLiteral("application/x-www-form-urlencoded"));
|
|
|
|
request.setRawHeader(QByteArrayLiteral("Referer"), referer.toEncoded());
|
|
|
|
request.setRawHeader(QByteArrayLiteral("Cache-Control"), QByteArrayLiteral("no-cache"));
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-10-08 18:02:02 -04:00
|
|
|
Utility::printRequest(QStringLiteral("POST"), request);
|
|
|
|
|
2023-10-11 13:39:10 -04:00
|
|
|
const auto reply = m_launcher.mgr()->post(request, postData.toString(QUrl::FullyEncoded).toUtf8());
|
|
|
|
m_launcher.setupIgnoreSSL(reply);
|
2023-09-16 18:52:12 -04:00
|
|
|
co_await reply;
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-12-17 10:09:01 -05:00
|
|
|
const QString str = QString::fromUtf8(reply->readAll());
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-09-16 19:02:59 -04:00
|
|
|
const QRegularExpression re(QStringLiteral(R"lit(window.external.user\("login=auth,ok,(?<launchParams>.*)\);)lit"));
|
|
|
|
const QRegularExpressionMatch match = re.match(str);
|
2023-09-16 18:52:12 -04:00
|
|
|
if (match.hasMatch()) {
|
2023-09-16 19:02:59 -04:00
|
|
|
const auto parts = match.captured(1).split(QLatin1Char(','));
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-09-16 19:02:59 -04:00
|
|
|
const bool terms = parts[3] == QLatin1String("1");
|
|
|
|
const bool playable = parts[9] == QLatin1String("1");
|
2022-03-16 14:33:04 -04:00
|
|
|
|
2023-09-16 18:52:12 -04:00
|
|
|
if (!playable) {
|
2023-10-11 13:39:10 -04:00
|
|
|
Q_EMIT m_launcher.loginError(i18n("Your account is unplayable. Check that you have the correct license, and a valid subscription."));
|
2023-10-11 14:13:42 -04:00
|
|
|
co_return false;
|
2023-09-16 18:52:12 -04:00
|
|
|
}
|
2022-03-16 14:33:04 -04:00
|
|
|
|
2023-09-16 18:52:12 -04:00
|
|
|
if (!terms) {
|
2023-10-11 13:39:10 -04:00
|
|
|
Q_EMIT m_launcher.loginError(i18n("Your account is unplayable. You need to accept the terms of service from the official launcher first."));
|
2023-10-11 14:13:42 -04:00
|
|
|
co_return false;
|
2023-09-16 18:52:12 -04:00
|
|
|
}
|
2022-03-16 14:33:04 -04:00
|
|
|
|
2023-10-11 13:39:10 -04:00
|
|
|
m_SID = parts[1];
|
|
|
|
m_auth.region = parts[5].toInt();
|
|
|
|
m_auth.maxExpansion = parts[13].toInt();
|
2022-03-16 14:51:16 -04:00
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
co_return true;
|
2023-09-16 18:52:12 -04:00
|
|
|
} else {
|
2023-09-16 19:02:59 -04:00
|
|
|
const QRegularExpression re(QStringLiteral(R"lit(window.external.user\("login=auth,ng,err,(?<launchParams>.*)\);)lit"));
|
|
|
|
const QRegularExpressionMatch match = re.match(str);
|
2022-03-16 14:51:16 -04:00
|
|
|
|
2023-09-16 18:52:12 -04:00
|
|
|
// there's a stray quote at the end of the error string, so let's remove that
|
2023-10-11 13:39:10 -04:00
|
|
|
Q_EMIT m_launcher.loginError(match.captured(1).chopped(1));
|
2023-10-11 14:13:42 -04:00
|
|
|
|
|
|
|
co_return false;
|
2023-09-16 18:52:12 -04:00
|
|
|
}
|
2021-11-01 09:54:58 -04:00
|
|
|
}
|
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
QCoro::Task<bool> SquareEnixLogin::registerSession()
|
2023-07-30 08:49:34 -04:00
|
|
|
{
|
2023-10-11 14:13:42 -04:00
|
|
|
qInfo(ASTRA_LOG) << "Registering the session...";
|
|
|
|
|
2021-11-01 09:54:58 -04:00
|
|
|
QUrl url;
|
2023-09-16 19:02:59 -04:00
|
|
|
url.setScheme(QStringLiteral("https"));
|
2023-10-11 13:39:10 -04:00
|
|
|
url.setHost(QStringLiteral("patch-gamever.%1").arg(m_launcher.settings()->squareEnixServer()));
|
2023-10-11 14:13:42 -04:00
|
|
|
url.setPath(QStringLiteral("/http/win32/ffxivneo_release_game/%1/%2").arg(m_info->profile->baseGameVersion(), m_SID));
|
2021-11-01 09:54:58 -04:00
|
|
|
|
|
|
|
auto request = QNetworkRequest(url);
|
2023-10-11 13:39:10 -04:00
|
|
|
m_launcher.setSSL(request);
|
2023-09-16 19:02:59 -04:00
|
|
|
request.setRawHeader(QByteArrayLiteral("X-Hash-Check"), QByteArrayLiteral("enabled"));
|
|
|
|
request.setRawHeader(QByteArrayLiteral("User-Agent"), QByteArrayLiteral("FFXIV PATCH CLIENT"));
|
|
|
|
request.setHeader(QNetworkRequest::ContentTypeHeader, QByteArrayLiteral("application/x-www-form-urlencoded"));
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
QString report = QStringLiteral("%1=%2").arg(m_info->profile->bootVersion(), co_await getBootHash());
|
2023-10-11 13:39:10 -04:00
|
|
|
for (int i = 0; i < m_auth.maxExpansion; i++) {
|
2023-10-11 14:13:42 -04:00
|
|
|
if (i < static_cast<int>(m_info->profile->numInstalledExpansions())) {
|
|
|
|
report += QStringLiteral("\nex%1\t%2").arg(QString::number(i + 1), m_info->profile->expansionVersion(i));
|
2022-07-20 17:58:21 -04:00
|
|
|
} else {
|
2023-09-17 09:40:39 -04:00
|
|
|
report += QStringLiteral("\nex%1\t2012.01.01.0000.0000").arg(QString::number(i + 1));
|
2022-07-20 17:58:21 -04:00
|
|
|
}
|
|
|
|
}
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-10-08 18:02:02 -04:00
|
|
|
Utility::printRequest(QStringLiteral("POST"), request);
|
|
|
|
|
2023-10-11 13:39:10 -04:00
|
|
|
const auto reply = m_launcher.mgr()->post(request, report.toUtf8());
|
2023-09-16 18:52:12 -04:00
|
|
|
co_await reply;
|
|
|
|
|
|
|
|
if (reply->error() == QNetworkReply::NoError) {
|
2023-10-06 18:14:32 -04:00
|
|
|
QString patchUniqueId;
|
2023-09-16 19:02:59 -04:00
|
|
|
if (reply->rawHeaderList().contains(QByteArrayLiteral("X-Patch-Unique-Id"))) {
|
2023-12-17 10:09:01 -05:00
|
|
|
patchUniqueId = QString::fromUtf8(reply->rawHeader(QByteArrayLiteral("X-Patch-Unique-Id")));
|
2023-10-06 18:14:32 -04:00
|
|
|
} else if (reply->rawHeaderList().contains(QByteArrayLiteral("x-patch-unique-id"))) {
|
2023-12-17 10:09:01 -05:00
|
|
|
patchUniqueId = QString::fromUtf8(reply->rawHeader(QByteArrayLiteral("x-patch-unique-id")));
|
2023-10-06 18:14:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!patchUniqueId.isEmpty()) {
|
2023-12-17 10:09:01 -05:00
|
|
|
const QString body = QString::fromUtf8(reply->readAll());
|
2022-07-20 18:00:42 -04:00
|
|
|
|
2023-09-17 18:43:58 -04:00
|
|
|
if (!body.isEmpty()) {
|
2023-10-11 14:13:42 -04:00
|
|
|
m_patcher = new Patcher(m_launcher, m_info->profile->gamePath() + QStringLiteral("/game"), *m_info->profile->gameData(), this);
|
2023-10-11 13:39:10 -04:00
|
|
|
const bool hasPatched = co_await m_patcher->patch(PatchList(body));
|
2023-09-17 18:43:58 -04:00
|
|
|
if (hasPatched) {
|
|
|
|
// re-read game version if it has updated
|
2023-10-11 14:13:42 -04:00
|
|
|
m_info->profile->readGameVersion();
|
2023-09-17 18:43:58 -04:00
|
|
|
}
|
2023-10-11 13:39:10 -04:00
|
|
|
m_patcher->deleteLater();
|
2023-09-17 18:43:58 -04:00
|
|
|
}
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-10-11 13:39:10 -04:00
|
|
|
m_auth.SID = patchUniqueId;
|
2022-07-20 18:00:42 -04:00
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
co_return true;
|
2023-09-16 18:52:12 -04:00
|
|
|
} else {
|
2023-10-11 13:39:10 -04:00
|
|
|
Q_EMIT m_launcher.loginError(i18n("Fatal error, request was successful but X-Patch-Unique-Id was not recieved."));
|
2023-09-16 18:52:12 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (reply->error() == QNetworkReply::SslHandshakeFailedError) {
|
2023-10-11 13:39:10 -04:00
|
|
|
Q_EMIT m_launcher.loginError(
|
2023-09-16 18:52:12 -04:00
|
|
|
i18n("SSL handshake error detected. If you are using OpenSUSE or Fedora, try running `update-crypto-policies --set LEGACY`."));
|
|
|
|
} else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 405) {
|
2023-10-11 13:39:10 -04:00
|
|
|
Q_EMIT m_launcher.loginError(i18n("The game failed the anti-tamper check. Restore the game to the original state and try updating again."));
|
2023-09-23 13:01:02 -04:00
|
|
|
} else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 410) {
|
2023-10-11 13:39:10 -04:00
|
|
|
Q_EMIT m_launcher.loginError(i18n("This game version is no longer supported."));
|
2021-11-01 09:54:58 -04:00
|
|
|
} else {
|
2023-10-11 13:39:10 -04:00
|
|
|
Q_EMIT m_launcher.loginError(i18n("Unknown error when registering the session."));
|
2021-11-01 09:54:58 -04:00
|
|
|
}
|
2023-09-16 18:52:12 -04:00
|
|
|
}
|
2023-10-11 14:13:42 -04:00
|
|
|
|
|
|
|
co_return false;
|
2021-11-01 09:54:58 -04:00
|
|
|
}
|
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
QCoro::Task<QString> SquareEnixLogin::getBootHash()
|
2023-07-30 08:49:34 -04:00
|
|
|
{
|
2023-09-16 19:02:59 -04:00
|
|
|
const QList<QString> fileList = {QStringLiteral("ffxivboot.exe"),
|
|
|
|
QStringLiteral("ffxivboot64.exe"),
|
|
|
|
QStringLiteral("ffxivlauncher.exe"),
|
|
|
|
QStringLiteral("ffxivlauncher64.exe"),
|
|
|
|
QStringLiteral("ffxivupdater.exe"),
|
|
|
|
QStringLiteral("ffxivupdater64.exe")};
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-10-11 14:13:42 -04:00
|
|
|
const auto hashFuture = QtConcurrent::mapped(fileList, [this](const auto &filename) -> QString {
|
|
|
|
return getFileHash(m_info->profile->gamePath() + QStringLiteral("/boot/") + filename);
|
2023-09-17 18:43:58 -04:00
|
|
|
});
|
|
|
|
|
|
|
|
co_await hashFuture;
|
|
|
|
const QList<QString> hashes = hashFuture.results();
|
|
|
|
|
2021-11-01 09:54:58 -04:00
|
|
|
QString result;
|
|
|
|
for (int i = 0; i < fileList.count(); i++) {
|
2023-09-17 18:43:58 -04:00
|
|
|
result += fileList[i] + QStringLiteral("/") + hashes[i];
|
2021-11-01 09:54:58 -04:00
|
|
|
|
|
|
|
if (i != fileList.length() - 1)
|
2023-09-16 19:02:59 -04:00
|
|
|
result += QStringLiteral(",");
|
2021-11-01 09:54:58 -04:00
|
|
|
}
|
|
|
|
|
2023-09-17 18:43:58 -04:00
|
|
|
co_return result;
|
2023-10-11 14:13:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
QString SquareEnixLogin::getFileHash(const QString &file)
|
|
|
|
{
|
|
|
|
auto f = QFile(file);
|
|
|
|
if (!f.open(QIODevice::ReadOnly))
|
|
|
|
return {};
|
|
|
|
|
|
|
|
QCryptographicHash hash(QCryptographicHash::Sha1);
|
|
|
|
hash.addData(&f);
|
|
|
|
|
2023-12-17 10:09:01 -05:00
|
|
|
return QStringLiteral("%1/%2").arg(QString::number(f.size()), QString::fromUtf8(hash.result().toHex()));
|
2022-05-09 15:53:17 -04:00
|
|
|
}
|