1
Fork 0
mirror of https://github.com/redstrate/Astra.git synced 2025-04-20 11:47:46 +00:00

Separate main launcher core and launcher window, add basic command line support

This commit is contained in:
redstrate 2021-11-23 15:34:23 -05:00
parent 6d03631b66
commit 9bd6e9c977
16 changed files with 364 additions and 328 deletions

View file

@ -12,12 +12,16 @@ add_subdirectory(external)
add_executable(xivlauncher add_executable(xivlauncher
src/main.cpp src/main.cpp
src/xivlauncher.cpp src/launchercore.cpp
src/sapphirelauncher.cpp src/sapphirelauncher.cpp
src/squareboot.cpp src/squareboot.cpp
src/squarelauncher.cpp src/squarelauncher.cpp
src/settingswindow.cpp src/settingswindow.cpp
src/blowfish.cpp src/assetupdater.cpp src/assetupdater.h) src/blowfish.cpp
src/assetupdater.cpp
src/assetupdater.h
src/launcherwindow.cpp
src/launcherwindow.h)
target_link_libraries(xivlauncher Qt5::Core Qt5::Widgets Qt5::Network qt5keychain QuaZip) target_link_libraries(xivlauncher Qt5::Core Qt5::Widgets Qt5::Network qt5keychain QuaZip)

View file

