mirror of
https://github.com/redstrate/Astra.git
synced 2025-04-22 12:47:44 +00:00
Misc code cleanup
Make more things const, auto and whatnot
This commit is contained in:
parent
1401f9e85e
commit
27e8169a0f
30 changed files with 120 additions and 141 deletions
|
@ -5,7 +5,7 @@
|
|||
|
||||
#include <QDir>
|
||||
#include <QObject>
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
#include <QtQml>
|
||||
#include <qcorotask.h>
|
||||
|
||||
#include "accountconfig.h"
|
||||
|
@ -104,13 +104,13 @@ private:
|
|||
void fetchAvatar();
|
||||
QCoro::Task<> fetchPassword();
|
||||
|
||||
/*
|
||||
* Sets a value in the keychain. This function is asynchronous.
|
||||
/**
|
||||
* @brief Sets a value in the keychain. This function is asynchronous.
|
||||
*/
|
||||
void setKeychainValue(const QString &key, const QString &value);
|
||||
|
||||
/*
|
||||
* Retrieves a value from the keychain. This function is synchronous.
|
||||
/**
|
||||
* @brief Retrieves a value from the keychain. This function is synchronous.
|
||||
*/
|
||||
QCoro::Task<QString> getKeychainValue(const QString &key);
|
||||
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
|
||||
#include "account.h"
|
||||
|
||||
|
@ -36,7 +35,7 @@ public:
|
|||
|
||||
[[nodiscard]] Account *getByUuid(const QString &uuid) const;
|
||||
|
||||
Q_INVOKABLE bool canDelete(Account *account) const;
|
||||
Q_INVOKABLE bool canDelete(const Account *account) const;
|
||||
Q_INVOKABLE void deleteAccount(Account *account);
|
||||
|
||||
Q_INVOKABLE [[nodiscard]] bool hasAnyAccounts() const;
|
||||
|
|
|
@ -3,9 +3,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QJsonArray>
|
||||
#include <QObject>
|
||||
#include <QTemporaryDir>
|
||||
#include <qcorotask.h>
|
||||
|
||||
#include "launchercore.h"
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
#include <QtQml>
|
||||
|
||||
class LauncherCore;
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <QtQml>
|
||||
|
||||
#include <physis.hpp>
|
||||
|
||||
class ExistingInstallModel : public QAbstractListModel
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
#include <QtQml>
|
||||
|
||||
class LauncherCore;
|
||||
class Profile;
|
||||
|
|
|
@ -16,7 +16,7 @@ public:
|
|||
explicit GameRunner(LauncherCore &launcher, QObject *parent = nullptr);
|
||||
|
||||
/// Begins the game executable, but calls to Dalamud if needed.
|
||||
void beginGameExecutable(Profile &settings, const std::optional<LoginAuth> &auth);
|
||||
void beginGameExecutable(Profile &profile, const std::optional<LoginAuth> &auth);
|
||||
|
||||
private:
|
||||
/// Starts a vanilla game session with no Dalamud injection.
|
||||
|
@ -26,10 +26,10 @@ private:
|
|||
void beginDalamudGame(const QString &gameExecutablePath, Profile &profile, const std::optional<LoginAuth> &auth);
|
||||
|
||||
/// Returns the game arguments needed to properly launch the game. This encrypts it too if needed, and it's already joined!
|
||||
QString getGameArgs(const Profile &profile, const std::optional<LoginAuth> &auth);
|
||||
QString getGameArgs(const Profile &profile, const std::optional<LoginAuth> &auth) const;
|
||||
|
||||
/// This wraps it in wine if needed.
|
||||
void launchExecutable(const Profile &settings, QProcess *process, const QStringList &args, bool isGame, bool needsRegistrySetup);
|
||||
void launchExecutable(const Profile &profile, QProcess *process, const QStringList &args, bool isGame, bool needsRegistrySetup);
|
||||
|
||||
/// Set a Wine registry key
|
||||
/// \param settings The profile that's being launched
|
||||
|
|
|
@ -3,9 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QFuture>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QProcess>
|
||||
#include <QtQml>
|
||||
#include <qcorotask.h>
|
||||
|
||||
|
@ -102,11 +100,11 @@ public:
|
|||
Q_INVOKABLE void refreshLogoImage();
|
||||
|
||||
[[nodiscard]] Profile *currentProfile() const;
|
||||
void setCurrentProfile(Profile *profile);
|
||||
void setCurrentProfile(const Profile *profile);
|
||||
|
||||
[[nodiscard]] QString autoLoginProfileName() const;
|
||||
[[nodiscard]] Profile *autoLoginProfile() const;
|
||||
void setAutoLoginProfile(Profile *value);
|
||||
void setAutoLoginProfile(const Profile *value);
|
||||
|
||||
// Networking misc.
|
||||
void buildRequest(const Profile &settings, QNetworkRequest &request);
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
#include <QtQml>
|
||||
|
||||
#include "config.h"
|
||||
#include "profile.h"
|
||||
|
|
|
@ -3,14 +3,16 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "patchlist.h"
|
||||
#include <QDir>
|
||||
#include <QMutex>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QString>
|
||||
#include <physis.hpp>
|
||||
#include <qcorotask.h>
|
||||
|
||||
#include <physis.hpp>
|
||||
|
||||
#include "patchlist.h"
|
||||
|
||||
class LauncherCore;
|
||||
|
||||
// General-purpose patcher routine. It opens a nice dialog box, handles downloading
|
||||
|
|
|
@ -3,9 +3,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QObject>
|
||||
#include <QtLogging>
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
#include <QtQml>
|
||||
|
||||
#include <physis.hpp>
|
||||
|
||||
class Account;
|
||||
|
@ -150,13 +149,13 @@ public:
|
|||
void setDalamudApplicable(bool applicable);
|
||||
|
||||
/// @return If Dalamud is enabled, and it's also applicable for the current game version.
|
||||
bool dalamudShouldLaunch() const;
|
||||
[[nodiscard]] bool dalamudShouldLaunch() const;
|
||||
|
||||
[[nodiscard]] QString compatibilityToolVersion() const;
|
||||
void setCompatibilityToolVersion(const QString &version);
|
||||
|
||||
BootData *bootData();
|
||||
GameData *gameData();
|
||||
BootData *bootData() const;
|
||||
GameData *gameData() const;
|
||||
|
||||
[[nodiscard]] bool loggedIn() const;
|
||||
void setLoggedIn(bool value);
|
||||
|
|
|
@ -3,8 +3,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QAbstractListModel>
|
||||
#include <QtQml/qqmlregistration.h>
|
||||
#include <QtQml>
|
||||
|
||||
#include "profile.h"
|
||||
|
||||
|
@ -41,7 +40,7 @@ public:
|
|||
[[nodiscard]] QList<Profile *> profiles() const;
|
||||
[[nodiscard]] int numProfiles() const;
|
||||
|
||||
Q_INVOKABLE bool canDelete(Profile *account) const;
|
||||
Q_INVOKABLE bool canDelete(const Profile *account) const;
|
||||
|
||||
[[nodiscard]] Q_INVOKABLE bool hasAnyExistingInstallations() const;
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
|
||||
#include "launchercore.h"
|
||||
|
||||
class SapphireLogin : QObject
|
||||
|
@ -13,6 +11,7 @@ public:
|
|||
explicit SapphireLogin(LauncherCore &window, QObject *parent = nullptr);
|
||||
|
||||
/// Begins the login process for Sapphire servers
|
||||
/// \param lobbyUrl The URL to the Sapphire lobby server
|
||||
/// \param info The required login information
|
||||
QCoro::Task<std::optional<LoginAuth>> login(const QString &lobbyUrl, const LoginInformation &info);
|
||||
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
|
||||
#include "account.h"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <cotp.h>
|
||||
|
@ -84,7 +83,7 @@ bool Account::isSapphire() const
|
|||
return m_config.isSapphire();
|
||||
}
|
||||
|
||||
void Account::setIsSapphire(bool value)
|
||||
void Account::setIsSapphire(const bool value)
|
||||
{
|
||||
if (m_config.isSapphire() != value) {
|
||||
m_config.setIsSapphire(value);
|
||||
|
@ -98,10 +97,10 @@ QString Account::lobbyUrl() const
|
|||
return m_config.lobbyUrl();
|
||||
}
|
||||
|
||||
void Account::setLobbyUrl(const QString &value)
|
||||
void Account::setLobbyUrl(const QString &url)
|
||||
{
|
||||
if (m_config.lobbyUrl() != value) {
|
||||
m_config.setLobbyUrl(value);
|
||||
if (m_config.lobbyUrl() != url) {
|
||||
m_config.setLobbyUrl(url);
|
||||
m_config.save();
|
||||
Q_EMIT lobbyUrlChanged();
|
||||
}
|
||||
|
@ -194,7 +193,7 @@ void Account::setPassword(const QString &password)
|
|||
|
||||
QString Account::getOTP()
|
||||
{
|
||||
auto otpSecret = QCoro::waitFor(getKeychainValue(QStringLiteral("otp-secret")));
|
||||
const auto otpSecret = QCoro::waitFor(getKeychainValue(QStringLiteral("otp-secret")));
|
||||
if (otpSecret.isEmpty()) {
|
||||
return {};
|
||||
}
|
||||
|
@ -246,12 +245,12 @@ void Account::fetchAvatar()
|
|||
url.setHost(QStringLiteral("na.%1").arg(m_launcher.settings()->mainServer())); // TODO: NA isnt the only thing in the world...
|
||||
url.setPath(QStringLiteral("/lodestone/character/%1").arg(lodestoneId()));
|
||||
|
||||
QNetworkRequest request(url);
|
||||
const QNetworkRequest request(url);
|
||||
Utility::printRequest(QStringLiteral("GET"), request);
|
||||
|
||||
const auto reply = m_launcher.mgr()->get(request);
|
||||
connect(reply, &QNetworkReply::finished, [this, filename, reply] {
|
||||
QString document = QString::fromUtf8(reply->readAll());
|
||||
const QString document = QString::fromUtf8(reply->readAll());
|
||||
if (!document.isEmpty()) {
|
||||
const static QRegularExpression re(
|
||||
QStringLiteral(R"lit(<div\s[^>]*class=["|']frame__chara__face["|'][^>]*>\s*<img\s[&>]*src=["|']([^"']*))lit"));
|
||||
|
@ -260,7 +259,7 @@ void Account::fetchAvatar()
|
|||
if (match.hasCaptured(1)) {
|
||||
const QString newAvatarUrl = match.captured(1);
|
||||
|
||||
const QNetworkRequest avatarRequest = QNetworkRequest(QUrl(newAvatarUrl));
|
||||
const auto avatarRequest = QNetworkRequest(QUrl(newAvatarUrl));
|
||||
Utility::printRequest(QStringLiteral("GET"), avatarRequest);
|
||||
|
||||
auto avatarReply = m_launcher.mgr()->get(avatarRequest);
|
||||
|
@ -331,9 +330,11 @@ QCoro::Task<QString> Account::getKeychainValue(const QString &key)
|
|||
}
|
||||
}
|
||||
|
||||
// ReSharper disable once CppMemberFunctionMayBeConst
|
||||
// ^ Could be const, but this function shouldn't be considered as such
|
||||
void Account::updateConfig()
|
||||
{
|
||||
auto configDir = getConfigDir().absoluteFilePath(QStringLiteral("FFXIV.cfg"));
|
||||
const auto configDir = getConfigDir().absoluteFilePath(QStringLiteral("FFXIV.cfg"));
|
||||
|
||||
if (!QFile::exists(configDir)) {
|
||||
return;
|
||||
|
@ -341,24 +342,24 @@ void Account::updateConfig()
|
|||
|
||||
qInfo(ASTRA_LOG) << "Updating FFXIV.cfg...";
|
||||
|
||||
auto configDirStd = configDir.toStdString();
|
||||
const auto configDirStd = configDir.toStdString();
|
||||
|
||||
auto cfgFileBuffer = physis_read_file(configDirStd.c_str());
|
||||
auto cfgFile = physis_cfg_parse(cfgFileBuffer);
|
||||
const auto cfgFileBuffer = physis_read_file(configDirStd.c_str());
|
||||
const auto cfgFile = physis_cfg_parse(cfgFileBuffer);
|
||||
|
||||
// Ensure that the opening cutscene movie never plays, since it's broken in most versions of Wine
|
||||
physis_cfg_set_value(cfgFile, "CutsceneMovieOpening", "1");
|
||||
|
||||
auto screenshotDir = m_launcher.settings()->screenshotDir();
|
||||
const auto screenshotDir = m_launcher.settings()->screenshotDir();
|
||||
Utility::createPathIfNeeded(screenshotDir);
|
||||
|
||||
auto screenshotDirWin = Utility::toWindowsPath(screenshotDir);
|
||||
auto screenshotDirWinStd = screenshotDirWin.toStdString();
|
||||
const auto screenshotDirWin = Utility::toWindowsPath(screenshotDir);
|
||||
const auto screenshotDirWinStd = screenshotDirWin.toStdString();
|
||||
|
||||
// Set the screenshot path
|
||||
physis_cfg_set_value(cfgFile, "ScreenShotDir", screenshotDirWinStd.c_str());
|
||||
|
||||
auto buffer = physis_cfg_write(cfgFile);
|
||||
const auto buffer = physis_cfg_write(cfgFile);
|
||||
|
||||
QFile file(configDir);
|
||||
file.open(QIODevice::WriteOnly);
|
||||
|
|
|
@ -22,7 +22,7 @@ void AccountManager::load()
|
|||
const QString uuid = QString(id).remove("account-"_L1);
|
||||
qInfo(ASTRA_LOG) << "Loading account" << uuid;
|
||||
|
||||
auto account = new Account(m_launcher, uuid, this);
|
||||
const auto account = new Account(m_launcher, uuid, this);
|
||||
m_accounts.append(account);
|
||||
Q_EMIT accountsChanged();
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ int AccountManager::rowCount(const QModelIndex &index) const
|
|||
return static_cast<int>(m_accounts.size());
|
||||
}
|
||||
|
||||
QVariant AccountManager::data(const QModelIndex &index, int role) const
|
||||
QVariant AccountManager::data(const QModelIndex &index, const int role) const
|
||||
{
|
||||
if (!checkIndex(index)) {
|
||||
return {};
|
||||
|
@ -54,9 +54,9 @@ QHash<int, QByteArray> AccountManager::roleNames() const
|
|||
return {{AccountRole, QByteArrayLiteral("account")}};
|
||||
}
|
||||
|
||||
Account *AccountManager::createSquareEnixAccount(const QString &username, int licenseType, bool isFreeTrial)
|
||||
Account *AccountManager::createSquareEnixAccount(const QString &username, const int licenseType, const bool isFreeTrial)
|
||||
{
|
||||
auto account = new Account(m_launcher, QUuid::createUuid().toString(), this);
|
||||
const auto account = new Account(m_launcher, QUuid::createUuid().toString(), this);
|
||||
account->setIsSapphire(false);
|
||||
account->setLicense(static_cast<Account::GameLicense>(licenseType));
|
||||
account->setIsFreeTrial(isFreeTrial);
|
||||
|
@ -69,7 +69,7 @@ Account *AccountManager::createSquareEnixAccount(const QString &username, int li
|
|||
|
||||
Account *AccountManager::createSapphireAccount(const QString &lobbyUrl, const QString &username)
|
||||
{
|
||||
auto account = new Account(m_launcher, QUuid::createUuid().toString(), this);
|
||||
const auto account = new Account(m_launcher, QUuid::createUuid().toString(), this);
|
||||
account->setIsSapphire(true);
|
||||
account->setName(username);
|
||||
account->setLobbyUrl(lobbyUrl);
|
||||
|
@ -90,7 +90,7 @@ Account *AccountManager::getByUuid(const QString &uuid) const
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
bool AccountManager::canDelete(Account *account) const
|
||||
bool AccountManager::canDelete(const Account *account) const
|
||||
{
|
||||
Q_UNUSED(account)
|
||||
return m_accounts.size() != 1;
|
||||
|
|
|
@ -207,7 +207,7 @@ QCoro::Task<bool> AssetUpdater::installCompatibilityTool()
|
|||
{
|
||||
Q_EMIT launcher.stageChanged(i18n("Updating compatibility tool..."));
|
||||
|
||||
const QNetworkRequest request = QNetworkRequest(QUrl(m_remoteCompatibilityToolUrl));
|
||||
const auto request = QNetworkRequest(QUrl(m_remoteCompatibilityToolUrl));
|
||||
Utility::printRequest(QStringLiteral("GET"), request);
|
||||
|
||||
const auto reply = launcher.mgr()->get(request);
|
||||
|
@ -233,8 +233,8 @@ QCoro::Task<bool> AssetUpdater::installCompatibilityTool()
|
|||
}
|
||||
|
||||
// the first directory is the same as the version we download
|
||||
const KArchiveDirectory *root = dynamic_cast<const KArchiveDirectory *>(archive.directory()->entry(m_remoteCompatibilityToolVersion));
|
||||
root->copyTo(m_wineDir.absolutePath(), true);
|
||||
const auto *root = dynamic_cast<const KArchiveDirectory *>(archive.directory()->entry(m_remoteCompatibilityToolVersion));
|
||||
Q_UNUSED(root->copyTo(m_wineDir.absolutePath(), true))
|
||||
|
||||
archive.close();
|
||||
|
||||
|
@ -249,7 +249,7 @@ QCoro::Task<bool> AssetUpdater::installDxvkTool()
|
|||
{
|
||||
Q_EMIT launcher.stageChanged(i18n("Updating DXVK..."));
|
||||
|
||||
const QNetworkRequest request = QNetworkRequest(QUrl(m_remoteDxvkToolUrl));
|
||||
const auto request = QNetworkRequest(QUrl(m_remoteDxvkToolUrl));
|
||||
Utility::printRequest(QStringLiteral("GET"), request);
|
||||
|
||||
const auto reply = launcher.mgr()->get(request);
|
||||
|
@ -275,8 +275,8 @@ QCoro::Task<bool> AssetUpdater::installDxvkTool()
|
|||
}
|
||||
|
||||
// the first directory is the same as the version we download
|
||||
const KArchiveDirectory *root = dynamic_cast<const KArchiveDirectory *>(archive.directory()->entry(m_remoteDxvkToolVersion));
|
||||
root->copyTo(m_dxvkDir.absolutePath(), true);
|
||||
const auto *root = dynamic_cast<const KArchiveDirectory *>(archive.directory()->entry(m_remoteDxvkToolVersion));
|
||||
Q_UNUSED(root->copyTo(m_dxvkDir.absolutePath(), true))
|
||||
|
||||
archive.close();
|
||||
|
||||
|
@ -289,7 +289,7 @@ QCoro::Task<bool> AssetUpdater::installDalamudAssets()
|
|||
{
|
||||
Q_EMIT launcher.stageChanged(i18n("Updating Dalamud assets..."));
|
||||
|
||||
const QNetworkRequest request = QNetworkRequest(QUrl(m_remoteDalamudAssetPackageUrl));
|
||||
const auto request = QNetworkRequest(QUrl(m_remoteDalamudAssetPackageUrl));
|
||||
Utility::printRequest(QStringLiteral("GET"), request);
|
||||
|
||||
const auto reply = launcher.mgr()->get(request);
|
||||
|
@ -321,7 +321,7 @@ QCoro::Task<bool> AssetUpdater::installDalamud()
|
|||
{
|
||||
Q_EMIT launcher.stageChanged(i18n("Updating Dalamud..."));
|
||||
|
||||
const QNetworkRequest request = QNetworkRequest(QUrl(m_remoteDalamudDownloadUrl));
|
||||
const auto request = QNetworkRequest(QUrl(m_remoteDalamudDownloadUrl));
|
||||
Utility::printRequest(QStringLiteral("GET"), request);
|
||||
|
||||
const auto reply = launcher.mgr()->get(request);
|
||||
|
|
|
@ -6,9 +6,6 @@
|
|||
#include <KArchiveDirectory>
|
||||
#include <KLocalizedString>
|
||||
#include <KZip>
|
||||
#include <QFile>
|
||||
#include <QNetworkReply>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "astra_log.h"
|
||||
#include "launchercore.h"
|
||||
|
@ -16,7 +13,7 @@
|
|||
#include "utility.h"
|
||||
|
||||
// TODO: this should be dynamically grabbed from the webpage to avoid hardcoding it
|
||||
const QString installerUrl = QStringLiteral("https://download.finalfantasyxiv.com/s9qmq6SJfMMqYM4o/ffxiv-dawntrail-bench.zip");
|
||||
const auto installerUrl = QStringLiteral("https://download.finalfantasyxiv.com/s9qmq6SJfMMqYM4o/ffxiv-dawntrail-bench.zip");
|
||||
|
||||
BenchmarkInstaller::BenchmarkInstaller(LauncherCore &launcher, Profile &profile, QObject *parent)
|
||||
: QObject(parent)
|
||||
|
@ -34,7 +31,7 @@ BenchmarkInstaller::BenchmarkInstaller(LauncherCore &launcher, Profile &profile,
|
|||
void BenchmarkInstaller::start()
|
||||
{
|
||||
if (m_localInstallerPath.isEmpty()) {
|
||||
const QNetworkRequest request = QNetworkRequest(QUrl(installerUrl));
|
||||
const auto request = QNetworkRequest(QUrl(installerUrl));
|
||||
Utility::printRequest(QStringLiteral("GET"), request);
|
||||
|
||||
// TODO: benchmarks are usually quite large, and need download progress reporting
|
||||
|
@ -76,7 +73,7 @@ void BenchmarkInstaller::installGame()
|
|||
|
||||
// the first directory is the same as the version we download
|
||||
const KArchiveDirectory *root = archive.directory();
|
||||
root->copyTo(installDirectory.absolutePath(), true);
|
||||
Q_UNUSED(root->copyTo(installDirectory.absolutePath(), true))
|
||||
|
||||
archive.close();
|
||||
|
||||
|
|
|
@ -29,10 +29,10 @@ void CompatibilityToolInstaller::installCompatibilityTool()
|
|||
if (astraToolDir.exists()) {
|
||||
Q_EMIT error(i18n("The compatibility tool is already installed."));
|
||||
return;
|
||||
} else {
|
||||
QDir().mkpath(astraToolDir.absolutePath());
|
||||
}
|
||||
|
||||
Q_UNUSED(QDir().mkpath(astraToolDir.absolutePath()))
|
||||
|
||||
QString command;
|
||||
if (KSandbox::isFlatpak()) {
|
||||
QFile::copy(QStringLiteral("/app/bin/steamwrap"), astraToolDir.absoluteFilePath(QStringLiteral("steamwrap")));
|
||||
|
@ -62,7 +62,7 @@ void CompatibilityToolInstaller::installCompatibilityTool()
|
|||
toolManifestFile.write(toolManifestContents.toUtf8());
|
||||
toolManifestFile.close();
|
||||
|
||||
const QString compatibilityToolContents = QStringLiteral(
|
||||
const auto compatibilityToolContents = QStringLiteral(
|
||||
"\"compatibilitytools\"\n"
|
||||
"{\n"
|
||||
" \"compat_tools\"\n"
|
||||
|
|
|
@ -53,7 +53,7 @@ static char ChecksumTable[] = {'f', 'X', '1', 'p', 'G', 't', 'd', 'S', '5', 'C',
|
|||
|
||||
inline char GetChecksum(const unsigned int key)
|
||||
{
|
||||
auto value = key & 0x000F0000;
|
||||
const auto value = key & 0x000F0000;
|
||||
return ChecksumTable[value >> 16];
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ ExistingInstallModel::ExistingInstallModel(QObject *parent)
|
|||
fill();
|
||||
}
|
||||
|
||||
QVariant ExistingInstallModel::data(const QModelIndex &index, int role) const
|
||||
QVariant ExistingInstallModel::data(const QModelIndex &index, const int role) const
|
||||
{
|
||||
Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid));
|
||||
|
||||
|
@ -54,8 +54,8 @@ QHash<int, QByteArray> ExistingInstallModel::roleNames() const
|
|||
|
||||
void ExistingInstallModel::fill()
|
||||
{
|
||||
auto dirs = physis_find_existing_game_dirs();
|
||||
for (int i = 0; i < dirs.count; i++) {
|
||||
const auto dirs = physis_find_existing_game_dirs();
|
||||
for (uint32_t i = 0; i < dirs.count; i++) {
|
||||
// We shouldn't be able to import our own game installs, that's handled elsewhere in the UI
|
||||
if (dirs.entries[i].install_type != ExistingInstallType::Astra) {
|
||||
beginInsertRows({}, m_existingInstalls.size(), m_existingInstalls.size());
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
#include "profile.h"
|
||||
#include "utility.h"
|
||||
|
||||
const QString installerUrl = QStringLiteral("https://download.finalfantasyxiv.com/inst/ffxivsetup.exe");
|
||||
const auto installerUrl = QStringLiteral("https://download.finalfantasyxiv.com/inst/ffxivsetup.exe");
|
||||
const QByteArray installerSha256 = QByteArray::fromHex("cf70bfaaf4f429794358ef84acbcbdc4193bee109fa1b6aea81bd4de038e500e");
|
||||
|
||||
GameInstaller::GameInstaller(LauncherCore &launcher, Profile &profile, QObject *parent)
|
||||
|
@ -33,7 +33,7 @@ GameInstaller::GameInstaller(LauncherCore &launcher, Profile &profile, const QSt
|
|||
void GameInstaller::start()
|
||||
{
|
||||
if (m_localInstallerPath.isEmpty()) {
|
||||
const QNetworkRequest request = QNetworkRequest(QUrl(installerUrl));
|
||||
const auto request = QNetworkRequest(QUrl(installerUrl));
|
||||
Utility::printRequest(QStringLiteral("GET"), request);
|
||||
|
||||
auto reply = m_launcher.mgr()->get(request);
|
||||
|
|
|
@ -42,9 +42,9 @@ void GameRunner::beginVanillaGame(const QString &gameExecutablePath, Profile &pr
|
|||
{
|
||||
profile.setLoggedIn(true);
|
||||
|
||||
auto gameProcess = new QProcess(this);
|
||||
const auto gameProcess = new QProcess(this);
|
||||
gameProcess->setProcessEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
connect(gameProcess, &QProcess::finished, this, [this, &profile](int exitCode) {
|
||||
connect(gameProcess, &QProcess::finished, this, [this, &profile](const int exitCode) {
|
||||
profile.setLoggedIn(false);
|
||||
Q_UNUSED(exitCode)
|
||||
Q_EMIT m_launcher.gameClosed();
|
||||
|
@ -85,8 +85,8 @@ void GameRunner::beginDalamudGame(const QString &gameExecutablePath, Profile &pr
|
|||
const QDir dalamudInstallDir = dalamudDir.absoluteFilePath(profile.dalamudChannelName());
|
||||
const QString dalamudInjector = dalamudInstallDir.absoluteFilePath(QStringLiteral("Dalamud.Injector.exe"));
|
||||
|
||||
auto dalamudProcess = new QProcess(this);
|
||||
connect(dalamudProcess, &QProcess::finished, this, [this, &profile](int exitCode) {
|
||||
const auto dalamudProcess = new QProcess(this);
|
||||
connect(dalamudProcess, &QProcess::finished, this, [this, &profile](const int exitCode) {
|
||||
profile.setLoggedIn(false);
|
||||
Q_UNUSED(exitCode)
|
||||
Q_EMIT m_launcher.gameClosed();
|
||||
|
@ -123,7 +123,7 @@ void GameRunner::beginDalamudGame(const QString &gameExecutablePath, Profile &pr
|
|||
true);
|
||||
}
|
||||
|
||||
QString GameRunner::getGameArgs(const Profile &profile, const std::optional<LoginAuth> &auth)
|
||||
QString GameRunner::getGameArgs(const Profile &profile, const std::optional<LoginAuth> &auth) const
|
||||
{
|
||||
QList<std::pair<QString, QString>> gameArgs;
|
||||
|
||||
|
@ -333,7 +333,7 @@ void GameRunner::launchExecutable(const Profile &profile, QProcess *process, con
|
|||
|
||||
void GameRunner::addRegistryKey(const Profile &settings, const QString &key, const QString &value, const QString &data)
|
||||
{
|
||||
auto process = new QProcess(this);
|
||||
const auto process = new QProcess(this);
|
||||
process->setProcessEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
launchExecutable(settings,
|
||||
process,
|
||||
|
@ -345,7 +345,7 @@ void GameRunner::addRegistryKey(const Profile &settings, const QString &key, con
|
|||
|
||||
void GameRunner::setWindowsVersion(const Profile &settings, const QString &version)
|
||||
{
|
||||
auto process = new QProcess(this);
|
||||
const auto process = new QProcess(this);
|
||||
process->setProcessEnvironment(QProcessEnvironment::systemEnvironment());
|
||||
launchExecutable(settings, process, {QStringLiteral("winecfg"), QStringLiteral("/v"), version}, false, false);
|
||||
process->waitForFinished();
|
||||
|
|
|
@ -39,14 +39,14 @@ LauncherCore::LauncherCore()
|
|||
m_accountManager->load();
|
||||
|
||||
// restore profile -> account connections
|
||||
for (auto profile : m_profileManager->profiles()) {
|
||||
if (auto account = m_accountManager->getByUuid(profile->accountUuid())) {
|
||||
for (const auto profile : m_profileManager->profiles()) {
|
||||
if (const auto account = m_accountManager->getByUuid(profile->accountUuid())) {
|
||||
profile->setAccount(account);
|
||||
}
|
||||
}
|
||||
|
||||
// set default profile, if found
|
||||
if (auto profile = m_profileManager->getProfileByUUID(m_settings->currentProfile())) {
|
||||
if (const auto profile = m_profileManager->getProfileByUUID(m_settings->currentProfile())) {
|
||||
setCurrentProfile(profile);
|
||||
}
|
||||
|
||||
|
@ -64,7 +64,7 @@ void LauncherCore::login(Profile *profile, const QString &username, const QStrin
|
|||
{
|
||||
Q_ASSERT(profile != nullptr);
|
||||
|
||||
auto loginInformation = new LoginInformation(this);
|
||||
const auto loginInformation = new LoginInformation(this);
|
||||
loginInformation->profile = profile;
|
||||
|
||||
// Benchmark never has to login, of course
|
||||
|
@ -162,7 +162,7 @@ void LauncherCore::refreshLogoImage()
|
|||
const QDir logoDir = cacheDir.absoluteFilePath(QStringLiteral("logos"));
|
||||
|
||||
if (!logoDir.exists()) {
|
||||
QDir().mkpath(logoDir.absolutePath());
|
||||
Q_UNUSED(QDir().mkpath(logoDir.absolutePath()))
|
||||
}
|
||||
|
||||
const auto saveTexture = [](GameData *data, const QString &path, const QString &name) {
|
||||
|
@ -170,18 +170,18 @@ void LauncherCore::refreshLogoImage()
|
|||
return;
|
||||
}
|
||||
|
||||
auto file = physis_gamedata_extract_file(data, path.toStdString().c_str());
|
||||
const auto file = physis_gamedata_extract_file(data, path.toStdString().c_str());
|
||||
if (file.data != nullptr) {
|
||||
auto tex = physis_texture_parse(file);
|
||||
const auto tex = physis_texture_parse(file);
|
||||
|
||||
QImage image(tex.rgba, tex.width, tex.height, QImage::Format_RGBA8888);
|
||||
image.save(name);
|
||||
const QImage image(tex.rgba, tex.width, tex.height, QImage::Format_RGBA8888);
|
||||
Q_UNUSED(image.save(name))
|
||||
}
|
||||
};
|
||||
|
||||
// TODO: this finds the first profile that has a valid image, but this could probably be cached per-profile
|
||||
for (int i = 0; i < m_profileManager->numProfiles(); i++) {
|
||||
auto profile = m_profileManager->getProfile(i);
|
||||
const auto profile = m_profileManager->getProfile(i);
|
||||
if (profile->isGameInstalled() && profile->gameData()) {
|
||||
// A Realm Reborn
|
||||
saveTexture(profile->gameData(), QStringLiteral("ui/uld/Title_Logo.tex"), logoDir.absoluteFilePath(QStringLiteral("ffxiv.png")));
|
||||
|
@ -220,7 +220,7 @@ Profile *LauncherCore::currentProfile() const
|
|||
return m_profileManager->getProfile(m_currentProfileIndex);
|
||||
}
|
||||
|
||||
void LauncherCore::setCurrentProfile(Profile *profile)
|
||||
void LauncherCore::setCurrentProfile(const Profile *profile)
|
||||
{
|
||||
Q_ASSERT(profile != nullptr);
|
||||
|
||||
|
@ -246,7 +246,7 @@ void LauncherCore::setCurrentProfile(Profile *profile)
|
|||
return m_profileManager->getProfileByUUID(m_settings->config()->autoLoginProfile());
|
||||
}
|
||||
|
||||
void LauncherCore::setAutoLoginProfile(Profile *profile)
|
||||
void LauncherCore::setAutoLoginProfile(const Profile *profile)
|
||||
{
|
||||
if (profile != nullptr) {
|
||||
auto uuid = profile->uuid();
|
||||
|
@ -370,7 +370,7 @@ QCoro::Task<> LauncherCore::beginLogin(LoginInformation &info)
|
|||
}
|
||||
}
|
||||
|
||||
auto assetUpdater = new AssetUpdater(*info.profile, *this, this);
|
||||
const auto assetUpdater = new AssetUpdater(*info.profile, *this, this);
|
||||
if (co_await assetUpdater->update()) {
|
||||
// If we expect an auth ticket, don't continue if missing
|
||||
if (!info.profile->isBenchmark() && auth == std::nullopt) {
|
||||
|
@ -435,10 +435,10 @@ QCoro::Task<> LauncherCore::fetchNews()
|
|||
auto bannerReply = mgr()->get(bannerRequest);
|
||||
co_await bannerReply;
|
||||
|
||||
auto document = QJsonDocument::fromJson(headlineReply->readAll());
|
||||
auto bannerDocument = QJsonDocument::fromJson(bannerReply->readAll());
|
||||
const auto document = QJsonDocument::fromJson(headlineReply->readAll());
|
||||
const auto bannerDocument = QJsonDocument::fromJson(bannerReply->readAll());
|
||||
|
||||
auto headline = new Headline(this);
|
||||
const auto headline = new Headline(this);
|
||||
if (document.isEmpty() || bannerDocument.isEmpty()) {
|
||||
headline->failedToLoad = true;
|
||||
} else {
|
||||
|
|
|
@ -88,7 +88,7 @@ private:
|
|||
|
||||
Q_GLOBAL_STATIC(Logger, logger)
|
||||
|
||||
void handler(QtMsgType type, const QMessageLogContext &context, const QString &message)
|
||||
void handler(const QtMsgType type, const QMessageLogContext &context, const QString &message)
|
||||
{
|
||||
switch (type) {
|
||||
case QtDebugMsg:
|
||||
|
|
|
@ -4,9 +4,6 @@
|
|||
#include <KAboutData>
|
||||
#include <KLocalizedContext>
|
||||
#include <KLocalizedString>
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QMessageBox>
|
||||
#include <QQuickStyle>
|
||||
#include <kdsingleapplication.h>
|
||||
#include <qcoroqml.h>
|
||||
|
@ -16,12 +13,9 @@
|
|||
#endif
|
||||
|
||||
#include "astra-version.h"
|
||||
#include "compatibilitytoolinstaller.h"
|
||||
#include "gameinstaller.h"
|
||||
#include "launchercore.h"
|
||||
#include "logger.h"
|
||||
#include "physis_logger.h"
|
||||
#include "sapphirelogin.h"
|
||||
|
||||
#ifdef Q_OS_WIN
|
||||
#include <BreezeIcons/BreezeIcons>
|
||||
|
@ -45,9 +39,9 @@ int main(int argc, char *argv[])
|
|||
qputenv("QT_QUICK_CONTROLS_MOBILE", "1");
|
||||
}
|
||||
|
||||
QGuiApplication app(argc, argv);
|
||||
const QGuiApplication app(argc, argv);
|
||||
|
||||
KDSingleApplication singleApplication;
|
||||
const KDSingleApplication singleApplication;
|
||||
if (!singleApplication.isPrimaryInstance()) {
|
||||
return 0;
|
||||
}
|
||||
|
@ -153,7 +147,7 @@ int main(int argc, char *argv[])
|
|||
|
||||
QQmlApplicationEngine engine;
|
||||
|
||||
auto core = engine.singletonInstance<LauncherCore *>(QStringLiteral("zone.xiv.astra"), QStringLiteral("LauncherCore"));
|
||||
const auto core = engine.singletonInstance<LauncherCore *>(QStringLiteral("zone.xiv.astra"), QStringLiteral("LauncherCore"));
|
||||
if (parser.isSet(steamOption)) {
|
||||
core->initializeSteam();
|
||||
}
|
||||
|
|
|
@ -6,9 +6,7 @@
|
|||
#include <KLocalizedString>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
#include <QStandardPaths>
|
||||
#include <QtConcurrent>
|
||||
#include <physis.hpp>
|
||||
#include <qcorofuture.h>
|
||||
|
@ -110,7 +108,7 @@ QCoro::Task<bool> Patcher::patch(const PatchList &patchList)
|
|||
|
||||
auto patchReply = m_launcher.mgr()->get(patchRequest);
|
||||
|
||||
connect(patchReply, &QNetworkReply::downloadProgress, this, [this, ourIndex](int received, int total) {
|
||||
connect(patchReply, &QNetworkReply::downloadProgress, this, [this, ourIndex](const int received, const int total) {
|
||||
Q_UNUSED(total)
|
||||
updateDownloadProgress(ourIndex, received);
|
||||
});
|
||||
|
@ -256,7 +254,7 @@ QString Patcher::getBaseString() const
|
|||
}
|
||||
}
|
||||
|
||||
void Patcher::updateDownloadProgress(const int index, int received)
|
||||
void Patcher::updateDownloadProgress(const int index, const int received)
|
||||
{
|
||||
QMutexLocker locker(&m_finishedPatchesMutex);
|
||||
|
||||
|
@ -275,7 +273,7 @@ void Patcher::updateMessage()
|
|||
repositoryName = QStringLiteral("ffxiv");
|
||||
}
|
||||
|
||||
const float progress = ((float)patch.bytesDownloaded / (float)patch.length) * 100.0f;
|
||||
const float progress = (static_cast<float>(patch.bytesDownloaded) / static_cast<float>(patch.length)) * 100.0f;
|
||||
const QString progressStr = QStringLiteral("%1").arg(progress, 1, 'f', 1, QLatin1Char('0'));
|
||||
|
||||
Q_EMIT m_launcher.stageChanged(i18n("Downloading %1 - %2 [%3/%4]", repositoryName, patch.version, m_finishedPatches, m_remainingPatches),
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "processlogger.h"
|
||||
#include "astra_log.h"
|
||||
#include "utility.h"
|
||||
|
||||
#include <QStandardPaths>
|
||||
|
||||
ProcessLogger::ProcessLogger(QProcess *process)
|
||||
|
|
|
@ -52,7 +52,7 @@ void Profile::readDalamudInfo()
|
|||
if (QFile::exists(dalamudDepsJson)) {
|
||||
QFile depsJson(dalamudDepsJson);
|
||||
depsJson.open(QFile::ReadOnly);
|
||||
QJsonDocument doc = QJsonDocument::fromJson(depsJson.readAll());
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(depsJson.readAll());
|
||||
|
||||
QString versionString;
|
||||
for (const auto &target : doc["targets"_L1].toObject().keys()) {
|
||||
|
@ -85,10 +85,10 @@ void Profile::readGameData()
|
|||
return;
|
||||
}
|
||||
|
||||
auto header = physis_gamedata_extract_file(m_gameData, "exd/exversion.exh");
|
||||
const auto header = physis_gamedata_extract_file(m_gameData, "exd/exversion.exh");
|
||||
physis_EXH *exh = physis_parse_excel_sheet_header(header);
|
||||
if (exh != nullptr) {
|
||||
physis_EXD exd = physis_gamedata_read_excel_sheet(m_gameData, "ExVersion", exh, Language::English, 0);
|
||||
const physis_EXD exd = physis_gamedata_read_excel_sheet(m_gameData, "ExVersion", exh, Language::English, 0);
|
||||
|
||||
for (unsigned int i = 0; i < exd.row_count; i++) {
|
||||
m_expansionNames.push_back(QString::fromLatin1(exd.row_data[i].column_data[0].string._0));
|
||||
|
@ -334,10 +334,10 @@ Profile::DalamudChannel Profile::dalamudChannel() const
|
|||
return static_cast<DalamudChannel>(m_config->dalamudChannel());
|
||||
}
|
||||
|
||||
void Profile::setDalamudChannel(const DalamudChannel value)
|
||||
void Profile::setDalamudChannel(const DalamudChannel channel)
|
||||
{
|
||||
if (static_cast<DalamudChannel>(m_config->dalamudChannel()) != value) {
|
||||
m_config->setDalamudChannel(static_cast<int>(value));
|
||||
if (static_cast<DalamudChannel>(m_config->dalamudChannel()) != channel) {
|
||||
m_config->setDalamudChannel(static_cast<int>(channel));
|
||||
m_config->save();
|
||||
Q_EMIT dalamudChannelChanged();
|
||||
}
|
||||
|
@ -376,7 +376,7 @@ bool Profile::isBenchmark() const
|
|||
return m_config->isBenchmark();
|
||||
}
|
||||
|
||||
void Profile::setIsBenchmark(bool value)
|
||||
void Profile::setIsBenchmark(const bool value)
|
||||
{
|
||||
if (m_config->isBenchmark() != value) {
|
||||
m_config->setIsBenchmark(value);
|
||||
|
@ -421,7 +421,7 @@ void Profile::readGameVersion()
|
|||
}
|
||||
|
||||
// Extract frontier url if possible
|
||||
const QString launcherPath = QString(gamePath() + QStringLiteral("/boot/ffxivlauncher64.exe"));
|
||||
const auto launcherPath = QString(gamePath() + QStringLiteral("/boot/ffxivlauncher64.exe"));
|
||||
m_frontierUrl = QString::fromUtf8(physis_extract_frontier_url(launcherPath.toStdString().c_str()));
|
||||
|
||||
Q_EMIT gameInstallChanged();
|
||||
|
@ -552,7 +552,7 @@ int Profile::dalamudAssetVersion() const
|
|||
return m_dalamudAssetVersion;
|
||||
}
|
||||
|
||||
void Profile::setDalamudAssetVersion(int version)
|
||||
void Profile::setDalamudAssetVersion(const int version)
|
||||
{
|
||||
m_dalamudAssetVersion = version;
|
||||
}
|
||||
|
@ -572,7 +572,7 @@ void Profile::setDalamudVersion(const QString &version)
|
|||
m_dalamudVersion = version;
|
||||
}
|
||||
|
||||
void Profile::setDalamudApplicable(bool applicable)
|
||||
void Profile::setDalamudApplicable(const bool applicable)
|
||||
{
|
||||
m_dalamudApplicable = applicable;
|
||||
}
|
||||
|
@ -593,12 +593,12 @@ void Profile::setCompatibilityToolVersion(const QString &version)
|
|||
m_compatibilityToolVersion = version;
|
||||
}
|
||||
|
||||
BootData *Profile::bootData()
|
||||
BootData *Profile::bootData() const
|
||||
{
|
||||
return m_bootData;
|
||||
}
|
||||
|
||||
GameData *Profile::gameData()
|
||||
GameData *Profile::gameData() const
|
||||
{
|
||||
return m_gameData;
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ int ProfileManager::getProfileIndex(const QString &name)
|
|||
|
||||
Profile *ProfileManager::getProfileByUUID(const QString &uuid)
|
||||
{
|
||||
for (auto &m_profile : m_profiles) {
|
||||
for (const auto &m_profile : m_profiles) {
|
||||
if (m_profile->uuid() == uuid)
|
||||
return m_profile;
|
||||
}
|
||||
|
@ -43,7 +43,7 @@ Profile *ProfileManager::getProfileByUUID(const QString &uuid)
|
|||
|
||||
Profile *ProfileManager::addProfile()
|
||||
{
|
||||
auto newProfile = new Profile(m_launcher, QUuid::createUuid().toString(), this);
|
||||
const auto newProfile = new Profile(m_launcher, QUuid::createUuid().toString(), this);
|
||||
newProfile->setName(QStringLiteral("New Profile"));
|
||||
|
||||
insertProfile(newProfile);
|
||||
|
@ -85,7 +85,7 @@ void ProfileManager::load()
|
|||
if (id.contains("profile-"_L1)) {
|
||||
const QString uuid = QString(id).remove("profile-"_L1);
|
||||
qInfo(ASTRA_LOG) << "Loading profile" << uuid;
|
||||
auto profile = new Profile(m_launcher, uuid, this);
|
||||
const auto profile = new Profile(m_launcher, uuid, this);
|
||||
insertProfile(profile);
|
||||
}
|
||||
}
|
||||
|
@ -102,7 +102,7 @@ int ProfileManager::rowCount(const QModelIndex &index) const
|
|||
return static_cast<int>(m_profiles.size());
|
||||
}
|
||||
|
||||
QVariant ProfileManager::data(const QModelIndex &index, int role) const
|
||||
QVariant ProfileManager::data(const QModelIndex &index, const int role) const
|
||||
{
|
||||
if (!checkIndex(index)) {
|
||||
return {};
|
||||
|
@ -134,7 +134,7 @@ QList<Profile *> ProfileManager::profiles() const
|
|||
return m_profiles;
|
||||
}
|
||||
|
||||
bool ProfileManager::canDelete(Profile *account) const
|
||||
bool ProfileManager::canDelete(const Profile *account) const
|
||||
{
|
||||
Q_UNUSED(account)
|
||||
return m_profiles.size() != 1;
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
#include "astra_http_log.h"
|
||||
|
||||
#include <QSslConfiguration>
|
||||
#include <QStandardPaths>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
|
@ -22,7 +21,7 @@ void Utility::printRequest(const QString &type, const QNetworkRequest &request)
|
|||
void Utility::createPathIfNeeded(const QDir &dir)
|
||||
{
|
||||
if (!QDir().exists(dir.absolutePath())) {
|
||||
QDir().mkpath(dir.absolutePath());
|
||||
Q_UNUSED(QDir().mkpath(dir.absolutePath()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue