diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt
index f429b87..984594d 100644
--- a/launcher/CMakeLists.txt
+++ b/launcher/CMakeLists.txt
@@ -17,6 +17,7 @@ target_sources(astra PRIVATE
include/squareboot.h
include/squarelauncher.h
include/steamapi.h
+ include/utility.h
src/account.cpp
src/accountmanager.cpp
@@ -32,6 +33,7 @@ target_sources(astra PRIVATE
src/squareboot.cpp
src/squarelauncher.cpp
src/steamapi.cpp
+ src/utility.cpp
resources.qrc)
kconfig_add_kcfg_files(astra GENERATE_MOC config.kcfgc accountconfig.kcfgc profileconfig.kcfgc)
diff --git a/launcher/config.kcfg b/launcher/config.kcfg
index 0bb1937..07abb68 100644
--- a/launcher/config.kcfg
+++ b/launcher/config.kcfg
@@ -17,5 +17,8 @@ SPDX-License-Identifier: CC0-1.0
+
+ false
+
diff --git a/launcher/include/account.h b/launcher/include/account.h
index 56d5979..d474887 100644
--- a/launcher/include/account.h
+++ b/launcher/include/account.h
@@ -3,6 +3,7 @@
#pragma once
+#include
#include
#include "accountconfig.h"
@@ -66,6 +67,8 @@ public:
Q_INVOKABLE QString getOTP();
+ QDir getConfigDir() const;
+
Q_SIGNALS:
void nameChanged();
void lodestoneIdChanged();
diff --git a/launcher/include/assetupdater.h b/launcher/include/assetupdater.h
index ca1e441..5bc4f27 100644
--- a/launcher/include/assetupdater.h
+++ b/launcher/include/assetupdater.h
@@ -37,6 +37,11 @@ private:
QString remoteRuntimeVersion;
QTemporaryDir tempDir;
+ QDir dataDir;
+ QDir appDataDir;
+ QDir dalamudDir;
+ QDir dalamudAssetDir;
+ QDir dalamudRuntimeDir;
bool doneDownloadingDalamud = false;
bool doneDownloadingRuntimeCore = false;
@@ -48,6 +53,5 @@ private:
QList dalamudAssetNeededFilenames;
QJsonArray remoteDalamudAssetArray;
- QString dataDir;
Profile &m_profile;
};
diff --git a/launcher/include/launchercore.h b/launcher/include/launchercore.h
index c096ca0..bade3b3 100755
--- a/launcher/include/launchercore.h
+++ b/launcher/include/launchercore.h
@@ -63,6 +63,7 @@ class LauncherCore : public QObject
Q_PROPERTY(AccountManager *accountManager READ accountManager CONSTANT)
Q_PROPERTY(bool closeWhenLaunched READ closeWhenLaunched WRITE setCloseWhenLaunched NOTIFY closeWhenLaunchedChanged)
Q_PROPERTY(bool showNews READ showNews WRITE setShowNews NOTIFY showNewsChanged)
+ Q_PROPERTY(bool keepPatches READ keepPatches WRITE setKeepPatches NOTIFY keepPatchesChanged)
Q_PROPERTY(Headline *headline READ headline NOTIFY newsChanged)
public:
@@ -120,6 +121,9 @@ public:
bool showNews() const;
void setShowNews(bool value);
+ bool keepPatches() const;
+ void setKeepPatches(bool value);
+
int defaultProfileIndex = 0;
bool m_isSteam = false;
@@ -147,6 +151,7 @@ signals:
void gameClosed();
void closeWhenLaunchedChanged();
void showNewsChanged();
+ void keepPatchesChanged();
void loginError(QString message);
void stageChanged(QString message);
void stageIndeterminate();
diff --git a/launcher/include/patcher.h b/launcher/include/patcher.h
index b77e188..b1a62b8 100644
--- a/launcher/include/patcher.h
+++ b/launcher/include/patcher.h
@@ -3,6 +3,7 @@
#pragma once
+#include
#include
#include
#include
@@ -25,6 +26,7 @@ signals:
private:
void checkIfDone();
+ void setupDirectories();
[[nodiscard]] bool isBoot() const
{
@@ -42,6 +44,7 @@ private:
QVector patchQueue;
+ QDir patchesDir;
QString baseDirectory;
BootData *boot_data = nullptr;
GameData *game_data = nullptr;
diff --git a/launcher/include/profile.h b/launcher/include/profile.h
index 8fb2f47..64a0af8 100644
--- a/launcher/include/profile.h
+++ b/launcher/include/profile.h
@@ -150,6 +150,8 @@ public:
return !m_wineVersion.isEmpty();
}
+ QString dalamudChannelName() const;
+
Q_SIGNALS:
void gameInstallChanged();
void nameChanged();
diff --git a/launcher/include/profilemanager.h b/launcher/include/profilemanager.h
index 7dc479b..98654e2 100644
--- a/launcher/include/profilemanager.h
+++ b/launcher/include/profilemanager.h
@@ -36,7 +36,7 @@ public:
Q_INVOKABLE bool canDelete(Profile *account) const;
- static QString getDefaultGamePath();
+ static QString getDefaultGamePath(const QString &uuid);
private:
void insertProfile(Profile *profile);
diff --git a/launcher/include/utility.h b/launcher/include/utility.h
new file mode 100644
index 0000000..6ab684f
--- /dev/null
+++ b/launcher/include/utility.h
@@ -0,0 +1,9 @@
+#pragma once
+
+#include
+
+namespace Utility
+{
+QDir stateDirectory();
+QString toWindowsPath(const QDir &dir);
+}
\ No newline at end of file
diff --git a/launcher/profileconfig.kcfg b/launcher/profileconfig.kcfg
index ecd9c71..9d08887 100644
--- a/launcher/profileconfig.kcfg
+++ b/launcher/profileconfig.kcfg
@@ -20,7 +20,7 @@ SPDX-License-Identifier: CC0-1.0
1
- ProfileManager::getDefaultGamePath()
+ ProfileManager::getDefaultGamePath(mParamuuid)
diff --git a/launcher/src/account.cpp b/launcher/src/account.cpp
index 5c1eee9..e060279 100644
--- a/launcher/src/account.cpp
+++ b/launcher/src/account.cpp
@@ -178,6 +178,13 @@ QString Account::getOTP()
return totpStr;
}
+QDir Account::getConfigDir() const
+{
+ const QDir dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+ const QDir userDir = dataDir.absoluteFilePath("user");
+ return userDir.absoluteFilePath(m_key);
+}
+
void Account::fetchAvatar()
{
if (lodestoneId().isEmpty()) {
diff --git a/launcher/src/assetupdater.cpp b/launcher/src/assetupdater.cpp
index 6b21ce6..b1071be 100644
--- a/launcher/src/assetupdater.cpp
+++ b/launcher/src/assetupdater.cpp
@@ -35,9 +35,19 @@ AssetUpdater::AssetUpdater(Profile &profile, LauncherCore &launcher, QObject *pa
launcher.mgr->setRedirectPolicy(QNetworkRequest::NoLessSafeRedirectPolicy);
dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+ dalamudDir = dataDir.absoluteFilePath("dalamud");
+ dalamudAssetDir = dalamudDir.absoluteFilePath("assets");
+ dalamudRuntimeDir = dalamudDir.absoluteFilePath("runtime");
- if (!QDir().exists(dataDir))
- QDir().mkdir(dataDir);
+ const auto createIfNeeded = [](const QDir &dir) {
+ if (!QDir().exists(dir.absolutePath()))
+ QDir().mkdir(dir.absolutePath());
+ };
+
+ createIfNeeded(dataDir);
+ createIfNeeded(dalamudDir);
+ createIfNeeded(dalamudAssetDir);
+ createIfNeeded(dalamudRuntimeDir);
}
void AssetUpdater::update()
@@ -124,7 +134,7 @@ void AssetUpdater::update()
void AssetUpdater::beginInstall()
{
if (needsDalamudInstall) {
- bool success = !JlCompress::extractDir(tempDir.path() + "/latest.zip", dataDir + "/Dalamud").empty();
+ bool success = !JlCompress::extractDir(tempDir.path() + "/latest.zip", dalamudDir.absoluteFilePath(m_profile.dalamudChannelName())).empty();
if (!success) {
// TODO: handle failure here
@@ -135,14 +145,14 @@ void AssetUpdater::beginInstall()
}
if (needsRuntimeInstall) {
- bool success = !JlCompress::extractDir(tempDir.path() + "/dotnet-core.zip", dataDir + "/DalamudRuntime").empty();
+ bool success = !JlCompress::extractDir(tempDir.path() + "/dotnet-core.zip", dalamudRuntimeDir.absolutePath()).empty();
- success |= !JlCompress::extractDir(tempDir.path() + "/dotnet-desktop.zip", dataDir + "/DalamudRuntime").empty();
+ success |= !JlCompress::extractDir(tempDir.path() + "/dotnet-desktop.zip", dalamudRuntimeDir.absolutePath()).empty();
if (!success) {
qInfo() << "Failed to install dotnet!";
} else {
- QFile file(dataDir + "/DalamudRuntime/runtime.ver");
+ QFile file(dalamudRuntimeDir.absoluteFilePath("runtime.ver"));
file.open(QIODevice::WriteOnly | QIODevice::Text);
file.write(remoteRuntimeVersion.toUtf8());
file.close();
@@ -161,7 +171,7 @@ void AssetUpdater::checkIfDalamudAssetsDone()
m_profile.dalamudAssetVersion = remoteDalamudAssetVersion;
- QFile file(dataDir + "/DalamudAssets/" + "asset.ver");
+ QFile file(dalamudAssetDir.absoluteFilePath("asset.ver"));
file.open(QIODevice::WriteOnly | QIODevice::Text);
file.write(QString::number(remoteDalamudAssetVersion).toUtf8());
file.close();
@@ -294,21 +304,15 @@ void AssetUpdater::checkIfCheckingIsDone()
auto assetReply = launcher.mgr->get(assetRequest);
connect(assetReply, &QNetworkReply::finished, [this, assetReply, assetObject = assetObject.toObject()] {
- if (!QDir().exists(dataDir + "/DalamudAssets"))
- QDir().mkdir(dataDir + "/DalamudAssets");
-
const QString fileName = assetObject["FileName"].toString();
- const QList dirPath = fileName.left(fileName.lastIndexOf("/")).split('/');
+ const QString dirPath = fileName.left(fileName.lastIndexOf("/"));
- QString build = dataDir + "/DalamudAssets/";
- for (const auto &dir : dirPath) {
- if (!QDir().exists(build + dir))
- QDir().mkdir(build + dir);
+ const QString path = dalamudAssetDir.absoluteFilePath(dirPath);
- build += dir + "/";
- }
+ if (!QDir().exists(path))
+ QDir().mkpath(path);
- QFile file(dataDir + "/DalamudAssets/" + assetObject["FileName"].toString());
+ QFile file(dalamudAssetDir.absoluteFilePath(assetObject["FileName"].toString()));
file.open(QIODevice::WriteOnly);
file.write(assetReply->readAll());
file.close();
diff --git a/launcher/src/launchercore.cpp b/launcher/src/launchercore.cpp
index d8f9235..0483df1 100755
--- a/launcher/src/launchercore.cpp
+++ b/launcher/src/launchercore.cpp
@@ -21,6 +21,7 @@
#include "launchercore.h"
#include "sapphirelauncher.h"
#include "squarelauncher.h"
+#include "utility.h"
#ifdef ENABLE_WATCHDOG
#include "watchdog.h"
@@ -103,11 +104,23 @@ void LauncherCore::beginVanillaGame(const QString &gameExecutablePath, const Pro
void LauncherCore::beginDalamudGame(const QString &gameExecutablePath, const Profile &profile, const LoginAuth &auth)
{
- QString gamePath = gameExecutablePath;
- gamePath = "Z:" + gamePath.replace('/', '\\');
+ const QDir dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
+ const QDir configDir = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
+ const QDir stateDir = Utility::stateDirectory();
- QString dataDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
- dataDir = "Z:" + dataDir.replace('/', '\\');
+ const QString logDir = stateDir.absoluteFilePath("logs");
+
+ if (!QDir().exists(logDir))
+ QDir().mkpath(logDir);
+
+ const QDir dalamudDir = dataDir.absoluteFilePath("dalamud");
+ const QDir dalamudRuntimeDir = dalamudDir.absoluteFilePath("runtime");
+ const QDir dalamudAssetDir = dalamudDir.absoluteFilePath("assets");
+ const QDir dalamudConfigPath = configDir.absoluteFilePath("dalamud-config.json");
+ const QDir dalamudPluginDir = dalamudDir.absoluteFilePath("plugins");
+
+ const QDir dalamudInstallDir = dalamudDir.absoluteFilePath(profile.dalamudChannelName());
+ const QString dalamudInjector = dalamudInstallDir.absoluteFilePath("Dalamud.Injector.exe");
auto dalamudProcess = new QProcess(this);
connect(dalamudProcess, qOverload(&QProcess::finished), this, [this](int exitCode) {
@@ -116,7 +129,7 @@ void LauncherCore::beginDalamudGame(const QString &gameExecutablePath, const Pro
});
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
- env.insert("DALAMUD_RUNTIME", dataDir + "\\DalamudRuntime");
+ env.insert("DALAMUD_RUNTIME", Utility::toWindowsPath(dalamudRuntimeDir));
#if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
env.insert("XL_WINEONLINUX", "true");
@@ -127,16 +140,17 @@ void LauncherCore::beginDalamudGame(const QString &gameExecutablePath, const Pro
launchExecutable(profile,
dalamudProcess,
- {dataDir + "/Dalamud/" + "Dalamud.Injector.exe",
+ {Utility::toWindowsPath(dalamudInjector),
"launch",
"-m",
"inject",
- "--game=" + gamePath,
- "--dalamud-configuration-path=" + dataDir + "\\dalamudConfig.json",
- "--dalamud-plugin-directory=" + dataDir + "\\installedPlugins",
- "--dalamud-asset-directory=" + dataDir + "\\DalamudAssets",
+ "--game=" + Utility::toWindowsPath(gameExecutablePath),
+ "--dalamud-working-directory=" + Utility::toWindowsPath(dalamudDir),
+ "--dalamud-configuration-path=" + Utility::toWindowsPath(dalamudConfigPath),
+ "--dalamud-plugin-directory=" + Utility::toWindowsPath(dalamudPluginDir),
+ "--dalamud-asset-directory=" + Utility::toWindowsPath(dalamudAssetDir),
"--dalamud-client-language=" + QString::number(profile.language()),
- "--logpath=" + dataDir,
+ "--logpath=" + Utility::toWindowsPath(logDir),
"--",
args},
true,
@@ -158,6 +172,12 @@ QString LauncherCore::getGameArgs(const Profile &profile, const LoginAuth &auth)
gameArgs.push_back({"SYS.Region", QString::number(auth.region)});
gameArgs.push_back({"language", QString::number(profile.language())});
gameArgs.push_back({"ver", profile.repositories.repositories[0].version});
+ gameArgs.push_back({"UserPath", Utility::toWindowsPath(profile.account()->getConfigDir().absolutePath())});
+
+ // FIXME: this should belong somewhere else...
+ if (!QDir().exists(profile.account()->getConfigDir().absolutePath())) {
+ QDir().mkpath(profile.account()->getConfigDir().absolutePath());
+ }
if (!auth.lobbyhost.isEmpty()) {
gameArgs.push_back({"DEV.GMServerHost", auth.frontierHost});
@@ -431,6 +451,20 @@ void LauncherCore::setShowNews(const bool value)
}
}
+bool LauncherCore::keepPatches() const
+{
+ return Config::keepPatches();
+}
+
+void LauncherCore::setKeepPatches(const bool value)
+{
+ if (value != Config::keepPatches()) {
+ Config::setKeepPatches(value);
+ Config::self()->save();
+ Q_EMIT keepPatchesChanged();
+ }
+}
+
void LauncherCore::refreshNews()
{
QUrlQuery query;
@@ -524,33 +558,7 @@ void LauncherCore::openOfficialLauncher(Profile *profile)
executeArg = executeArg.arg(dateTime.time().hour(), 2, 10, QLatin1Char('0'));
executeArg = executeArg.arg(dateTime.time().minute(), 2, 10, QLatin1Char('0'));
- QList arguments;
- arguments.push_back({"ExecuteArg", executeArg});
-
- // find user path
- QString userPath;
-
- // TODO: don't put this here
- QString searchDir;
-#if defined(Q_OS_LINUX) || defined(Q_OS_MAC)
- searchDir = profile->winePrefixPath() + "/drive_c/users";
-#else
- searchDir = "C:/Users";
-#endif
-
- QDirIterator it(searchDir);
- while (it.hasNext()) {
- QString dir = it.next();
- QFileInfo fi(dir);
- QString fileName = fi.fileName();
-
- // FIXME: is there no easier way to filter out these in Qt?
- if (fi.fileName() != "Public" && fi.fileName() != "." && fi.fileName() != "..") {
- userPath = fileName;
- }
- }
-
- arguments.push_back({"UserPath", QString(R"(C:\Users\%1\Documents\My Games\FINAL FANTASY XIV - A Realm Reborn)").arg(userPath)});
+ QList arguments{{"ExecuteArg", executeArg}, {"UserPath", Utility::toWindowsPath(profile->account()->getConfigDir().absolutePath())}};
const QString argFormat = " /%1 =%2";
diff --git a/launcher/src/patcher.cpp b/launcher/src/patcher.cpp
index eb8d140..70331df 100644
--- a/launcher/src/patcher.cpp
+++ b/launcher/src/patcher.cpp
@@ -21,6 +21,8 @@ Patcher::Patcher(LauncherCore &launcher, QString baseDirectory, BootData *boot_d
, boot_data(boot_data)
, m_launcher(launcher)
{
+ setupDirectories();
+
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Updater/Launcher version."));
}
@@ -30,6 +32,8 @@ Patcher::Patcher(LauncherCore &launcher, QString baseDirectory, GameData *game_d
, game_data(game_data)
, m_launcher(launcher)
{
+ setupDirectories();
+
Q_EMIT m_launcher.stageChanged(i18n("Checking the FINAL FANTASY XIV Game version."));
}
@@ -65,18 +69,18 @@ void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchL
const QString name = version;
const QStringList hashes = patchParts.size() == 9 ? (patchParts[7].split(',')) : QStringList();
const QString url = patchParts[patchParts.size() == 9 ? 8 : 5];
-
- qDebug() << "Parsed patch name: " << name;
+ const QString filename = QStringLiteral("%1.patch").arg(name);
auto url_parts = url.split('/');
const QString repository = url_parts[url_parts.size() - 3];
- const QString patchesDir = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/patches/" + repository;
+ const QDir repositoryDir = patchesDir.absoluteFilePath(repository);
- if (!QDir().exists(patchesDir))
- QDir().mkpath(patchesDir);
+ if (!QDir().exists(repositoryDir.absolutePath()))
+ QDir().mkpath(repositoryDir.absolutePath());
- if (!QFile::exists(patchesDir + "/" + name + ".patch")) {
+ const QString patchPath = repositoryDir.absoluteFilePath(filename);
+ if (!QFile::exists(patchPath)) {
qDebug() << "Need to download " + name;
QNetworkRequest patchRequest(url);
@@ -95,15 +99,13 @@ void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchL
connect(patchReply,
&QNetworkReply::finished,
- [this, ourIndex, patchesDir, name, patchReply, repository, version, hashes, hashBlockSize, length] {
- QFile file(patchesDir + "/" + name + ".patch");
+ [this, ourIndex, patchPath, name, patchReply, repository, version, hashes, hashBlockSize, length] {
+ QFile file(patchPath);
file.open(QIODevice::WriteOnly);
file.write(patchReply->readAll());
file.close();
- auto patch_path = patchesDir + "/" + name + ".patch";
-
- patchQueue[ourIndex] = {name, repository, version, patch_path, hashes, hashBlockSize, length};
+ patchQueue[ourIndex] = {name, repository, version, patchPath, hashes, hashBlockSize, length};
remainingPatches--;
checkIfDone();
@@ -111,7 +113,7 @@ void Patcher::processPatchList(QNetworkAccessManager &mgr, const QString &patchL
} else {
qDebug() << "Found existing patch: " << name;
- patchQueue[ourIndex] = {name, repository, version, patchesDir + "/" + name + ".patch", hashes, hashBlockSize, length};
+ patchQueue[ourIndex] = {name, repository, version, patchPath, hashes, hashBlockSize, length};
remainingPatches--;
checkIfDone();
@@ -190,3 +192,15 @@ void Patcher::processPatch(const QueuedPatch &patch)
verFile.write(patch.version.toUtf8());
verFile.close();
}
+
+void Patcher::setupDirectories()
+{
+ QDir dataDir;
+ if (m_launcher.keepPatches()) {
+ dataDir.setPath(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation));
+ } else {
+ dataDir.setPath(QStandardPaths::writableLocation(QStandardPaths::TempLocation));
+ }
+
+ patchesDir.setPath(dataDir.absoluteFilePath("patches"));
+}
diff --git a/launcher/src/profile.cpp b/launcher/src/profile.cpp
index fc1220b..70a2edd 100644
--- a/launcher/src/profile.cpp
+++ b/launcher/src/profile.cpp
@@ -498,3 +498,17 @@ QString Profile::wineVersionText() const
return m_wineVersion;
}
}
+
+QString Profile::dalamudChannelName() const
+{
+ switch (dalamudChannel()) {
+ case DalamudChannel::Stable:
+ return QStringLiteral("stable");
+ case DalamudChannel::Staging:
+ return QStringLiteral("staging");
+ case DalamudChannel::Net5:
+ return QStringLiteral("net5");
+ }
+
+ Q_UNREACHABLE();
+}
diff --git a/launcher/src/profilemanager.cpp b/launcher/src/profilemanager.cpp
index 6bf1a9c..f5b6215 100644
--- a/launcher/src/profilemanager.cpp
+++ b/launcher/src/profilemanager.cpp
@@ -35,7 +35,6 @@ Profile *ProfileManager::addProfile()
newProfile->readWineInfo();
- newProfile->setGamePath(getDefaultGamePath());
newProfile->setWinePrefixPath(getDefaultWinePrefixPath());
insertProfile(newProfile);
@@ -68,7 +67,7 @@ QString ProfileManager::getDefaultWinePrefixPath()
return "";
}
-QString ProfileManager::getDefaultGamePath()
+QString ProfileManager::getDefaultGamePath(const QString &uuid)
{
#if defined(Q_OS_WIN)
return "C:\\Program Files (x86)\\SquareEnix\\FINAL FANTASY XIV - A Realm Reborn";
@@ -81,8 +80,9 @@ QString ProfileManager::getDefaultGamePath()
#endif
#if defined(Q_OS_LINUX)
- const QString appData = QStandardPaths::standardLocations(QStandardPaths::StandardLocation::AppDataLocation)[0];
- return QStringLiteral("%1/game").arg(appData);
+ const QDir appData = QStandardPaths::standardLocations(QStandardPaths::StandardLocation::AppDataLocation)[0];
+ const QDir gameDir = appData.absoluteFilePath("game");
+ return gameDir.absoluteFilePath(uuid);
#endif
}
diff --git a/launcher/src/utility.cpp b/launcher/src/utility.cpp
new file mode 100644
index 0000000..3fbd644
--- /dev/null
+++ b/launcher/src/utility.cpp
@@ -0,0 +1,20 @@
+#include "utility.h"
+
+#include
+
+QDir Utility::stateDirectory()
+{
+ if (qEnvironmentVariableIsSet("XDG_STATE_HOME")) {
+ return qEnvironmentVariable("XDG_STATE_HOME");
+ }
+
+ const QDir homeDir = QStandardPaths::standardLocations(QStandardPaths::HomeLocation)[0];
+ const QDir localDir = homeDir.absoluteFilePath(".local");
+ const QDir stateDir = localDir.absoluteFilePath("state");
+ return stateDir.absoluteFilePath("astra");
+}
+
+QString Utility::toWindowsPath(const QDir &dir)
+{
+ return "Z:" + dir.absolutePath().replace('/', '\\');
+}
diff --git a/launcher/ui/Settings/DeveloperSettings.qml b/launcher/ui/Settings/DeveloperSettings.qml
index 20ad127..59aa3d6 100644
--- a/launcher/ui/Settings/DeveloperSettings.qml
+++ b/launcher/ui/Settings/DeveloperSettings.qml
@@ -28,6 +28,8 @@ Kirigami.ScrollablePage {
MobileForm.FormCheckDelegate {
text: i18n("Keep Patches")
description: i18n("Do not delete patches after they're used. Astra will not redownload patch data, if found.")
+ checked: LauncherCore.keepPatches
+ onCheckedChanged: LauncherCore.keepPatches = checked
}
}
}