@ -6,7 +6,7 @@
#include <quazip/JlCompress.h> #include <quazip/JlCompress.h>
#include "xivlauncher.h" #include "launchercore.h"
const QString dalamudRemotePath = "https://goatcorp.github.io/dalamud-distrib/"; const QString dalamudRemotePath = "https://goatcorp.github.io/dalamud-distrib/";
const QString dalamudVersion = "latest"; const QString dalamudVersion = "latest";
@ -14,19 +14,19 @@ const QString dalamudVersion = "latest";
const QString nativeLauncherRemotePath = "https://github.com/redstrate/nativelauncher/releases/download/"; const QString nativeLauncherRemotePath = "https://github.com/redstrate/nativelauncher/releases/download/";
const QString nativeLauncherVersion = "v1.0.0"; const QString nativeLauncherVersion = "v1.0.0";
AssetUpdater::AssetUpdater(LauncherWindow &launcher) : launcher(launcher) { AssetUpdater::AssetUpdater(LauncherCore &launcher) : launcher(launcher) {
connect(launcher.mgr, &QNetworkAccessManager::finished, this, &AssetUpdater::finishDownload); connect(launcher.mgr, &QNetworkAccessManager::finished, this, &AssetUpdater::finishDownload);
launcher.mgr->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy); launcher.mgr->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
} }
void AssetUpdater::update() { void AssetUpdater::update(const ProfileSettings& profile) {
QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
const bool hasDalamud = QFile::exists(dataDir + "/NativeLauncher.exe") && QFile::exists(dataDir + "/Dalamud"); const bool hasDalamud = QFile::exists(dataDir + "/NativeLauncher.exe") && QFile::exists(dataDir + "/Dalamud");
// first we determine if we need dalamud // first we determine if we need dalamud
const bool needsDalamud = launcher.currentProfile().enableDalamud && !hasDalamud; const bool needsDalamud = profile.enableDalamud && !hasDalamud;
if(needsDalamud) { if(needsDalamud) {
// download nativelauncher release (needed to launch the game with fixed ACLs) // download nativelauncher release (needed to launch the game with fixed ACLs)
{ {

View file

@ -3,15 +3,16 @@
#include <QObject> #include <QObject>
#include <QTemporaryDir> #include <QTemporaryDir>
class LauncherWindow; class LauncherCore;
class QNetworkReply; class QNetworkReply;
struct ProfileSettings;
class AssetUpdater : public QObject { class AssetUpdater : public QObject {
Q_OBJECT Q_OBJECT
public: public:
AssetUpdater(LauncherWindow& launcher); AssetUpdater(LauncherCore& launcher);
void update(); void update(const ProfileSettings& profile);
void finishDownload(QNetworkReply* reply); void finishDownload(QNetworkReply* reply);
void beginInstall(); void beginInstall();
@ -19,7 +20,7 @@ signals:
void finishedUpdating(); void finishedUpdating();
private: private:
LauncherWindow& launcher; LauncherCore& launcher;
QTemporaryDir tempDir; QTemporaryDir tempDir;
}; };

View file

@ -27,7 +27,7 @@
#include <windows.h> #include <windows.h>
#endif #endif
#include "xivlauncher.h" #include "launchercore.h"
#include "sapphirelauncher.h" #include "sapphirelauncher.h"
#include "squarelauncher.h" #include "squarelauncher.h"
#include "squareboot.h" #include "squareboot.h"
@ -35,7 +35,7 @@
#include "blowfish.h" #include "blowfish.h"
#include "assetupdater.h" #include "assetupdater.h"
void LauncherWindow::setSSL(QNetworkRequest& request) { void LauncherCore::setSSL(QNetworkRequest& request) {
QSslConfiguration config; QSslConfiguration config;
config.setProtocol(QSsl::AnyProtocol); config.setProtocol(QSsl::AnyProtocol);
config.setPeerVerifyMode(QSslSocket::VerifyNone); config.setPeerVerifyMode(QSslSocket::VerifyNone);
@ -43,7 +43,7 @@ void LauncherWindow::setSSL(QNetworkRequest& request) {
request.setSslConfiguration(config); request.setSslConfiguration(config);
} }
void LauncherWindow::buildRequest(QNetworkRequest& request) { void LauncherCore::buildRequest(QNetworkRequest& request) {
setSSL(request); setSSL(request);
request.setHeader(QNetworkRequest::UserAgentHeader, request.setHeader(QNetworkRequest::UserAgentHeader,
QString("SQEXAuthor/2.0.0(Windows 6.2; ja-jp; %1)").arg(QString(QSysInfo::bootUniqueId()))); QString("SQEXAuthor/2.0.0(Windows 6.2; ja-jp; %1)").arg(QString(QSysInfo::bootUniqueId())));
@ -106,20 +106,20 @@ QString encryptGameArg(QString arg) {
return QString("//**sqex0003%1%2**//").arg(base64, QString(checksum)); return QString("//**sqex0003%1%2**//").arg(base64, QString(checksum));
} }
void LauncherWindow::launchGame(const LoginAuth auth) { void LauncherCore::launchGame(const ProfileSettings& profile, const LoginAuth auth) {
QList<QString> arguments; QList<QString> arguments;
QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
if(currentProfile().enableDalamud) { if(profile.enableDalamud) {
arguments.push_back(dataDir + "/NativeLauncher.exe"); arguments.push_back(dataDir + "/NativeLauncher.exe");
} }
// now for the actual game... // now for the actual game...
if(currentProfile().useDX9) { if(profile.useDX9) {
arguments.push_back(currentProfile().gamePath + "\\game\\ffxiv.exe"); arguments.push_back(profile.gamePath + "\\game\\ffxiv.exe");
} else { } else {
arguments.push_back(currentProfile().gamePath + "\\game\\ffxiv_dx11.exe"); arguments.push_back(profile.gamePath + "\\game\\ffxiv_dx11.exe");
} }
struct Argument { struct Argument {
@ -133,8 +133,8 @@ void LauncherWindow::launchGame(const LoginAuth auth) {
gameArgs.push_back({"DEV.MaxEntitledExpansionID", QString::number(auth.maxExpansion)}); gameArgs.push_back({"DEV.MaxEntitledExpansionID", QString::number(auth.maxExpansion)});
gameArgs.push_back({"DEV.TestSID", auth.SID}); gameArgs.push_back({"DEV.TestSID", auth.SID});
gameArgs.push_back({"SYS.Region", QString::number(auth.region)}); gameArgs.push_back({"SYS.Region", QString::number(auth.region)});
gameArgs.push_back({"language", QString::number(currentProfile().language)}); gameArgs.push_back({"language", QString::number(profile.language)});
gameArgs.push_back({"ver", currentProfile().gameVersion}); gameArgs.push_back({"ver", profile.gameVersion});
if(!auth.lobbyhost.isEmpty()) { if(!auth.lobbyhost.isEmpty()) {
gameArgs.push_back({"DEV.GMServerHost", auth.frontierHost}); gameArgs.push_back({"DEV.GMServerHost", auth.frontierHost});
@ -146,8 +146,8 @@ void LauncherWindow::launchGame(const LoginAuth auth) {
auto gameProcess = new QProcess(this); auto gameProcess = new QProcess(this);
if(currentProfile().enableDalamud) { if(profile.enableDalamud) {
connect(gameProcess, &QProcess::readyReadStandardOutput, [this, gameProcess] { connect(gameProcess, &QProcess::readyReadStandardOutput, [this, gameProcess, profile] {
QString output = gameProcess->readAllStandardOutput(); QString output = gameProcess->readAllStandardOutput();
auto dalamudProcess = new QProcess(); auto dalamudProcess = new QProcess();
@ -162,11 +162,11 @@ void LauncherWindow::launchGame(const LoginAuth auth) {
QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
dalamudProcess->start(currentProfile().winePath, {dataDir + "/Dalamud/" + "Dalamud.Injector.exe", output}); dalamudProcess->start(profile.winePath, {dataDir + "/Dalamud/" + "Dalamud.Injector.exe", output});
}); });
} }
if(currentProfile().encryptArguments) { if(profile.encryptArguments) {
QString argJoined; QString argJoined;
for(auto arg : gameArgs) { for(auto arg : gameArgs) {
argJoined += QString(" /%1 =%2").arg(arg.key, arg.value); argJoined += QString(" /%1 =%2").arg(arg.key, arg.value);
@ -174,47 +174,46 @@ void LauncherWindow::launchGame(const LoginAuth auth) {
auto earg = encryptGameArg(argJoined); auto earg = encryptGameArg(argJoined);
arguments.append(earg); arguments.append(earg);
launchExecutable(gameProcess, arguments); launchExecutable(profile, gameProcess, arguments);
} else { } else {
for(auto arg : gameArgs) { for(auto arg : gameArgs) {
arguments.push_back(QString(" %1=%2").arg(arg.key, arg.value)); arguments.push_back(QString(" %1=%2").arg(arg.key, arg.value));
} }
launchExecutable(gameProcess, arguments); launchExecutable(profile, gameProcess, arguments);
} }
} }
void LauncherWindow::launchExecutable(const QStringList args) { void LauncherCore::launchExecutable(const ProfileSettings& profile, const QStringList args) {
auto process = new QProcess(this); auto process = new QProcess(this);
launchExecutable(process, args); launchExecutable(profile, process, args);
} }
void LauncherWindow::launchExecutable(QProcess* process, const QStringList args) { void LauncherCore::launchExecutable(const ProfileSettings& profile, QProcess* process, const QStringList args) {
QList<QString> arguments; QList<QString> arguments;
QStringList env = QProcess::systemEnvironment(); QStringList env = QProcess::systemEnvironment();
#if defined(Q_OS_LINUX) #if defined(Q_OS_LINUX)
if(currentProfile().useGamescope) { if(profile.useGamescope) {
arguments.push_back("gamescope"); arguments.push_back("gamescope");
arguments.push_back("-f"); arguments.push_back("-f");
arguments.push_back("-b"); arguments.push_back("-b");
} }
if(currentProfile().useGamemode) if(profile.useGamemode)
arguments.push_back("gamemoderun"); arguments.push_back("gamemoderun");
if(currentProfile().useEsync) { if(profile.useEsync)
env << "WINEESYNC=1"; env << "WINEESYNC=1";
}
#endif #endif
#if defined(Q_OS_MAC) || defined(Q_OS_LINUX) #if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
env << "WINEPREFIX=" + currentProfile().winePrefixPath; env << "WINEPREFIX=" + profile.winePrefixPath;
if(currentProfile().enableDXVKhud) if(profile.enableDXVKhud)
env << "DXVK_HUD=full"; env << "DXVK_HUD=full";
arguments.push_back(currentProfile().winePath); arguments.push_back(profile.winePath);
#endif #endif
arguments.append(args); arguments.append(args);
@ -222,20 +221,20 @@ void LauncherWindow::launchExecutable(QProcess* process, const QStringList args)
auto executable = arguments[0]; auto executable = arguments[0];
arguments.removeFirst(); arguments.removeFirst();
process->setWorkingDirectory(currentProfile().gamePath + "/game/"); process->setWorkingDirectory(profile.gamePath + "/game/");
process->setEnvironment(env); process->setEnvironment(env);
process->start(executable, arguments); process->start(executable, arguments);
} }
QString LauncherWindow::readVersion(QString path) { QString LauncherCore::readVersion(QString path) {
QFile file(path); QFile file(path);
file.open(QFile::OpenModeFlag::ReadOnly); file.open(QFile::OpenModeFlag::ReadOnly);
return file.readAll(); return file.readAll();
} }
void LauncherWindow::readInitialInformation() { void LauncherCore::readInitialInformation() {
defaultProfileIndex = settings.value("defaultProfile", 0).toInt(); defaultProfileIndex = settings.value("defaultProfile", 0).toInt();
auto profiles = settings.childGroups(); auto profiles = settings.childGroups();
@ -312,7 +311,7 @@ void LauncherWindow::readInitialInformation() {
readGameVersion(); readGameVersion();
} }
void LauncherWindow::readWineInfo(ProfileSettings& profile) { void LauncherCore::readWineInfo(ProfileSettings& profile) {
#if defined(Q_OS_MAC) #if defined(Q_OS_MAC)
switch(profile.wineVersion) { switch(profile.wineVersion) {
case 0: // system wine case 0: // system wine
@ -339,15 +338,14 @@ void LauncherWindow::readWineInfo(ProfileSettings& profile) {
#endif #endif
} }
void LauncherWindow::readGameVersion() { void LauncherCore::readGameVersion() {
for(auto& profile : profileSettings) { for(auto& profile : profileSettings) {
profile.bootVersion = readVersion(profile.gamePath + "/boot/ffxivboot.ver"); profile.bootVersion = readVersion(profile.gamePath + "/boot/ffxivboot.ver");
profile.gameVersion = readVersion(profile.gamePath + "/game/ffxivgame.ver"); profile.gameVersion = readVersion(profile.gamePath + "/game/ffxivgame.ver");
} }
} }
LauncherWindow::LauncherWindow(QWidget* parent) : LauncherCore::LauncherCore() : settings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::applicationName()) {
QMainWindow(parent), settings(QSettings::IniFormat, QSettings::UserScope, QCoreApplication::applicationName()) {
mgr = new QNetworkAccessManager(); mgr = new QNetworkAccessManager();
sapphireLauncher = new SapphireLauncher(*this); sapphireLauncher = new SapphireLauncher(*this);
squareLauncher = new SquareLauncher(*this); squareLauncher = new SquareLauncher(*this);
@ -355,152 +353,17 @@ LauncherWindow::LauncherWindow(QWidget* parent) :
assetUpdater = new AssetUpdater(*this); assetUpdater = new AssetUpdater(*this);
readInitialInformation(); readInitialInformation();
QMenu* fileMenu = menuBar()->addMenu("File");
// sorry linux users, for some reason my global menu does not like qt6 apps right now
#if defined(Q_OS_LINUX)
menuBar()->setNativeMenuBar(false);
#endif
QAction* settingsAction = fileMenu->addAction("Settings...");
connect(settingsAction, &QAction::triggered, [=] {
auto window = new SettingsWindow(*this);
connect(this, &LauncherWindow::settingsChanged, window, &SettingsWindow::reloadControls);
window->show();
});
QMenu* toolsMenu = menuBar()->addMenu("Tools");
QAction* launchOfficial = toolsMenu->addAction("Launch Official Client...");
connect(launchOfficial, &QAction::triggered, [=] {
launchExecutable({currentProfile().gamePath + "/boot/ffxivboot64.exe"});
});
QAction* launchSysInfo = toolsMenu->addAction("Launch System Info...");
connect(launchSysInfo, &QAction::triggered, [=] {
launchExecutable({currentProfile().gamePath + "/boot/ffxivsysinfo64.exe"});
});
QAction* launchCfgBackup = toolsMenu->addAction("Launch Config Backup...");
connect(launchCfgBackup, &QAction::triggered, [=] {
launchExecutable({currentProfile().gamePath + "/boot/ffxivconfig64.exe"});
});
#if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
QMenu* wineMenu = toolsMenu->addMenu("Wine");
QAction* wineCfg = wineMenu->addAction("winecfg");
connect(wineCfg, &QAction::triggered, [=] {
launchExecutable({"winecfg.exe"});
});
QAction* controlPanel = wineMenu->addAction("Control Panel");
connect(controlPanel, &QAction::triggered, [=] {
launchExecutable({"control.exe"});
});
#endif
auto layout = new QFormLayout();
profileSelect = new QComboBox();
connect(profileSelect, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [=](int index) {
reloadControls();
});
layout->addRow("Profile", profileSelect);
usernameEdit = new QLineEdit();
layout->addRow("Username", usernameEdit);
rememberUsernameBox = new QCheckBox();
connect(rememberUsernameBox, &QCheckBox::stateChanged, [=](int) {
currentProfile().rememberUsername = rememberUsernameBox->isChecked();
saveSettings();
});
layout->addRow("Remember Username?", rememberUsernameBox);
passwordEdit = new QLineEdit();
passwordEdit->setEchoMode(QLineEdit::EchoMode::Password);
layout->addRow("Password", passwordEdit);
rememberPasswordBox = new QCheckBox();
connect(rememberPasswordBox, &QCheckBox::stateChanged, [=](int) {
currentProfile().rememberPassword = rememberPasswordBox->isChecked();
saveSettings();
});
layout->addRow("Remember Password?", rememberPasswordBox);
otpEdit = new QLineEdit();
layout->addRow("One-Time Password", otpEdit);
auto loginButton = new QPushButton("Login");
layout->addRow(loginButton);
registerButton = new QPushButton("Register");
layout->addRow(registerButton);
auto emptyWidget = new QWidget();
emptyWidget->setLayout(layout);
setCentralWidget(emptyWidget);
connect(assetUpdater, &AssetUpdater::finishedUpdating, [=] {
auto info = LoginInformation{usernameEdit->text(), passwordEdit->text(), otpEdit->text()};
if(currentProfile().rememberUsername) {
auto job = new QKeychain::WritePasswordJob("LauncherWindow");
job->setTextData(usernameEdit->text());
job->setKey(currentProfile().name + "-username");
job->start();
}
if(currentProfile().rememberPassword) {
auto job = new QKeychain::WritePasswordJob("LauncherWindow");
job->setTextData(passwordEdit->text());
job->setKey(currentProfile().name + "-password");
job->start();
}
if(currentProfile().isSapphire) {
sapphireLauncher->login(currentProfile().lobbyURL, info);
} else {
squareBoot->bootCheck(info);
}
});
connect(loginButton, &QPushButton::released, [=] {
// update the assets first if needed, then it calls the slot above :-)
assetUpdater->update();
});
connect(registerButton, &QPushButton::released, [=] {
if(currentProfile().isSapphire) {
auto info = LoginInformation{usernameEdit->text(), passwordEdit->text(), otpEdit->text()};
sapphireLauncher->registerAccount(currentProfile().lobbyURL, info);
}
});
reloadControls();
} }
LauncherWindow::~LauncherWindow() = default; ProfileSettings LauncherCore::getProfile(int index) const {
ProfileSettings LauncherWindow::currentProfile() const {
return getProfile(profileSelect->currentIndex());
}
ProfileSettings& LauncherWindow::currentProfile() {
return getProfile(profileSelect->currentIndex());
}
ProfileSettings LauncherWindow::getProfile(int index) const {
return profileSettings[index]; return profileSettings[index];
} }
ProfileSettings& LauncherWindow::getProfile(int index) { ProfileSettings& LauncherCore::getProfile(int index) {
return profileSettings[index]; return profileSettings[index];
} }
int LauncherWindow::getProfileIndex(QString name) { int LauncherCore::getProfileIndex(QString name) {
for(int i = 0; i < profileSettings.size(); i++) { for(int i = 0; i < profileSettings.size(); i++) {
if(profileSettings[i].name == name) if(profileSettings[i].name == name)
return i; return i;
@ -509,7 +372,7 @@ int LauncherWindow::getProfileIndex(QString name) {
return -1; return -1;
} }
QList<QString> LauncherWindow::profileList() const { QList<QString> LauncherCore::profileList() const {
QList<QString> list; QList<QString> list;
for(auto profile : profileSettings) { for(auto profile : profileSettings) {
list.append(profile.name); list.append(profile.name);
@ -518,7 +381,7 @@ QList<QString> LauncherWindow::profileList() const {
return list; return list;
} }
int LauncherWindow::addProfile() { int LauncherCore::addProfile() {
ProfileSettings newProfile; ProfileSettings newProfile;
newProfile.uuid = QUuid::createUuid(); newProfile.uuid = QUuid::createUuid();
newProfile.name = "New Profile"; newProfile.name = "New Profile";
@ -530,7 +393,7 @@ int LauncherWindow::addProfile() {
return profileSettings.size() - 1; return profileSettings.size() - 1;
} }
int LauncherWindow::deleteProfile(QString name) { int LauncherCore::deleteProfile(QString name) {
int index = 0; int index = 0;
for(int i = 0; i < profileSettings.size(); i++) { for(int i = 0; i < profileSettings.size(); i++) {
if(profileSettings[i].name == name) if(profileSettings[i].name == name)
@ -547,7 +410,7 @@ int LauncherWindow::deleteProfile(QString name) {
return index - 1; return index - 1;
} }
void LauncherWindow::saveSettings() { void LauncherCore::saveSettings() {
settings.setValue("defaultProfile", defaultProfileIndex); settings.setValue("defaultProfile", defaultProfileIndex);
for(int i = 0; i < profileSettings.size(); i++) { for(int i = 0; i < profileSettings.size(); i++) {
@ -582,52 +445,4 @@ void LauncherWindow::saveSettings() {
settings.endGroup(); settings.endGroup();
} }
}
void LauncherWindow::reloadControls() {
if(currentlyReloadingControls)
return;
currentlyReloadingControls = true;
const int oldIndex = profileSelect->currentIndex();
profileSelect->clear();
for(const auto& profile : profileList()) {
profileSelect->addItem(profile);
}
profileSelect->setCurrentIndex(oldIndex);
if(profileSelect->currentIndex() == -1) {
profileSelect->setCurrentIndex(defaultProfileIndex);
}
rememberUsernameBox->setChecked(currentProfile().rememberUsername);
if(currentProfile().rememberUsername) {
auto job = new QKeychain::ReadPasswordJob("LauncherWindow");
job->setKey(currentProfile().name + "-username");
job->start();
connect(job, &QKeychain::ReadPasswordJob::finished, [=](QKeychain::Job* j) {
usernameEdit->setText(job->textData());
});
}
rememberPasswordBox->setChecked(currentProfile().rememberPassword);
if(currentProfile().rememberPassword) {
auto job = new QKeychain::ReadPasswordJob("LauncherWindow");
job->setKey(currentProfile().name + "-password");
job->start();
connect(job, &QKeychain::ReadPasswordJob::finished, [=](QKeychain::Job* j) {
passwordEdit->setText(job->textData());
});
}
registerButton->setEnabled(currentProfile().isSapphire);
otpEdit->setEnabled(!currentProfile().isSapphire);
currentlyReloadingControls = false;
} }

View file

@ -4,9 +4,6 @@
#include <QNetworkAccessManager> #include <QNetworkAccessManager>
#include <QFuture> #include <QFuture>
#include <QSettings> #include <QSettings>
#include <QComboBox>
#include <QCheckBox>
#include <QPushButton>
#include <QUuid> #include <QUuid>
#include <QProcess> #include <QProcess>
@ -45,6 +42,8 @@ struct ProfileSettings {
}; };
struct LoginInformation { struct LoginInformation {
ProfileSettings* settings = nullptr;
QString username, password, oneTimePassword; QString username, password, oneTimePassword;
}; };
@ -57,18 +56,13 @@ struct LoginAuth {
QString lobbyhost, frontierHost; QString lobbyhost, frontierHost;
}; };
class LauncherWindow : public QMainWindow { class LauncherCore : public QObject {
Q_OBJECT Q_OBJECT
public: public:
explicit LauncherWindow(QWidget* parent = nullptr); LauncherCore();
~LauncherWindow() override;
QNetworkAccessManager* mgr; QNetworkAccessManager* mgr;
ProfileSettings currentProfile() const;
ProfileSettings& currentProfile();
ProfileSettings getProfile(int index) const; ProfileSettings getProfile(int index) const;
ProfileSettings& getProfile(int index); ProfileSettings& getProfile(int index);
@ -77,9 +71,9 @@ public:
int addProfile(); int addProfile();
int deleteProfile(QString name); int deleteProfile(QString name);
void launchGame(const LoginAuth auth); void launchGame(const ProfileSettings& settings, const LoginAuth auth);
void launchExecutable(QStringList args); void launchExecutable(const ProfileSettings& settings, QStringList args);
void launchExecutable(QProcess* process, QStringList args); void launchExecutable(const ProfileSettings& settings, QProcess* process, QStringList args);
void buildRequest(QNetworkRequest& request); void buildRequest(QNetworkRequest& request);
void setSSL(QNetworkRequest& request); void setSSL(QNetworkRequest& request);
QString readVersion(QString path); QString readVersion(QString path);
@ -90,26 +84,15 @@ public:
QSettings settings; QSettings settings;
public slots:
void reloadControls();
signals:
void settingsChanged();
private:
bool currentlyReloadingControls = false;
SapphireLauncher* sapphireLauncher; SapphireLauncher* sapphireLauncher;
SquareBoot* squareBoot; SquareBoot* squareBoot;
SquareLauncher* squareLauncher; SquareLauncher* squareLauncher;
AssetUpdater* assetUpdater; AssetUpdater* assetUpdater;
QComboBox* profileSelect;
QLineEdit* usernameEdit, *passwordEdit;
QLineEdit* otpEdit;
QCheckBox* rememberUsernameBox, *rememberPasswordBox;
QPushButton* registerButton;
QVector<ProfileSettings> profileSettings;
int defaultProfileIndex = 0; int defaultProfileIndex = 0;
signals:
void settingsChanged();
private:
QVector<ProfileSettings> profileSettings;
}; };

192
src/launcherwindow.cpp Normal file
View file

@ -0,0 +1,192 @@
#include "launcherwindow.h"
#include <QMenuBar>
#include <keychain.h>
#include <QFormLayout>
#include "settingswindow.h"
#include "squareboot.h"
#include "squarelauncher.h"
#include "sapphirelauncher.h"
#include "assetupdater.h"
LauncherWindow::LauncherWindow(LauncherCore& core, QWidget* parent) : QMainWindow(parent), core(core) {
connect(&core, &LauncherCore::settingsChanged, this, &LauncherWindow::reloadControls);
QMenu* fileMenu = menuBar()->addMenu("File");
QAction* settingsAction = fileMenu->addAction("Settings...");
connect(settingsAction, &QAction::triggered, [=] {
auto window = new SettingsWindow(*this, this->core);
connect(&this->core, &LauncherCore::settingsChanged, window, &SettingsWindow::reloadControls);
window->show();
});
QMenu* toolsMenu = menuBar()->addMenu("Tools");
QAction* launchOfficial = toolsMenu->addAction("Launch Official Client...");
connect(launchOfficial, &QAction::triggered, [=] {
this->core.launchExecutable(currentProfile(), {currentProfile().gamePath + "/boot/ffxivboot64.exe"});
});
QAction* launchSysInfo = toolsMenu->addAction("Launch System Info...");
connect(launchSysInfo, &QAction::triggered, [=] {
this->core.launchExecutable(currentProfile(), {currentProfile().gamePath + "/boot/ffxivsysinfo64.exe"});
});
QAction* launchCfgBackup = toolsMenu->addAction("Launch Config Backup...");
connect(launchCfgBackup, &QAction::triggered, [=] {
this->core.launchExecutable(currentProfile(), {currentProfile().gamePath + "/boot/ffxivconfig64.exe"});
});
#if defined(Q_OS_MAC) || defined(Q_OS_LINUX)
QMenu* wineMenu = toolsMenu->addMenu("Wine");
QAction* wineCfg = wineMenu->addAction("winecfg");
connect(wineCfg, &QAction::triggered, [=] {
this->core.launchExecutable(currentProfile(), {"winecfg.exe"});
});
QAction* controlPanel = wineMenu->addAction("Control Panel");
connect(controlPanel, &QAction::triggered, [=] {
this->core.launchExecutable(currentProfile(), {"control.exe"});
});
#endif
auto layout = new QFormLayout();
profileSelect = new QComboBox();
connect(profileSelect, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [=](int index) {
reloadControls();
});
layout->addRow("Profile", profileSelect);
usernameEdit = new QLineEdit();
layout->addRow("Username", usernameEdit);
rememberUsernameBox = new QCheckBox();
connect(rememberUsernameBox, &QCheckBox::stateChanged, [=](int) {
currentProfile().rememberUsername = rememberUsernameBox->isChecked();
this->core.saveSettings();
});
layout->addRow("Remember Username?", rememberUsernameBox);
passwordEdit = new QLineEdit();
passwordEdit->setEchoMode(QLineEdit::EchoMode::Password);
layout->addRow("Password", passwordEdit);
rememberPasswordBox = new QCheckBox();
connect(rememberPasswordBox, &QCheckBox::stateChanged, [=](int) {
currentProfile().rememberPassword = rememberPasswordBox->isChecked();
this->core.saveSettings();
});
layout->addRow("Remember Password?", rememberPasswordBox);
otpEdit = new QLineEdit();
layout->addRow("One-Time Password", otpEdit);
auto loginButton = new QPushButton("Login");
layout->addRow(loginButton);
registerButton = new QPushButton("Register");
layout->addRow(registerButton);
auto emptyWidget = new QWidget();
emptyWidget->setLayout(layout);
setCentralWidget(emptyWidget);
connect(core.assetUpdater, &AssetUpdater::finishedUpdating, [=] {
auto info = LoginInformation{&currentProfile(), usernameEdit->text(), passwordEdit->text(), otpEdit->text()};
if(currentProfile().rememberUsername) {
auto job = new QKeychain::WritePasswordJob("LauncherWindow");
job->setTextData(usernameEdit->text());
job->setKey(currentProfile().name + "-username");
job->start();
}
if(currentProfile().rememberPassword) {
auto job = new QKeychain::WritePasswordJob("LauncherWindow");
job->setTextData(passwordEdit->text());
job->setKey(currentProfile().name + "-password");
job->start();
}
if(currentProfile().isSapphire) {
this->core.sapphireLauncher->login(currentProfile().lobbyURL, info);
} else {
this->core.squareBoot->bootCheck(info);
}
});
connect(loginButton, &QPushButton::released, [=] {
// update the assets first if needed, then it calls the slot above :-)
this->core.assetUpdater->update(currentProfile());
});
connect(registerButton, &QPushButton::released, [=] {
if(currentProfile().isSapphire) {
auto info = LoginInformation{&currentProfile(), usernameEdit->text(), passwordEdit->text(), otpEdit->text()};
this->core.sapphireLauncher->registerAccount(currentProfile().lobbyURL, info);
}
});
reloadControls();
}
ProfileSettings LauncherWindow::currentProfile() const {
return core.getProfile(profileSelect->currentIndex());
}
ProfileSettings& LauncherWindow::currentProfile() {
return core.getProfile(profileSelect->currentIndex());
}
void LauncherWindow::reloadControls() {
if(currentlyReloadingControls)
return;
currentlyReloadingControls = true;
const int oldIndex = profileSelect->currentIndex();
profileSelect->clear();
for(const auto& profile : core.profileList()) {
profileSelect->addItem(profile);
}
profileSelect->setCurrentIndex(oldIndex);
if(profileSelect->currentIndex() == -1) {
profileSelect->setCurrentIndex(core.defaultProfileIndex);
}
rememberUsernameBox->setChecked(currentProfile().rememberUsername);
if(currentProfile().rememberUsername) {
auto job = new QKeychain::ReadPasswordJob("LauncherWindow");
job->setKey(currentProfile().name + "-username");
job->start();
connect(job, &QKeychain::ReadPasswordJob::finished, [=](QKeychain::Job* j) {
usernameEdit->setText(job->textData());
});
}
rememberPasswordBox->setChecked(currentProfile().rememberPassword);
if(currentProfile().rememberPassword) {
auto job = new QKeychain::ReadPasswordJob("LauncherWindow");
job->setKey(currentProfile().name + "-password");
job->start();
connect(job, &QKeychain::ReadPasswordJob::finished, [=](QKeychain::Job* j) {
passwordEdit->setText(job->textData());
});
}
registerButton->setEnabled(currentProfile().isSapphire);
otpEdit->setEnabled(!currentProfile().isSapphire);
currentlyReloadingControls = false;
}

31
src/launcherwindow.h Normal file
View file

@ -0,0 +1,31 @@
#pragma once
#include <QMainWindow>
#include <QComboBox>
#include <QCheckBox>
#include <QPushButton>
#include "launchercore.h"
class LauncherWindow : public QMainWindow {
Q_OBJECT
public:
explicit LauncherWindow(LauncherCore& core, QWidget* parent = nullptr);
ProfileSettings currentProfile() const;
ProfileSettings& currentProfile();
public slots:
void reloadControls();
private:
LauncherCore& core;
bool currentlyReloadingControls = false;
QComboBox* profileSelect;
QLineEdit* usernameEdit, *passwordEdit;
QLineEdit* otpEdit;
QCheckBox* rememberUsernameBox, *rememberPasswordBox;
QPushButton* registerButton;
};

View file

@ -1,5 +1,8 @@
#include "xivlauncher.h" #include "launchercore.h"
#include "launcherwindow.h"
#include <QApplication> #include <QApplication>
#include <QCommandLineParser>
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
QApplication app(argc, argv); QApplication app(argc, argv);
@ -13,7 +16,15 @@ int main(int argc, char* argv[]) {
QCoreApplication::setApplicationName("xivlauncher-debug"); QCoreApplication::setApplicationName("xivlauncher-debug");
#endif #endif
LauncherWindow w; LauncherCore c;
QCommandLineParser parser;
parser.setApplicationDescription("Cross-platform FFXIV Launcher");
parser.addHelpOption();
parser.addVersionOption();
parser.process(app);
LauncherWindow w(c);
w.show(); w.show();
return app.exec(); return app.exec();

View file

@ -5,7 +5,7 @@
#include <QMessageBox> #include <QMessageBox>
#include <QNetworkReply> #include <QNetworkReply>
SapphireLauncher::SapphireLauncher(LauncherWindow& window) : window(window) { SapphireLauncher::SapphireLauncher(LauncherCore& window) : window(window) {
} }
@ -33,7 +33,7 @@ void SapphireLauncher::login(QString lobbyUrl, const LoginInformation& info) {
auth.frontierHost = document["frontierHost"].toString(); auth.frontierHost = document["frontierHost"].toString();
auth.region = 3; auth.region = 3;
window.launchGame(auth); window.launchGame(*info.settings, auth);
} else { } else {
auto messageBox = new QMessageBox(QMessageBox::Icon::Critical, "Failed to Login", "Invalid username/password."); auto messageBox = new QMessageBox(QMessageBox::Icon::Critical, "Failed to Login", "Invalid username/password.");
messageBox->show(); messageBox->show();
@ -64,6 +64,6 @@ void SapphireLauncher::registerAccount(QString lobbyUrl, const LoginInformation&
auth.frontierHost = document["frontierHost"].toString(); auth.frontierHost = document["frontierHost"].toString();
auth.region = 3; auth.region = 3;
window.launchGame(auth); window.launchGame(*info.settings, auth);
}); });
} }

View file

@ -2,15 +2,15 @@
#include <QString> #include <QString>
#include "xivlauncher.h" #include "launchercore.h"
class SapphireLauncher : QObject { class SapphireLauncher : QObject {
public: public:
SapphireLauncher(LauncherWindow& window); SapphireLauncher(LauncherCore& window);
void login(QString lobbyUrl, const LoginInformation& info); void login(QString lobbyUrl, const LoginInformation& info);
void registerAccount(QString lobbyUrl, const LoginInformation& info); void registerAccount(QString lobbyUrl, const LoginInformation& info);
private: private:
LauncherWindow& window; LauncherCore& window;
}; };

View file

@ -11,9 +11,10 @@
#include <QProcess> #include <QProcess>
#include <QGridLayout> #include <QGridLayout>
#include "xivlauncher.h" #include "launchercore.h"
#include "launcherwindow.h"
SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window(window), QWidget(parent) { SettingsWindow::SettingsWindow(LauncherWindow& window, LauncherCore& core, QWidget* parent) : core(core), window(window), QWidget(parent) {
setWindowTitle("Settings"); setWindowTitle("Settings");
setWindowModality(Qt::WindowModality::ApplicationModal); setWindowModality(Qt::WindowModality::ApplicationModal);
@ -30,17 +31,17 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
auto addProfileButton = new QPushButton("Add Profile"); auto addProfileButton = new QPushButton("Add Profile");
connect(addProfileButton, &QPushButton::pressed, [=] { connect(addProfileButton, &QPushButton::pressed, [=] {
profileWidget->setCurrentRow(this->window.addProfile()); profileWidget->setCurrentRow(this->core.addProfile());
this->window.saveSettings(); this->core.saveSettings();
}); });
mainLayout->addWidget(addProfileButton, 2, 0); mainLayout->addWidget(addProfileButton, 2, 0);
deleteProfileButton = new QPushButton("Delete Profile"); deleteProfileButton = new QPushButton("Delete Profile");
connect(deleteProfileButton, &QPushButton::pressed, [=] { connect(deleteProfileButton, &QPushButton::pressed, [=] {
profileWidget->setCurrentRow(this->window.deleteProfile(getCurrentProfile().name)); profileWidget->setCurrentRow(this->core.deleteProfile(getCurrentProfile().name));
this->window.saveSettings(); this->core.saveSettings();
}); });
mainLayout->addWidget(deleteProfileButton, 3, 0); mainLayout->addWidget(deleteProfileButton, 3, 0);
@ -49,7 +50,7 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
getCurrentProfile().name = nameEdit->text(); getCurrentProfile().name = nameEdit->text();
reloadControls(); reloadControls();
this->window.saveSettings(); this->core.saveSettings();
}); });
mainLayout->addWidget(nameEdit, 0, 1); mainLayout->addWidget(nameEdit, 0, 1);
@ -66,10 +67,10 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
connect(directXCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [=](int index) { connect(directXCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [=](int index) {
getCurrentProfile().useDX9 = directXCombo->currentIndex() == 1; getCurrentProfile().useDX9 = directXCombo->currentIndex() == 1;
this->window.saveSettings(); this->core.saveSettings();
}); });
currentGameDirectory = new QLabel(window.currentProfile().gamePath); currentGameDirectory = new QLabel();
currentGameDirectory->setWordWrap(true); currentGameDirectory->setWordWrap(true);
gameBoxLayout->addRow("Game Directory", currentGameDirectory); gameBoxLayout->addRow("Game Directory", currentGameDirectory);
@ -78,9 +79,9 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
getCurrentProfile().gamePath = QFileDialog::getExistingDirectory(this, "Open Game Directory"); getCurrentProfile().gamePath = QFileDialog::getExistingDirectory(this, "Open Game Directory");
this->reloadControls(); this->reloadControls();
this->window.saveSettings(); this->core.saveSettings();
this->window.readGameVersion(); this->core.readGameVersion();
}); });
gameBoxLayout->addWidget(selectDirectoryButton); gameBoxLayout->addWidget(selectDirectoryButton);
@ -100,7 +101,7 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
connect(encryptArgumentsBox, &QCheckBox::stateChanged, [=](int) { connect(encryptArgumentsBox, &QCheckBox::stateChanged, [=](int) {
getCurrentProfile().encryptArguments = encryptArgumentsBox->isChecked(); getCurrentProfile().encryptArguments = encryptArgumentsBox->isChecked();
this->window.saveSettings(); this->core.saveSettings();
}); });
loginBoxLayout->addRow("Encrypt Game Arguments", encryptArgumentsBox); loginBoxLayout->addRow("Encrypt Game Arguments", encryptArgumentsBox);
@ -108,7 +109,7 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
connect(enableDalamudBox, &QCheckBox::stateChanged, [=](int) { connect(enableDalamudBox, &QCheckBox::stateChanged, [=](int) {
getCurrentProfile().enableDalamud = enableDalamudBox->isChecked(); getCurrentProfile().enableDalamud = enableDalamudBox->isChecked();
this->window.saveSettings(); this->core.saveSettings();
}); });
loginBoxLayout->addRow("Enable Dalamud Injection", enableDalamudBox); loginBoxLayout->addRow("Enable Dalamud Injection", enableDalamudBox);
@ -120,8 +121,7 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
getCurrentProfile().isSapphire = index == 1; getCurrentProfile().isSapphire = index == 1;
reloadControls(); reloadControls();
this->window.reloadControls(); this->core.saveSettings();
this->window.saveSettings();
}); });
loginBoxLayout->addRow("Server Lobby", serverType); loginBoxLayout->addRow("Server Lobby", serverType);
@ -129,7 +129,7 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
lobbyServerURL = new QLineEdit(); lobbyServerURL = new QLineEdit();
connect(lobbyServerURL, &QLineEdit::editingFinished, [=] { connect(lobbyServerURL, &QLineEdit::editingFinished, [=] {
getCurrentProfile().lobbyURL = lobbyServerURL->text(); getCurrentProfile().lobbyURL = lobbyServerURL->text();
this->window.saveSettings(); this->core.saveSettings();
}); });
loginBoxLayout->addRow("Lobby URL", lobbyServerURL); loginBoxLayout->addRow("Lobby URL", lobbyServerURL);
@ -137,8 +137,7 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
connect(rememberUsernameBox, &QCheckBox::stateChanged, [=](int) { connect(rememberUsernameBox, &QCheckBox::stateChanged, [=](int) {
getCurrentProfile().rememberUsername = rememberUsernameBox->isChecked(); getCurrentProfile().rememberUsername = rememberUsernameBox->isChecked();
this->window.reloadControls(); this->core.saveSettings();
this->window.saveSettings();
}); });
loginBoxLayout->addRow("Remember Username?", rememberUsernameBox); loginBoxLayout->addRow("Remember Username?", rememberUsernameBox);
@ -146,8 +145,7 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
connect(rememberPasswordBox, &QCheckBox::stateChanged, [=](int) { connect(rememberPasswordBox, &QCheckBox::stateChanged, [=](int) {
getCurrentProfile().rememberPassword = rememberPasswordBox->isChecked(); getCurrentProfile().rememberPassword = rememberPasswordBox->isChecked();
this->window.reloadControls(); this->core.saveSettings();
this->window.saveSettings();
}); });
loginBoxLayout->addRow("Remember Password?", rememberPasswordBox); loginBoxLayout->addRow("Remember Password?", rememberPasswordBox);
@ -163,7 +161,7 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
infoLabel->setWordWrap(true); infoLabel->setWordWrap(true);
wineBoxLayout->addWidget(infoLabel); wineBoxLayout->addWidget(infoLabel);
winePathLabel = new QLabel(window.currentProfile().winePath); winePathLabel = new QLabel();
winePathLabel->setWordWrap(true); winePathLabel->setWordWrap(true);
wineBoxLayout->addRow("Wine Executable", winePathLabel); wineBoxLayout->addRow("Wine Executable", winePathLabel);
@ -184,19 +182,19 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
connect(wineVersionCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [this](int index) { connect(wineVersionCombo, static_cast<void(QComboBox::*)(int)>(&QComboBox::currentIndexChanged), [this](int index) {
getCurrentProfile().wineVersion = index; getCurrentProfile().wineVersion = index;
this->window.readWineInfo(getCurrentProfile()); this->core.readWineInfo(getCurrentProfile());
this->window.saveSettings(); this->core.saveSettings();
this->reloadControls(); this->reloadControls();
}); });
connect(selectWineButton, &QPushButton::pressed, [this] { connect(selectWineButton, &QPushButton::pressed, [this] {
getCurrentProfile().winePath = QFileDialog::getOpenFileName(this, "Open Wine Executable"); getCurrentProfile().winePath = QFileDialog::getOpenFileName(this, "Open Wine Executable");
this->window.saveSettings(); this->core.saveSettings();
this->reloadControls(); this->reloadControls();
}); });
winePrefixDirectory = new QLabel(window.currentProfile().winePrefixPath); winePrefixDirectory = new QLabel();
winePrefixDirectory->setWordWrap(true); winePrefixDirectory->setWordWrap(true);
wineBoxLayout->addRow("Wine Prefix", winePrefixDirectory); wineBoxLayout->addRow("Wine Prefix", winePrefixDirectory);
@ -204,7 +202,7 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
connect(selectPrefixButton, &QPushButton::pressed, [this] { connect(selectPrefixButton, &QPushButton::pressed, [this] {
getCurrentProfile().winePrefixPath = QFileDialog::getExistingDirectory(this, "Open Wine Prefix"); getCurrentProfile().winePrefixPath = QFileDialog::getExistingDirectory(this, "Open Wine Prefix");
this->window.saveSettings(); this->core.saveSettings();
this->reloadControls(); this->reloadControls();
}); });
wineBoxLayout->addWidget(selectPrefixButton); wineBoxLayout->addWidget(selectPrefixButton);
@ -216,12 +214,11 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
wineBoxLayout->addWidget(openPrefixButton); wineBoxLayout->addWidget(openPrefixButton);
auto enableDXVKhud = new QCheckBox("Enable DXVK HUD"); auto enableDXVKhud = new QCheckBox("Enable DXVK HUD");
enableDXVKhud->setChecked(window.currentProfile().enableDXVKhud);
wineBoxLayout->addWidget(enableDXVKhud); wineBoxLayout->addWidget(enableDXVKhud);
connect(enableDXVKhud, &QCheckBox::stateChanged, [this](int state) { connect(enableDXVKhud, &QCheckBox::stateChanged, [this](int state) {
this->window.currentProfile().enableDXVKhud = state; getCurrentProfile().enableDXVKhud = state;
this->window.settings.setValue("enableDXVKhud", static_cast<bool>(state)); this->core.settings.setValue("enableDXVKhud", static_cast<bool>(state));
}); });
#endif #endif
@ -237,7 +234,7 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
connect(useEsync, &QCheckBox::stateChanged, [this](int state) { connect(useEsync, &QCheckBox::stateChanged, [this](int state) {
getCurrentProfile().useEsync = state; getCurrentProfile().useEsync = state;
this->window.saveSettings(); this->core.saveSettings();
}); });
useGamescope = new QCheckBox("Use Gamescope"); useGamescope = new QCheckBox("Use Gamescope");
@ -251,7 +248,7 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
connect(useGamescope, &QCheckBox::stateChanged, [this](int state) { connect(useGamescope, &QCheckBox::stateChanged, [this](int state) {
getCurrentProfile().useGamescope = state; getCurrentProfile().useGamescope = state;
this->window.saveSettings(); this->core.saveSettings();
}); });
useGamemode = new QCheckBox("Use Gamemode"); useGamemode = new QCheckBox("Use Gamemode");
@ -265,7 +262,7 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
connect(useGamemode, &QCheckBox::stateChanged, [this](int state) { connect(useGamemode, &QCheckBox::stateChanged, [this](int state) {
getCurrentProfile().useGamemode = state; getCurrentProfile().useGamemode = state;
this->window.saveSettings(); this->core.saveSettings();
}); });
#endif #endif
@ -282,15 +279,15 @@ void SettingsWindow::reloadControls() {
profileWidget->clear(); profileWidget->clear();
for(auto profile : window.profileList()) { for(const auto& profile : core.profileList()) {
profileWidget->addItem(profile); profileWidget->addItem(profile);
} }
profileWidget->setCurrentRow(oldRow); profileWidget->setCurrentRow(oldRow);
// deleting the main profile is unsupported behavior // deleting the main profile is unsupported behavior
deleteProfileButton->setEnabled(window.profileList().size() > 1); deleteProfileButton->setEnabled(core.profileList().size() > 1);
ProfileSettings& profile = window.getProfile(profileWidget->currentRow()); ProfileSettings& profile = core.getProfile(profileWidget->currentRow());
nameEdit->setText(profile.name); nameEdit->setText(profile.name);
// game // game
@ -325,7 +322,7 @@ void SettingsWindow::reloadControls() {
} }
ProfileSettings& SettingsWindow::getCurrentProfile() { ProfileSettings& SettingsWindow::getCurrentProfile() {
return this->window.getProfile(profileWidget->currentRow()); return this->core.getProfile(profileWidget->currentRow());
} }
void SettingsWindow::openPath(const QString path) { void SettingsWindow::openPath(const QString path) {

View file

@ -8,12 +8,13 @@
#include <QLabel> #include <QLabel>
#include <QPushButton> #include <QPushButton>
class LauncherCore;
class LauncherWindow; class LauncherWindow;
struct ProfileSettings; struct ProfileSettings;
class SettingsWindow : public QWidget { class SettingsWindow : public QWidget {
public: public:
SettingsWindow(LauncherWindow& window, QWidget* parent = nullptr); SettingsWindow(LauncherWindow& window, LauncherCore& core, QWidget* parent = nullptr);
public slots: public slots:
void reloadControls(); void reloadControls();
@ -48,4 +49,5 @@ private:
bool currentlyReloadingControls = false; bool currentlyReloadingControls = false;
LauncherWindow& window; LauncherWindow& window;
LauncherCore& core;
}; };

View file

@ -6,7 +6,7 @@
#include "squarelauncher.h" #include "squarelauncher.h"
SquareBoot::SquareBoot(LauncherWindow& window, SquareLauncher& launcher) : window(window), launcher(launcher) { SquareBoot::SquareBoot(LauncherCore& window, SquareLauncher& launcher) : window(window), launcher(launcher) {
} }
@ -17,7 +17,7 @@ void SquareBoot::bootCheck(LoginInformation& info) {
QUrl url; QUrl url;
url.setScheme("http"); url.setScheme("http");
url.setHost("patch-bootver.ffxiv.com"); url.setHost("patch-bootver.ffxiv.com");
url.setPath(QString("/http/win32/ffxivneo_release_boot/%1").arg(window.currentProfile().bootVersion)); url.setPath(QString("/http/win32/ffxivneo_release_boot/%1").arg(info.settings->bootVersion));
url.setQuery(query); url.setQuery(query);
auto request = QNetworkRequest(url); auto request = QNetworkRequest(url);

View file

@ -1,16 +1,16 @@
#pragma once #pragma once
#include "xivlauncher.h" #include "launchercore.h"
class SquareLauncher; class SquareLauncher;
class SquareBoot : public QObject { class SquareBoot : public QObject {
public: public:
SquareBoot(LauncherWindow& window, SquareLauncher& launcher); SquareBoot(LauncherCore& window, SquareLauncher& launcher);
void bootCheck(LoginInformation& info); void bootCheck(LoginInformation& info);
private: private:
LauncherWindow& window; LauncherCore& window;
SquareLauncher& launcher; SquareLauncher& launcher;
}; };

View file

@ -6,9 +6,9 @@
#include <QRegularExpressionMatch> #include <QRegularExpressionMatch>
#include <QMessageBox> #include <QMessageBox>
#include "xivlauncher.h" #include "launchercore.h"
SquareLauncher::SquareLauncher(LauncherWindow& window) : window(window) { SquareLauncher::SquareLauncher(LauncherCore& window) : window(window) {
} }
@ -88,7 +88,7 @@ void SquareLauncher::login(const LoginInformation& info, const QUrl referer) {
auth.region = parts[5].toInt(); auth.region = parts[5].toInt();
auth.maxExpansion = parts[13].toInt(); auth.maxExpansion = parts[13].toInt();
readExpansionVersions(auth.maxExpansion); readExpansionVersions(info, auth.maxExpansion);
registerSession(info); registerSession(info);
} }
@ -103,7 +103,7 @@ void SquareLauncher::registerSession(const LoginInformation& info) {
QUrl url; QUrl url;
url.setScheme("https"); url.setScheme("https");
url.setHost("patch-gamever.ffxiv.com"); url.setHost("patch-gamever.ffxiv.com");
url.setPath(QString("/http/win32/ffxivneo_release_game/%1/%2").arg(window.currentProfile().gameVersion, SID)); url.setPath(QString("/http/win32/ffxivneo_release_game/%1/%2").arg(info.settings->gameVersion, SID));
auto request = QNetworkRequest(url); auto request = QNetworkRequest(url);
window.setSSL(request); window.setSSL(request);
@ -111,7 +111,7 @@ void SquareLauncher::registerSession(const LoginInformation& info) {
request.setRawHeader("User-Agent", "FFXIV PATCH CLIENT"); request.setRawHeader("User-Agent", "FFXIV PATCH CLIENT");
request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded"); request.setHeader(QNetworkRequest::ContentTypeHeader,"application/x-www-form-urlencoded");
QString report = window.currentProfile().bootVersion + "=" + getBootHash(); QString report = info.settings->bootVersion + "=" + getBootHash(info);
for(int i = 0; i < expansionVersions.size(); i++) for(int i = 0; i < expansionVersions.size(); i++)
report += QString("\nex%1\t%2").arg(QString::number(i + 1), expansionVersions[i]); report += QString("\nex%1\t%2").arg(QString::number(i + 1), expansionVersions[i]);
@ -121,7 +121,7 @@ void SquareLauncher::registerSession(const LoginInformation& info) {
if(reply->rawHeaderList().contains("X-Patch-Unique-Id")) { if(reply->rawHeaderList().contains("X-Patch-Unique-Id")) {
auth.SID = reply->rawHeader("X-Patch-Unique-Id"); auth.SID = reply->rawHeader("X-Patch-Unique-Id");
window.launchGame(auth); window.launchGame(*info.settings, auth);
} else { } else {
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."); 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(); messageBox->show();
@ -129,7 +129,7 @@ void SquareLauncher::registerSession(const LoginInformation& info) {
}); });
} }
QString SquareLauncher::getBootHash() { QString SquareLauncher::getBootHash(const LoginInformation& info) {
const QList<QString> fileList = const QList<QString> fileList =
{ {
"ffxivboot.exe", "ffxivboot.exe",
@ -142,7 +142,7 @@ QString SquareLauncher::getBootHash() {
QString result; QString result;
for (int i = 0; i < fileList.count(); i++) { for (int i = 0; i < fileList.count(); i++) {
result += fileList[i] + "/" + getFileHash(window.currentProfile().gamePath + "/boot/" + fileList[i]); result += fileList[i] + "/" + getFileHash(info.settings->gamePath + "/boot/" + fileList[i]);
if (i != fileList.length() - 1) if (i != fileList.length() - 1)
result += ","; result += ",";
@ -151,9 +151,9 @@ QString SquareLauncher::getBootHash() {
return result; return result;
} }
void SquareLauncher::readExpansionVersions(int max) { void SquareLauncher::readExpansionVersions(const LoginInformation& info, int max) {
expansionVersions.clear(); expansionVersions.clear();
for(int i = 0; i < max; i++) for(int i = 0; i < max; i++)
expansionVersions.push_back(window.readVersion(QString("%1/game/sqpack/ex%2/ex%2.ver").arg(window.currentProfile().gamePath, QString::number(i + 1)))); expansionVersions.push_back(window.readVersion(QString("%1/game/sqpack/ex%2/ex%2.ver").arg(info.settings->gamePath, QString::number(i + 1))));
} }

View file

@ -1,25 +1,25 @@
#pragma once #pragma once
#include "xivlauncher.h" #include "launchercore.h"
class SquareLauncher : public QObject { class SquareLauncher : public QObject {
public: public:
SquareLauncher(LauncherWindow& window); SquareLauncher(LauncherCore& window);
void getStored(const LoginInformation& info); void getStored(const LoginInformation& info);
void login(const LoginInformation& info, const QUrl referer); void login(const LoginInformation& info, QUrl referer);
void registerSession(const LoginInformation& info); void registerSession(const LoginInformation& info);
private: private:
QString getBootHash(); QString getBootHash(const LoginInformation& info);
void readExpansionVersions(int max); void readExpansionVersions(const LoginInformation& info, int max);
QString stored, SID; QString stored, SID;
LoginAuth auth; LoginAuth auth;
LauncherWindow& window; LauncherCore& window;
QList<QString> expansionVersions; QList<QString> expansionVersions;
}; };