From b568418891a76a9d75bc860a6121e2ef8e56fc06 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Fri, 14 Oct 2022 22:45:24 +0200 Subject: [PATCH] GlosSIConfig: Find and launch EGS games --- GlosSIConfig/GlosSIConfig.vcxproj | 1 + GlosSIConfig/GlosSIConfig.vcxproj.filters | 3 + GlosSIConfig/Resource.rc | 168 +++++++++++++++- GlosSIConfig/UIModel.cpp | 25 +++ GlosSIConfig/UIModel.h | 5 + GlosSIConfig/qml.qrc | 1 + GlosSIConfig/qml/AddSelectTypeDialog.qml | 11 +- GlosSIConfig/qml/EGSSelectDialog.qml | 230 ++++++++++++++++++++++ GlosSIConfig/qml/ShortcutProps.qml | 16 ++ GlosSIConfig/qml/main.qml | 3 + 10 files changed, 458 insertions(+), 5 deletions(-) create mode 100644 GlosSIConfig/qml/EGSSelectDialog.qml diff --git a/GlosSIConfig/GlosSIConfig.vcxproj b/GlosSIConfig/GlosSIConfig.vcxproj index 0ba8142..b649a7b 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj +++ b/GlosSIConfig/GlosSIConfig.vcxproj @@ -143,6 +143,7 @@ + diff --git a/GlosSIConfig/GlosSIConfig.vcxproj.filters b/GlosSIConfig/GlosSIConfig.vcxproj.filters index f7483fd..08d81b8 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj.filters +++ b/GlosSIConfig/GlosSIConfig.vcxproj.filters @@ -80,6 +80,9 @@ qml + + qml + diff --git a/GlosSIConfig/Resource.rc b/GlosSIConfig/Resource.rc index 9f6cc17..73c56a6 100644 --- a/GlosSIConfig/Resource.rc +++ b/GlosSIConfig/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,0,9,1037005000102 - PRODUCTVERSION 0,0,9,1037005000102 + FILEVERSION 0,1,0,2033009290000 + PRODUCTVERSION 0,1,0,2033009290000 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - Config" - VALUE "FileVersion", "0.0.9.1-37-g5ebe102" + VALUE "FileVersion", "0.1.0.2-33-g929abfe" VALUE "InternalName", "GlosSIConfig" VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSIConfig.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.0.9.1-37-g5ebe102" + VALUE "ProductVersion", "0.1.0.2-33-g929abfe" END END BLOCK "VarFileInfo" @@ -1092,6 +1092,166 @@ IDI_ICON1 ICON "..\GlosSI_Icon.ico" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index 24e08f3..f4ba80a 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -416,6 +416,31 @@ void UIModel::saveDefaultConf(QVariantMap conf) const QVariantList UIModel::uwpApps() { return UWPFetch::UWPAppList(); } #endif +QVariantList UIModel::egsGamesList() const +{ + wchar_t* program_data_path_str; + std::filesystem::path path; + if (SHGetKnownFolderPath(FOLDERID_ProgramData, KF_FLAG_CREATE, NULL, &program_data_path_str) != S_OK) { + qDebug() << "Couldn't get ProgramDataPath"; + return {{"InstallLocation", "Error"}}; + } + path = std::filesystem::path(program_data_path_str); + path /= egs_games_json_path_; + + QFile file(path); + if (file.open(QIODevice::ReadOnly)) { + const auto data = file.readAll(); + file.close(); + auto json = QJsonDocument::fromJson(data).object(); + if (json["InstallationList"].isArray()) { + return json["InstallationList"].toVariant().toList(); + } + qDebug() << "InstallationList does not exist!"; + } + qDebug() << "Couldn't read EGS LauncherInstalled.dat " << path; + return {{"InstallLocation", "Error"}}; +} + bool UIModel::writeShortcutsVDF(const std::wstring& mode, const std::wstring& name, const std::wstring& shortcutspath, bool is_admin_try) const { diff --git a/GlosSIConfig/UIModel.h b/GlosSIConfig/UIModel.h index 8943d27..96eae0d 100644 --- a/GlosSIConfig/UIModel.h +++ b/GlosSIConfig/UIModel.h @@ -29,6 +29,7 @@ class UIModel : public QObject { Q_PROPERTY(bool hasAcrlyicEffect READ hasAcrylicEffect NOTIFY acrylicChanged) Q_PROPERTY(QVariantList targetList READ getTargetList NOTIFY targetListChanged) Q_PROPERTY(QVariantList uwpList READ uwpApps CONSTANT) + Q_PROPERTY(QVariantList egsList READ egsGamesList CONSTANT) Q_PROPERTY(bool foundSteam READ foundSteam CONSTANT) Q_PROPERTY(bool steamInputXboxSupportEnabled READ isSteamInputXboxSupportEnabled CONSTANT) @@ -60,6 +61,7 @@ class UIModel : public QObject { #ifdef _WIN32 Q_INVOKABLE QVariantList uwpApps(); #endif + Q_INVOKABLE QVariantList egsGamesList() const; [[nodiscard]] bool writeShortcutsVDF(const std::wstring& mode, const std::wstring& name, const std::wstring& shortcutspath, bool is_admin_try = false) const; @@ -92,6 +94,9 @@ class UIModel : public QObject { QString user_data_path_ = "/userdata/"; QString steam_executable_name_ = "steam.exe"; + const std::wstring_view egs_games_json_path_ = + L"Epic/UnrealEngineLauncher/LauncherInstalled.dat"; + QVariantList targets_; QString new_version_name_; diff --git a/GlosSIConfig/qml.qrc b/GlosSIConfig/qml.qrc index d3945e1..c9a5eaa 100644 --- a/GlosSIConfig/qml.qrc +++ b/GlosSIConfig/qml.qrc @@ -22,5 +22,6 @@ qml/AdvancedTargetSettings.qml qml/GlobalConf.qml svg/settings_fill_white_24dp.svg + qml/EGSSelectDialog.qml diff --git a/GlosSIConfig/qml/AddSelectTypeDialog.qml b/GlosSIConfig/qml/AddSelectTypeDialog.qml index 8ecc10e..42b36a3 100644 --- a/GlosSIConfig/qml/AddSelectTypeDialog.qml +++ b/GlosSIConfig/qml/AddSelectTypeDialog.qml @@ -89,7 +89,7 @@ Dialog { } } Button { - visible: uiModel.isWindows + visible: uiModel.isWindows text: qsTr("UWP App") highlighted: true onClicked: function(){ @@ -97,6 +97,15 @@ Dialog { confirmed("uwp") } } + Button { + visible: uiModel.isWindows + text: qsTr("EGS Game") + highlighted: true + onClicked: function(){ + close() + confirmed("egs") + } + } } } diff --git a/GlosSIConfig/qml/EGSSelectDialog.qml b/GlosSIConfig/qml/EGSSelectDialog.qml new file mode 100644 index 0000000..5fce0bd --- /dev/null +++ b/GlosSIConfig/qml/EGSSelectDialog.qml @@ -0,0 +1,230 @@ +/* +Copyright 2021-2022 Peter Repukat - FlatspotSoftware + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +import QtQuick 6.2 +import QtQuick.Controls 6.2 +import QtQuick.Layouts 6.2 +import QtQuick.Controls.Material 6.2 + +Dialog { + id: dlg + anchors.centerIn: parent + + signal confirmed(var param) + + visible: false + modal: true + dim: true + parent: Overlay.overlay + Overlay.modal: Rectangle { + color: Qt.rgba(0,0,0,0.4) + opacity: backdropOpacity + Behavior on opacity { + NumberAnimation { + duration: 300 + } + } + } + property real backdropOpacity: 1.0 + + property var unfilteredModel: null; + property var filteredModel: []; + + + onOpened: function() { + unfilteredModel = null; + unfilteredModel = uiModel.egsList; + listview.model = null; + filteredModel = []; + for(let i = 0; i < unfilteredModel.length; i++) + { + filteredModel.push(unfilteredModel[i]) + } + listview.model = filteredModel + } + + onClosed: function() { + listview.model = null; + unfilteredModel = null; + filteredModel = null; + } + + enter: Transition { + NumberAnimation{target: content; property: "y"; from: parent.height; to: 16; duration: 300; easing.type: Easing.OutQuad } + NumberAnimation{target: background; property: "y"; from: parent.height; to: 0; duration: 300; easing.type: Easing.OutQuad } + NumberAnimation{target: dlg; property: "backdropOpacity"; from: 0; to: 1; duration: 300; easing.type: Easing.OutQuad } + } + + exit: Transition { + NumberAnimation{target: content; property: "y"; from: 16; to: parent.height; duration: 300; easing.type: Easing.InQuad } + NumberAnimation{target: background; property: "y"; from: 0; to: parent.height; duration: 300; easing.type: Easing.InQuad } + NumberAnimation{target: dlg; property: "backdropOpacity"; from: 1; to: 0; duration: 300; easing.type: Easing.InQuad } + } + + background: RPane { + id: background + radius: 4 + Material.elevation: 64 + bgOpacity: 0.97 + } + contentItem: Item { + id: content + implicitWidth: listview.width + implicitHeight: listview.height + titlelabel.height + 16 + 64 + clip: true + Label { + id: titlelabel + text: qsTr("Select Epic Games Launcher Game...") + font.pixelSize: 24 + font.bold: true + } + + FluentTextInput { + width: listview.width - 2 + x: 1 + anchors.top: titlelabel.bottom + anchors.topMargin: 8 + id: searchBar + enabled: true + placeholderText: qsTr("Search...") + text: "" + onTextChanged: function() { + listview.model = null; + filteredModel = []; + for(let i = 0; i < unfilteredModel.length; i++) + { + if(unfilteredModel[i].AppName.toLowerCase().includes(searchBar.text.toLowerCase())) { + filteredModel.push(unfilteredModel[i]) + } + } + listview.model = filteredModel + } + } + + BusyIndicator { + running: visible + anchors.centerIn: parent + opacity: (!unfilteredModel || unfilteredModel.length == 0) ? 1 : 0 + Behavior on opacity { + NumberAnimation { + duration: 350 + easing.type: Easing.InOutQuad + } + } + visible: opacity == 0 ? false : true + } + + Button { + anchors.right: parent.right + anchors.top: listview.bottom + anchors.topMargin: 16 + anchors.rightMargin: 2 + text: qsTr("Cancel") + onClicked: dlg.close() + } + + ListView { + anchors.top: searchBar.bottom + anchors.topMargin: 16 + id: listview + width: window.width * 0.45 + height: window.height * 0.66 + spacing: 0 + clip: true + model: filteredModel + ScrollBar.vertical: ScrollBar { + } + + opacity: (!unfilteredModel || unfilteredModel.length == 0) ? 0 : 1 + Behavior on opacity { + ParallelAnimation { + NumberAnimation { + duration: 350 + easing.type: Easing.InOutQuad + } + PropertyAnimation { + target: listview + property: "anchors.topMargin" + from: window.height * 0.75 + to: 16 + duration: 350 + easing.type: Easing.InOutQuad + } + } + } + + + delegate: Item { + width: listview.width + height: textcolumn.implicitHeight > 72 ? 500 : 72 + + /*Image { + id: maybeIcon + width: textcolumn.implicitHeight > 72 ? 0 : 56 + height: 56 + anchors.left: parent.left + anchors.verticalCenter: parent.verticalCenter + source: "file:///" + modelData.IconPath + mipmap: true + smooth: true + }*/ + + Column { + id: textcolumn + anchors.left: parent.left + anchors.right: parent.right + anchors.leftMargin: 16 + anchors.verticalCenter: parent.verticalCenter + spacing: 2 + Label { + text: modelData.InstallLocation.split('/').pop().split('\\').pop() + font.pixelSize: 18 + font.bold: true + } + Label { + id: appNameLabel + text: "AppName: " + modelData.AppName + font.pixelSize: 12 + wrapMode: Text.WordWrap + width: parent.width + } + Label { + id: fullPathLabel + text: modelData.InstallLocation + font.pixelSize: 12 + color: '#888888' + wrapMode: Text.WordWrap + width: parent.width + } + } + + Rectangle { + anchors.bottom: parent.bottom + height: 1 + width: parent.width + color: Qt.rgba(1,1,1,0.25) + } + + MouseArea { + anchors.fill: parent + onClicked: function(){ + confirmed(modelData) + dlg.close(); + } + } + } + } + } +} \ No newline at end of file diff --git a/GlosSIConfig/qml/ShortcutProps.qml b/GlosSIConfig/qml/ShortcutProps.qml index ecf7b3a..30862f5 100644 --- a/GlosSIConfig/qml/ShortcutProps.qml +++ b/GlosSIConfig/qml/ShortcutProps.qml @@ -26,6 +26,7 @@ Item { property alias fileDialog: fileDialog property alias uwpSelectDialog: uwpSelectDialog + property alias egsSelectDialog: egsSelectDialog signal cancel() signal done(var shortcut) @@ -309,6 +310,21 @@ Item { launchApp.checked = true } } + EGSSelectDialog { + id: egsSelectDialog + onConfirmed: function(modelData) { + if (nameInput.text == "") { + nameInput.text = modelData.InstallLocation.split('/').pop().split('\\').pop() + } + pathInput.text = "com.epicgames.launcher://apps/" + + modelData.NamespaceId + + "%3A" + + modelData.ItemId + + "%3A" + + modelData.ArtifactId + "?action=launch&silent=true" + launchApp.checked = true + } + } InfoDialog { id: helpInfoDialog diff --git a/GlosSIConfig/qml/main.qml b/GlosSIConfig/qml/main.qml index 3894e96..579cdfb 100644 --- a/GlosSIConfig/qml/main.qml +++ b/GlosSIConfig/qml/main.qml @@ -387,6 +387,9 @@ Window { if (param == "uwp") { props.uwpSelectDialog.open(); } + if (param == "egs") { + props.egsSelectDialog.open(); + } } } }