2021-11-01 09:54:58 -04:00
|
|
|
#include "squarelauncher.h"
|
|
|
|
|
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 <QMessageBox>
|
|
|
|
#include <QNetworkReply>
|
2022-03-16 14:40:55 -04:00
|
|
|
#include <QPushButton>
|
2022-08-15 11:14:37 -04:00
|
|
|
#include <QRegularExpressionMatch>
|
|
|
|
#include <QUrlQuery>
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2021-11-23 15:34:23 -05:00
|
|
|
#include "launchercore.h"
|
2022-01-27 09:25:23 -05:00
|
|
|
|
2022-08-15 11:14:37 -04:00
|
|
|
SquareLauncher::SquareLauncher(LauncherCore& window) : window(window), QObject(&window) {}
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2022-09-05 15:52:44 -04:00
|
|
|
QString getFileHash(const QString& file) {
|
2021-11-01 09:54:58 -04:00
|
|
|
auto f = QFile(file);
|
|
|
|
if (!f.open(QIODevice::ReadOnly))
|
|
|
|
return "";
|
|
|
|
|
|
|
|
QCryptographicHash hash(QCryptographicHash::Sha1);
|
|
|
|
hash.addData(&f);
|
|
|
|
|
|
|
|
return QString("%1/%2").arg(QString::number(f.size()), hash.result().toHex());
|
|
|
|
}
|
|
|
|
|
|
|
|
void SquareLauncher::getStored(const LoginInformation& info) {
|
|
|
|
QUrlQuery query;
|
2022-03-16 14:33:04 -04:00
|
|
|
// en is always used to the top url
|
2021-11-01 09:54:58 -04:00
|
|
|
query.addQueryItem("lng", "en");
|
2022-03-16 14:33:04 -04:00
|
|
|
// for some reason, we always use region 3. the actual region is acquired later
|
2021-11-01 09:54:58 -04:00
|
|
|
query.addQueryItem("rgn", "3");
|
2022-05-09 16:02:07 -04:00
|
|
|
query.addQueryItem("isft", info.settings->isFreeTrial ? "1" : "0");
|
2021-11-01 09:54:58 -04:00
|
|
|
query.addQueryItem("cssmode", "1");
|
|
|
|
query.addQueryItem("isnew", "1");
|
2022-03-16 14:33:04 -04:00
|
|
|
query.addQueryItem("launchver", "3");
|
|
|
|
|
2022-08-15 11:14:37 -04:00
|
|
|
if (info.settings->license == GameLicense::WindowsSteam) {
|
2022-03-16 14:33:04 -04:00
|
|
|
query.addQueryItem("issteam", "1");
|
|
|
|
|
|
|
|
// TODO: get steam ticket information from steam api
|
|
|
|
query.addQueryItem("session_ticket", "1");
|
|
|
|
query.addQueryItem("ticket_size", "1");
|
|
|
|
}
|
2021-11-01 09:54:58 -04:00
|
|
|
|
|
|
|
QUrl url("https://ffxiv-login.square-enix.com/oauth/ffxivarr/login/top");
|
|
|
|
url.setQuery(query);
|
|
|
|
|
|
|
|
auto request = QNetworkRequest(url);
|
2022-03-16 15:03:35 -04:00
|
|
|
window.buildRequest(*info.settings, request);
|
2021-11-01 09:54:58 -04:00
|
|
|
|
|
|
|
QNetworkReply* reply = window.mgr->get(request);
|
|
|
|
|
2022-06-08 13:55:15 -04:00
|
|
|
connect(reply, &QNetworkReply::finished, [=, &info] {
|
2021-11-01 09:54:58 -04:00
|
|
|
auto str = QString(reply->readAll());
|
|
|
|
|
2022-03-16 14:33:04 -04:00
|
|
|
// fetches Steam username
|
2022-08-15 11:14:37 -04:00
|
|
|
if (info.settings->license == GameLicense::WindowsSteam) {
|
2022-03-16 14:33:04 -04:00
|
|
|
QRegularExpression re(R"lit(<input name=""sqexid"" type=""hidden"" value=""(?<sqexid>.*)""\/>)lit");
|
|
|
|
QRegularExpressionMatch match = re.match(str);
|
|
|
|
|
2022-08-15 11:14:37 -04:00
|
|
|
if (match.hasMatch()) {
|
2022-03-16 14:33:04 -04:00
|
|
|
username = match.captured(1);
|
|
|
|
} else {
|
2022-08-15 11:14:37 -04:00
|
|
|
auto messageBox = new QMessageBox(
|
|
|
|
QMessageBox::Icon::Critical,
|
|
|
|
"Failed to Login",
|
|
|
|
"Could not get Steam username, have you attached your account?");
|
2022-03-16 14:33:04 -04:00
|
|
|
messageBox->show();
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
username = info.username;
|
|
|
|
}
|
|
|
|
|
2021-11-01 09:54:58 -04:00
|
|
|
QRegularExpression re(R"lit(\t<\s*input .* name="_STORED_" value="(?<stored>.*)">)lit");
|
|
|
|
QRegularExpressionMatch match = re.match(str);
|
|
|
|
if (match.hasMatch()) {
|
|
|
|
stored = match.captured(1);
|
|
|
|
login(info, url);
|
|
|
|
} else {
|
2022-08-15 11:14:37 -04:00
|
|
|
auto messageBox = new QMessageBox(
|
|
|
|
QMessageBox::Icon::Critical,
|
|
|
|
"Failed to Login",
|
|
|
|
"Failed to contact SE servers. They may be in maintenance.");
|
2021-11-01 09:54:58 -04:00
|
|
|
messageBox->show();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-09-05 15:52:44 -04:00
|
|
|
void SquareLauncher::login(const LoginInformation& info, const QUrl& referer) {
|
2021-11-01 09:54:58 -04:00
|
|
|
QUrlQuery postData;
|
|
|
|
postData.addQueryItem("_STORED_", stored);
|
|
|
|
postData.addQueryItem("sqexid", info.username);
|
|
|
|
postData.addQueryItem("password", info.password);
|
|
|
|
postData.addQueryItem("otppw", info.oneTimePassword);
|
|
|
|
|
|
|
|
QNetworkRequest request(QUrl("https://ffxiv-login.square-enix.com/oauth/ffxivarr/login/login.send"));
|
2022-03-16 15:03:35 -04:00
|
|
|
window.buildRequest(*info.settings, request);
|
2022-08-15 11:14:37 -04:00
|
|
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
2021-11-01 09:54:58 -04:00
|
|
|
request.setRawHeader("Referer", referer.toEncoded());
|
|
|
|
request.setRawHeader("Cache-Control", "no-cache");
|
|
|
|
|
|
|
|
auto reply = window.mgr->post(request, postData.toString(QUrl::FullyEncoded).toUtf8());
|
2022-06-08 13:55:15 -04:00
|
|
|
connect(reply, &QNetworkReply::finished, [=, &info] {
|
2021-11-01 09:54:58 -04:00
|
|
|
auto str = QString(reply->readAll());
|
|
|
|
|
|
|
|
QRegularExpression re(R"lit(window.external.user\("login=auth,ok,(?<launchParams>.*)\);)lit");
|
|
|
|
QRegularExpressionMatch match = re.match(str);
|
2022-08-15 11:14:37 -04:00
|
|
|
if (match.hasMatch()) {
|
2021-11-01 09:54:58 -04:00
|
|
|
const auto parts = match.captured(1).split(',');
|
|
|
|
|
|
|
|
const bool terms = parts[3] == "1";
|
|
|
|
const bool playable = parts[9] == "1";
|
|
|
|
|
2022-08-15 11:14:37 -04:00
|
|
|
if (!playable) {
|
|
|
|
auto messageBox = new QMessageBox(
|
|
|
|
QMessageBox::Icon::Critical,
|
|
|
|
"Failed to Login",
|
|
|
|
"Your game is unplayable. Please check that you have the right license selected, and a "
|
|
|
|
"subscription to play.");
|
2022-03-16 14:40:55 -04:00
|
|
|
|
|
|
|
auto launcherButton = messageBox->addButton("Open Mog Station", QMessageBox::HelpRole);
|
|
|
|
connect(launcherButton, &QPushButton::clicked, [=] {
|
|
|
|
QDesktopServices::openUrl(QUrl("https://sqex.to/Msp"));
|
|
|
|
});
|
|
|
|
|
|
|
|
messageBox->addButton(QMessageBox::StandardButton::Ok);
|
2021-12-02 14:40:04 -05:00
|
|
|
|
2021-11-01 09:54:58 -04:00
|
|
|
messageBox->show();
|
|
|
|
|
2022-03-16 14:33:04 -04:00
|
|
|
return;
|
2021-11-01 09:54:58 -04:00
|
|
|
}
|
2022-03-16 14:33:04 -04:00
|
|
|
|
2022-08-15 11:14:37 -04:00
|
|
|
if (!terms) {
|
|
|
|
auto messageBox = new QMessageBox(
|
|
|
|
QMessageBox::Icon::Critical,
|
|
|
|
"Failed to Login",
|
|
|
|
"Your game is unplayable. You need to accept the terms of service from the official launcher.");
|
2022-03-16 14:33:04 -04:00
|
|
|
|
|
|
|
messageBox->show();
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
SID = parts[1];
|
|
|
|
auth.region = parts[5].toInt();
|
|
|
|
auth.maxExpansion = parts[13].toInt();
|
|
|
|
|
|
|
|
registerSession(info);
|
2021-11-01 09:54:58 -04:00
|
|
|
} else {
|
2022-03-16 14:51:16 -04:00
|
|
|
QRegularExpression re(R"lit(window.external.user\("login=auth,ng,err,(?<launchParams>.*)\);)lit");
|
|
|
|
QRegularExpressionMatch match = re.match(str);
|
|
|
|
|
|
|
|
const auto parts = match.captured(1).split(',');
|
|
|
|
|
|
|
|
// there's a stray quote at the end of the error string, so let's remove that
|
|
|
|
QString errorStr = match.captured(1).chopped(1);
|
|
|
|
|
|
|
|
auto messageBox = new QMessageBox(QMessageBox::Icon::Critical, "Failed to Login", errorStr);
|
2021-11-01 09:54:58 -04:00
|
|
|
messageBox->show();
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void SquareLauncher::registerSession(const LoginInformation& info) {
|
|
|
|
QUrl url;
|
|
|
|
url.setScheme("https");
|
|
|
|
url.setHost("patch-gamever.ffxiv.com");
|
2022-08-15 11:14:37 -04:00
|
|
|
url.setPath(QString("/http/win32/ffxivneo_release_game/%1/%2")
|
|
|
|
.arg(info.settings->repositories.repositories[0].version, SID));
|
2021-11-01 09:54:58 -04:00
|
|
|
|
|
|
|
auto request = QNetworkRequest(url);
|
|
|
|
window.setSSL(request);
|
|
|
|
request.setRawHeader("X-Hash-Check", "enabled");
|
|
|
|
request.setRawHeader("User-Agent", "FFXIV PATCH CLIENT");
|
2022-08-15 11:14:37 -04:00
|
|
|
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/x-www-form-urlencoded");
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2022-08-09 23:18:18 -04:00
|
|
|
QString report = QString("%1=%2").arg(info.settings->bootVersion, getBootHash(info));
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2022-08-15 11:14:37 -04:00
|
|
|
for (int i = 1; i < auth.maxExpansion + 1; i++) {
|
|
|
|
if (i <= info.settings->repositories.repositories_count) {
|
|
|
|
report +=
|
|
|
|
QString("\nex%1\t%2").arg(QString::number(i), info.settings->repositories.repositories[i].version);
|
2022-07-20 17:58:21 -04:00
|
|
|
} else {
|
|
|
|
report += QString("\nex%1\t2012.01.01.0000.0000").arg(QString::number(i));
|
|
|
|
}
|
|
|
|
}
|
2021-11-01 09:54:58 -04:00
|
|
|
|
|
|
|
auto reply = window.mgr->post(request, report.toUtf8());
|
2022-07-20 17:58:35 -04:00
|
|
|
connect(reply, &QNetworkReply::finished, [=, &info] {
|
2023-04-25 10:31:38 -04:00
|
|
|
if (reply->error() == QNetworkReply::NoError) {
|
|
|
|
if (reply->rawHeaderList().contains("X-Patch-Unique-Id")) {
|
|
|
|
QString body = reply->readAll();
|
2022-07-20 18:00:42 -04:00
|
|
|
|
2023-04-25 10:31:38 -04:00
|
|
|
patcher = new Patcher(info.settings->gamePath + "/game", info.settings->gameData);
|
|
|
|
connect(patcher, &Patcher::done, [=, &info] {
|
|
|
|
window.readGameVersion();
|
2022-07-20 18:00:42 -04:00
|
|
|
|
2023-04-25 10:31:38 -04:00
|
|
|
auth.SID = reply->rawHeader("X-Patch-Unique-Id");
|
2021-11-01 09:54:58 -04:00
|
|
|
|
2023-04-25 10:31:38 -04:00
|
|
|
window.launchGame(*info.settings, auth);
|
|
|
|
});
|
2022-07-20 18:00:42 -04:00
|
|
|
|
2023-04-25 10:31:38 -04:00
|
|
|
patcher->processPatchList(*window.mgr, body);
|
|
|
|
} else {
|
|
|
|
auto messageBox = new QMessageBox(
|
|
|
|
QMessageBox::Icon::Critical,
|
|
|
|
"Failed to Login",
|
|
|
|
"Fatal error, request was successful but X-Patch-Unique-Id was not received");
|
|
|
|
messageBox->show();
|
|
|
|
}
|
2021-11-01 09:54:58 -04:00
|
|
|
} else {
|
2023-04-25 10:31:38 -04:00
|
|
|
if (reply->error() == QNetworkReply::SslHandshakeFailedError) {
|
|
|
|
auto messageBox = new QMessageBox(
|
|
|
|
QMessageBox::Icon::Critical,
|
|
|
|
"Failed to Login",
|
|
|
|
"SSL handshake error detected. If you are using OpenSUSE Tumbleweed or Fedora, this launcher will "
|
|
|
|
"only work if you run the following command `update-crypto-policies --set LEGACY`");
|
|
|
|
messageBox->show();
|
|
|
|
} else if (reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt() == 405) {
|
|
|
|
auto messageBox = new QMessageBox(
|
|
|
|
QMessageBox::Icon::Critical,
|
|
|
|
"Failed to Login",
|
|
|
|
"Failed the anti-tamper check. Please restore your game to the original state or update the "
|
|
|
|
"game.");
|
|
|
|
messageBox->show();
|
|
|
|
} else {
|
|
|
|
int statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
|
|
|
|
auto messageBox = new QMessageBox(
|
|
|
|
QMessageBox::Icon::Critical, "Failed to Login", &"Unknown error! Status code was "[statusCode]);
|
|
|
|
messageBox->show();
|
|
|
|
}
|
2021-11-01 09:54:58 -04:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2021-11-23 15:34:23 -05:00
|
|
|
QString SquareLauncher::getBootHash(const LoginInformation& info) {
|
2022-08-15 11:14:37 -04:00
|
|
|
const QList<QString> fileList = {
|
|
|
|
"ffxivboot.exe",
|
|
|
|
"ffxivboot64.exe",
|
|
|
|
"ffxivlauncher.exe",
|
|
|
|
"ffxivlauncher64.exe",
|
|
|
|
"ffxivupdater.exe",
|
|
|
|
"ffxivupdater64.exe"};
|
2021-11-01 09:54:58 -04:00
|
|
|
|
|
|
|
QString result;
|
|
|
|
for (int i = 0; i < fileList.count(); i++) {
|
2021-11-23 15:34:23 -05:00
|
|
|
result += fileList[i] + "/" + getFileHash(info.settings->gamePath + "/boot/" + fileList[i]);
|
2021-11-01 09:54:58 -04:00
|
|
|
|
|
|
|
if (i != fileList.length() - 1)
|
|
|
|
result += ",";
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
2022-05-09 15:53:17 -04:00
|
|
|
}
|