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:
parent
4f8d190c37
commit
047dbfc1b8
8 changed files with 187 additions and 11 deletions
|
@ -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}"
|
||||
|
|
7
external/CMakeLists.txt
vendored
7
external/CMakeLists.txt
vendored
|
@ -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
87
src/assetupdater.cpp
Normal 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
25
src/assetupdater.h
Normal 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;
|
||||
};
|
|
@ -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;
|
||||
|
|
|
@ -40,6 +40,7 @@ private:
|
|||
|
||||
// login
|
||||
QCheckBox* encryptArgumentsBox = nullptr;
|
||||
QCheckBox* enableDalamudBox = nullptr;
|
||||
QComboBox* serverType = nullptr;
|
||||
QLineEdit* lobbyServerURL = nullptr;
|
||||
QCheckBox* rememberUsernameBox = nullptr, *rememberPasswordBox = nullptr;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue