1
Fork 0
mirror of https://github.com/redstrate/Astra.git synced 2025-04-22 12:47:44 +00:00

Add Dalamud injection support

* Also includes a basic asset downloader for Dalamud + NativeLauncher
This commit is contained in:
redstrate 2021-11-23 14:37:37 -05:00
parent 4f8d190c37
commit 047dbfc1b8
8 changed files with 187 additions and 11 deletions

View file

@ -17,14 +17,15 @@ add_executable(xivlauncher
src/squareboot.cpp
src/squarelauncher.cpp
src/settingswindow.cpp
src/blowfish.cpp)
src/blowfish.cpp src/assetupdater.cpp src/assetupdater.h)
target_link_libraries(xivlauncher Qt5::Core Qt5::Widgets Qt5::Network qt5keychain)
target_link_libraries(xivlauncher Qt5::Core Qt5::Widgets Qt5::Network qt5keychain QuaZip)
# disgusting, thanks qtkeychain
# disgusting, thanks qtkeychain and quazip
target_include_directories(xivlauncher PRIVATE
${CMAKE_BINARY_DIR}/_deps/qtkeychain-src
${CMAKE_BINARY_DIR}/_deps/qtkeychain-build)
${CMAKE_BINARY_DIR}/_deps/qtkeychain-build
${CMAKE_BINARY_DIR}/_deps/quazip-src)
install(TARGETS xivlauncher
DESTINATION "${INSTALL_BIN_PATH}"

View file

@ -10,3 +10,10 @@ set(BUILD_WITH_QT6 OFF CACHE BOOL "" FORCE)
set(QTKEYCHAIN_STATIC ON CACHE BOOL "" FORCE)
FetchContent_MakeAvailable(qtkeychain)
FetchContent_Declare(
quazip
GIT_REPOSITORY https://github.com/stachenov/quazip.git
GIT_TAG v1.2
)
FetchContent_MakeAvailable(quazip)

87
src/assetupdater.cpp Normal file
View file

@ -0,0 +1,87 @@
#include "assetupdater.h"
#include <QNetworkReply>
#include <QFile>
#include <QStandardPaths>
#include <quazip/JlCompress.h>
#include "xivlauncher.h"
const QString dalamudRemotePath = "https://goatcorp.github.io/dalamud-distrib/";
const QString dalamudVersion = "latest";
const QString nativeLauncherRemotePath = "https://github.com/redstrate/nativelauncher/releases/download/";
const QString nativeLauncherVersion = "v1.0.0";
AssetUpdater::AssetUpdater(LauncherWindow &launcher) : launcher(launcher) {
connect(launcher.mgr, &QNetworkAccessManager::finished, this, &AssetUpdater::finishDownload);
launcher.mgr->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
}
void AssetUpdater::update() {
QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
const bool hasDalamud = QFile::exists(dataDir + "/NativeLauncher.exe") && QFile::exists(dataDir + "/Dalamud");
// first we determine if we need dalamud
const bool needsDalamud = launcher.currentProfile().enableDalamud && !hasDalamud;
if(needsDalamud) {
// download nativelauncher release (needed to launch the game with fixed ACLs)
{
QNetworkRequest request(nativeLauncherRemotePath + nativeLauncherVersion + "/NativeLauncher.exe");
auto reply = launcher.mgr->get(request);
reply->setObjectName("NativeLauncher");
}
// download dalamud (... duh)
{
QNetworkRequest request(dalamudRemotePath + dalamudVersion + ".zip");
auto reply = launcher.mgr->get(request);
reply->setObjectName("Dalamud");
}
} else {
// non-dalamud users can bypass this process since it's not needed
finishedUpdating();
}
}
void AssetUpdater::finishDownload(QNetworkReply* reply) {
const auto checkIfFinished = [=] {
if(QFile::exists(tempDir.path() + "/NativeLauncher.exe") && QFile::exists(tempDir.path() + "/latest.zip")) {
beginInstall();
}
};
if(reply->objectName() == "Dalamud") {
QFile file(tempDir.path() + "/latest.zip");
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();
checkIfFinished();
} else if(reply->objectName() == "NativeLauncher") {
QFile file(tempDir.path() + "/NativeLauncher.exe");
file.open(QIODevice::WriteOnly);
file.write(reply->readAll());
file.close();
checkIfFinished();
}
}
void AssetUpdater::beginInstall() {
QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
bool success = !JlCompress::extractDir(tempDir.path() + "/latest.zip", dataDir + "/Dalamud").empty();
if(success) {
QFile::copy(tempDir.path() + "/NativeLauncher.exe", dataDir + "/NativeLauncher.exe");
finishedUpdating();
} else {
// STUB: install failure
}
}

25
src/assetupdater.h Normal file
View file

@ -0,0 +1,25 @@
#pragma once
#include <QObject>
#include <QTemporaryDir>
class LauncherWindow;
class QNetworkReply;
class AssetUpdater : public QObject {
Q_OBJECT
public:
AssetUpdater(LauncherWindow& launcher);
void update();
void finishDownload(QNetworkReply* reply);
void beginInstall();
signals:
void finishedUpdating();
private:
LauncherWindow& launcher;
QTemporaryDir tempDir;
};

View file

@ -100,11 +100,18 @@ SettingsWindow::SettingsWindow(LauncherWindow& window, QWidget* parent) : window
connect(encryptArgumentsBox, &QCheckBox::stateChanged, [=](int) {
getCurrentProfile().encryptArguments = encryptArgumentsBox->isChecked();
this->window.reloadControls();
this->window.saveSettings();
});
loginBoxLayout->addRow("Encrypt Game Arguments", encryptArgumentsBox);
enableDalamudBox = new QCheckBox();
connect(enableDalamudBox, &QCheckBox::stateChanged, [=](int) {
getCurrentProfile().enableDalamud = enableDalamudBox->isChecked();
this->window.saveSettings();
});
loginBoxLayout->addRow("Enable Dalamud Injection", enableDalamudBox);
serverType = new QComboBox();
serverType->insertItem(0, "Square Enix");
serverType->insertItem(1, "Sapphire");
@ -310,6 +317,8 @@ void SettingsWindow::reloadControls() {
rememberUsernameBox->setChecked(profile.rememberUsername);
rememberPasswordBox->setChecked(profile.rememberPassword);
enableDalamudBox->setChecked(profile.enableDalamud);
window.reloadControls();
currentlyReloadingControls = false;

View file

@ -40,6 +40,7 @@ private:
// login
QCheckBox* encryptArgumentsBox = nullptr;
QCheckBox* enableDalamudBox = nullptr;
QComboBox* serverType = nullptr;
QLineEdit* lobbyServerURL = nullptr;
QCheckBox* rememberUsernameBox = nullptr, *rememberPasswordBox = nullptr;

View file

@ -15,6 +15,8 @@
#include <QMessageBox>
#include <QMenuBar>
#include <QCoreApplication>
#include <QStandardPaths>
#include <QRegularExpressionMatch>
#if defined(Q_OS_MAC)
#include <sys/sysctl.h>
@ -31,6 +33,7 @@
#include "squareboot.h"
#include "settingswindow.h"
#include "blowfish.h"
#include "assetupdater.h"
void LauncherWindow::setSSL(QNetworkRequest& request) {
QSslConfiguration config;
@ -106,6 +109,12 @@ QString encryptGameArg(QString arg) {
void LauncherWindow::launchGame(const LoginAuth auth) {
QList<QString> arguments;
QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
if(currentProfile().enableDalamud) {
arguments.push_back(dataDir + "/NativeLauncher.exe");
}
// now for the actual game...
if(currentProfile().useDX9) {
arguments.push_back(currentProfile().gamePath + "\\game\\ffxiv.exe");
@ -135,6 +144,25 @@ void LauncherWindow::launchGame(const LoginAuth auth) {
}
}
auto gameProcess = new QProcess(this);
if(currentProfile().enableDalamud) {
connect(gameProcess, &QProcess::readyReadStandardOutput, [this, gameProcess] {
QString output = gameProcess->readAllStandardOutput();
auto dalamudProcess = new QProcess();
QStringList dalamudEnv = gameProcess->environment();
dalamudEnv << "XL_WINEONLINUX=true";
dalamudProcess->setEnvironment(dalamudEnv);
QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
dalamudProcess->start(currentProfile().winePath, {dataDir + "/Dalamud/" + "Dalamud.Injector.exe", output});
});
}
if(currentProfile().encryptArguments) {
QString argJoined;
for(auto arg : gameArgs) {
@ -143,20 +171,22 @@ void LauncherWindow::launchGame(const LoginAuth auth) {
auto earg = encryptGameArg(argJoined);
arguments.append(earg);
launchExecutable(arguments);
launchExecutable(gameProcess, arguments);
} else {
for(auto arg : gameArgs) {
arguments.push_back(QString(" %1=%2").arg(arg.key, arg.value));
}
launchExecutable(arguments);
launchExecutable(gameProcess, arguments);
}
}
void LauncherWindow::launchExecutable(const QStringList args) {
auto process = new QProcess(this);
process->setProcessChannelMode(QProcess::ForwardedChannels);
launchExecutable(process, args);
}
void LauncherWindow::launchExecutable(QProcess* process, const QStringList args) {
QList<QString> arguments;
QStringList env = QProcess::systemEnvironment();
@ -191,6 +221,7 @@ void LauncherWindow::launchExecutable(const QStringList args) {
process->setWorkingDirectory(currentProfile().gamePath + "/game/");
process->setEnvironment(env);
process->start(executable, arguments);
}
@ -268,6 +299,8 @@ void LauncherWindow::readInitialInformation() {
profile.useGamescope = settings.value("useGamescope", false).toBool();
profile.enableDXVKhud = settings.value("enableDXVKhud", false).toBool();
profile.enableDalamud = settings.value("enableDalamud", false).toBool();
profileSettings[settings.value("index").toInt()] = profile;
settings.endGroup();
@ -316,6 +349,7 @@ LauncherWindow::LauncherWindow(QWidget* parent) :
sapphireLauncher = new SapphireLauncher(*this);
squareLauncher = new SquareLauncher(*this);
squareBoot = new SquareBoot(*this, *squareLauncher);
assetUpdater = new AssetUpdater(*this);
readInitialInformation();
@ -406,7 +440,7 @@ LauncherWindow::LauncherWindow(QWidget* parent) :
emptyWidget->setLayout(layout);
setCentralWidget(emptyWidget);
connect(loginButton, &QPushButton::released, [=] {
connect(assetUpdater, &AssetUpdater::finishedUpdating, [=] {
auto info = LoginInformation{usernameEdit->text(), passwordEdit->text(), otpEdit->text()};
if(currentProfile().rememberUsername) {
@ -430,6 +464,11 @@ LauncherWindow::LauncherWindow(QWidget* parent) :
}
});
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()};
@ -536,6 +575,8 @@ void LauncherWindow::saveSettings() {
settings.setValue("rememberUsername", profile.rememberUsername);
settings.setValue("rememberPassword", profile.rememberPassword);
settings.setValue("enableDalamud", profile.enableDalamud);
settings.endGroup();
}
}

View file

@ -8,10 +8,12 @@
#include <QCheckBox>
#include <QPushButton>
#include <QUuid>
#include <QProcess>
class SapphireLauncher;
class SquareLauncher;
class SquareBoot;
class AssetUpdater;
struct ProfileSettings {
QUuid uuid;
@ -33,6 +35,7 @@ struct ProfileSettings {
bool useEsync = false, useGamescope = false, useGamemode = false;
bool useDX9 = false;
bool enableDXVKhud = false;
bool enableDalamud = false;
// login
bool encryptArguments = false;
@ -75,7 +78,8 @@ public:
int deleteProfile(QString name);
void launchGame(const LoginAuth auth);
void launchExecutable(const QStringList args);
void launchExecutable(QStringList args);
void launchExecutable(QProcess* process, QStringList args);
void buildRequest(QNetworkRequest& request);
void setSSL(QNetworkRequest& request);
QString readVersion(QString path);
@ -98,6 +102,7 @@ private:
SapphireLauncher* sapphireLauncher;
SquareBoot* squareBoot;
SquareLauncher* squareLauncher;
AssetUpdater* assetUpdater;
QComboBox* profileSelect;
QLineEdit* usernameEdit, *passwordEdit;