From 4948db82be2390bb8e17b6de458b453fb7bb5d63 Mon Sep 17 00:00:00 2001 From: Joshua Goins Date: Sun, 26 May 2024 08:02:56 -0400 Subject: [PATCH] Add a method to use existing game installations from other launchers This should work for XIVLauncher.Core, XIVQuickLauncher and the official launcher. More testing is needed of course, but the framework is there now. --- external/libphysis | 2 +- launcher/CMakeLists.txt | 2 + launcher/include/existinginstallmodel.h | 35 ++++++++++++ launcher/src/existinginstallmodel.cpp | 68 +++++++++++++++++++++++ launcher/ui/Setup/ExistingSetup.qml | 72 ++++++++++++++++--------- 5 files changed, 153 insertions(+), 26 deletions(-) create mode 100644 launcher/include/existinginstallmodel.h create mode 100644 launcher/src/existinginstallmodel.cpp diff --git a/external/libphysis b/external/libphysis index 07193fc..1b8a8dd 160000 --- a/external/libphysis +++ b/external/libphysis @@ -1 +1 @@ -Subproject commit 07193fc6e272a41c498112b3eb0e5808a6095e53 +Subproject commit 1b8a8dddcafe31799dfb5597ab41cc32ac3ec7a3 diff --git a/launcher/CMakeLists.txt b/launcher/CMakeLists.txt index b45195d..a49f226 100644 --- a/launcher/CMakeLists.txt +++ b/launcher/CMakeLists.txt @@ -47,6 +47,7 @@ target_sources(astra PRIVATE include/benchmarkinstaller.h include/compatibilitytoolinstaller.h include/encryptedarg.h + include/existinginstallmodel.h include/gamerunner.h include/gameinstaller.h include/headline.h @@ -68,6 +69,7 @@ target_sources(astra PRIVATE src/benchmarkinstaller.cpp src/compatibilitytoolinstaller.cpp src/encryptedarg.cpp + src/existinginstallmodel.cpp src/gamerunner.cpp src/headline.cpp src/gameinstaller.cpp diff --git a/launcher/include/existinginstallmodel.h b/launcher/include/existinginstallmodel.h new file mode 100644 index 0000000..b135d5f --- /dev/null +++ b/launcher/include/existinginstallmodel.h @@ -0,0 +1,35 @@ +// SPDX-FileCopyrightText: 2024 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#pragma once + +#include +#include + +class ExistingInstallModel : public QAbstractListModel +{ + Q_OBJECT + QML_ELEMENT + +public: + enum CustomRoles { + TypeRole = Qt::UserRole, + PathRole, + }; + + explicit ExistingInstallModel(QObject *parent = nullptr); + + QVariant data(const QModelIndex &index, int role) const override; + int rowCount(const QModelIndex &parent) const override; + QHash roleNames() const override; + +private: + void fill(); + + struct ExistingInstall { + ExistingInstallType type; + QString path; + }; + + QList m_existingInstalls; +}; diff --git a/launcher/src/existinginstallmodel.cpp b/launcher/src/existinginstallmodel.cpp new file mode 100644 index 0000000..557acca --- /dev/null +++ b/launcher/src/existinginstallmodel.cpp @@ -0,0 +1,68 @@ +// SPDX-FileCopyrightText: 2024 Joshua Goins +// SPDX-License-Identifier: GPL-3.0-or-later + +#include "existinginstallmodel.h" + +#include + +ExistingInstallModel::ExistingInstallModel(QObject *parent) + : QAbstractListModel(parent) +{ + fill(); +} + +QVariant ExistingInstallModel::data(const QModelIndex &index, int role) const +{ + Q_ASSERT(checkIndex(index, QAbstractItemModel::CheckIndexOption::IndexIsValid)); + + const auto &install = m_existingInstalls[index.row()]; + + switch (role) { + case TypeRole: { + switch (install.type) { + case ExistingInstallType::OfficialLauncher: + return i18n("Official Launcher"); + case ExistingInstallType::XIVLauncherCore: + return QStringLiteral("XIVLauncher.Core"); + case ExistingInstallType::XIVOnMac: + return QStringLiteral("XIV on Mac"); + case ExistingInstallType::XIVQuickLauncher: + return QStringLiteral("XIVQuickLauncher"); + default: + return i18n("Unknown"); + } + } + case PathRole: + return install.path; + default: + return {}; + } +} + +int ExistingInstallModel::rowCount(const QModelIndex &parent) const +{ + return parent.isValid() ? 0 : m_existingInstalls.size(); +} + +QHash ExistingInstallModel::roleNames() const +{ + return { + {TypeRole, "type"}, + {PathRole, "path"}, + }; +} + +void ExistingInstallModel::fill() +{ + auto dirs = physis_find_existing_game_dirs(); + for (int 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()); + m_existingInstalls.push_back(ExistingInstall{.type = dirs.entries[i].install_type, .path = QString::fromUtf8(dirs.entries[i].path)}); + endInsertRows(); + } + } +} + +#include "moc_existinginstallmodel.cpp" \ No newline at end of file diff --git a/launcher/ui/Setup/ExistingSetup.qml b/launcher/ui/Setup/ExistingSetup.qml index dfa65c8..725403a 100644 --- a/launcher/ui/Setup/ExistingSetup.qml +++ b/launcher/ui/Setup/ExistingSetup.qml @@ -19,30 +19,6 @@ FormCard.FormCardPage { title: i18n("Find Existing Installation") - FormCard.FormCard { - Layout.topMargin: Kirigami.Units.largeSpacing - Layout.fillWidth: true - - FormCard.FormTextDelegate { - id: helpTextDelegate - - text: i18n("Please select the path to your existing installation.") - } - - FormCard.FormDelegateSeparator { - above: helpTextDelegate - below: selectDelegate - } - - FormCard.FormButtonDelegate { - id: selectDelegate - - text: i18n("Select Existing Path") - icon.name: "document-open-folder" - onClicked: dialog.open() - } - } - data: FolderDialog { id: dialog @@ -51,4 +27,50 @@ FormCard.FormCardPage { applicationWindow().checkSetup(); } } -} \ No newline at end of file + + FormCard.FormCard { + Layout.fillWidth: true + Layout.topMargin: Kirigami.Units.largeSpacing + + Repeater { + model: ExistingInstallModel {} + + delegate: FormCard.FormButtonDelegate { + required property var path + required property var type + + text: path + description: type + + onClicked: { + page.profile.gamePath = path; + applicationWindow().checkSetup(); + } + } + } + } + + FormCard.FormCard { + Layout.fillWidth: true + Layout.topMargin: Kirigami.Units.largeSpacing + + FormCard.FormTextDelegate { + id: helpTextDelegate + + text: i18n("If you can't find your existing game installation, manually select the path below.") + } + FormCard.FormDelegateSeparator { + above: helpTextDelegate + below: selectDelegate + } + FormCard.FormButtonDelegate { + id: selectDelegate + + icon.name: "document-open-folder" + text: i18n("Select Existing Path") + + onClicked: dialog.open() + } + } +} +