From 7a01ceede1b6d078fdb1055e7420bc1a99e5cff9 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sat, 21 Jan 2023 23:55:32 +0100 Subject: [PATCH 01/65] GlosSITarget: default hideAltTab to true --- GlosSITarget/Settings.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GlosSITarget/Settings.h b/GlosSITarget/Settings.h index f2b37df..5254065 100644 --- a/GlosSITarget/Settings.h +++ b/GlosSITarget/Settings.h @@ -54,7 +54,7 @@ inline struct Window { int maxFps = 0; float scale = 0.f; bool disableOverlay = false; - bool hideAltTab = false; + bool hideAltTab = true; bool disableGlosSIOverlay = false; } window; From 4b57c6cb94da4e512870bb41d52167a6169a75ba Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 22 Jan 2023 00:00:23 +0100 Subject: [PATCH 02/65] GlosSIConfig: Default HideAltTab to true --- GlosSIConfig/UIModel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index 172630a..a382f49 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -424,11 +424,11 @@ QVariantMap UIModel::getDefaultConf() const {"window", QJsonObject{ {"disableOverlay", false}, - {"hideAltTab", false}, + {"hideAltTab", true}, {"maxFps", QJsonValue::Null}, {"scale", QJsonValue::Null}, {"windowMode", false}, - {"disableGlosSIOverlay", false}, + {"disableGlosSIOverlay", false} }}, }; From 2941b4548e00fd8205c628763f885719afccc21e Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 13:51:23 +0100 Subject: [PATCH 03/65] Add CEFInject lib and add PoC --- .gitmodules | 3 + CEFInjectLib/CEFInject.cpp | 142 +++++++++++++++++++ CEFInjectLib/CEFInject.h | 32 +++++ CEFInjectLib/CEFInjectLib.vcxproj | 158 ++++++++++++++++++++++ CEFInjectLib/CEFInjectLib.vcxproj.filters | 27 ++++ GlosSI.sln | 13 ++ GlosSITarget/SteamTarget.cpp | 4 + deps/easywsclient | 1 + 8 files changed, 380 insertions(+) create mode 100644 CEFInjectLib/CEFInject.cpp create mode 100644 CEFInjectLib/CEFInject.h create mode 100644 CEFInjectLib/CEFInjectLib.vcxproj create mode 100644 CEFInjectLib/CEFInjectLib.vcxproj.filters create mode 160000 deps/easywsclient diff --git a/.gitmodules b/.gitmodules index ae61cba..4e86e86 100644 --- a/.gitmodules +++ b/.gitmodules @@ -37,3 +37,6 @@ [submodule "deps/cpp-httplib"] path = deps/cpp-httplib url = https://github.com/yhirose/cpp-httplib +[submodule "deps/easywsclient"] + path = deps/easywsclient + url = https://github.com/dhbaird/easywsclient diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp new file mode 100644 index 0000000..c3a579d --- /dev/null +++ b/CEFInjectLib/CEFInject.cpp @@ -0,0 +1,142 @@ +/* +Copyright 2021-2023 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. +*/ + +#include "CEFInject.h" + + +#include + +#define _SSIZE_T_DEFINED +#include // seems like a hack to me, but eh + +#include +#include + +namespace nlohmann { + template <> + struct adl_serializer { + static void to_json(json& j, const std::wstring& str) { + j = std::wstring_convert>().to_bytes(str); + } + + static void from_json(const json& j, std::wstring& str) { + str = std::wstring_convert>().from_bytes(j.get()); + } + }; +} + +namespace CEFInject +{ + namespace internal + { + httplib::Client GetHttpClient(uint16_t port) + { + httplib::Client cli("localhost", port); + cli.set_connection_timeout(1000); + return cli; + } + + static uint32_t msg_id = 0; + + } + bool CEFDebugAvailable(uint16_t port) + { + auto cli = internal::GetHttpClient(port); + cli.set_connection_timeout(500); + cli.set_read_timeout(500); + if (auto res = cli.Get("/json")) { + if (res->status == 200) { + return true; + } + } + return false; + } + + std::vector AvailableTabs(uint16_t port) + { + std::vector tabs; + auto cli = internal::GetHttpClient(port); + if (auto res = cli.Get("/json")) { + if (res->status == 200) { + const auto json = nlohmann::json::parse(res->body); + for (const auto& j : json) { + tabs.push_back(j["title"].get()); + } + } + } + return tabs; + } + + + std::string InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port) + { + auto cli = internal::GetHttpClient(port); + if (auto res = cli.Get("/json")) { + if (res->status == 200) { + const auto json = nlohmann::json::parse(res->body); + for (const auto& tab : json) { + if (tab["title"].get().starts_with(tabname)) { + + +#ifdef _WIN32 + INT rc; + WSADATA wsaData; + + rc = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (rc) { + printf("WSAStartup Failed.\n"); + return {}; + } +#endif + + std::shared_ptr ws{ + easywsclient::WebSocket::from_url(tab["webSocketDebuggerUrl"].get()) + }; + if (ws) + { + ws->send( + nlohmann::json{ + {"id", internal::msg_id++}, + {"method", "Runtime.evaluate"}, + {"params", { + {"userGesture", true}, + {"expression", js} + }} + }.dump()); + std::string res; + while (ws->getReadyState() != easywsclient::WebSocket::CLOSED) { + ws->poll(); + ws->dispatch([&ws, &res](const std::string& message) { + res = message; + }); + ws->close(); + } +#ifdef _WIN32 + WSACleanup(); +#endif + return res; + } +#ifdef _WIN32 + WSACleanup(); +#endif + return {}; + } + } + } + } + } + +} \ No newline at end of file diff --git a/CEFInjectLib/CEFInject.h b/CEFInjectLib/CEFInject.h new file mode 100644 index 0000000..82f4510 --- /dev/null +++ b/CEFInjectLib/CEFInject.h @@ -0,0 +1,32 @@ +/* +Copyright 2021-2023 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. +*/ +#pragma once + +#include +#include + + +namespace CEFInject +{ + namespace internal { + httplib::Client GetHttpClient(uint16_t port); + } + + bool CEFDebugAvailable(uint16_t port = 8080); + std::vector AvailableTabs(uint16_t port = 8080); + std::string InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port = 8080); + +} \ No newline at end of file diff --git a/CEFInjectLib/CEFInjectLib.vcxproj b/CEFInjectLib/CEFInjectLib.vcxproj new file mode 100644 index 0000000..f39c501 --- /dev/null +++ b/CEFInjectLib/CEFInjectLib.vcxproj @@ -0,0 +1,158 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + + + + + + + 16.0 + Win32Proj + {74fba967-ab7e-43ea-b561-3f4821954b3b} + CEFInjectLib + 10.0 + + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + StaticLibrary + true + v143 + Unicode + + + StaticLibrary + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + ..\deps\cpp-httplib;..\deps\json\include;..\deps\easywsclient;$(IncludePath) + + + ..\deps\cpp-httplib;..\deps\json\include;..\deps\easywsclient;$(IncludePath) + + + + Level3 + true + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + Use + pch.h + + + + + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + Use + pch.h + + + + + true + true + true + + + + + Level3 + true + _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + stdcpp20 + + + + + true + + + + + Level3 + true + true + true + _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + NotUsing + pch.h + stdcpp20 + + + + + true + true + true + + + + + + \ No newline at end of file diff --git a/CEFInjectLib/CEFInjectLib.vcxproj.filters b/CEFInjectLib/CEFInjectLib.vcxproj.filters new file mode 100644 index 0000000..70e20e3 --- /dev/null +++ b/CEFInjectLib/CEFInjectLib.vcxproj.filters @@ -0,0 +1,27 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + + + Source Files + + + \ No newline at end of file diff --git a/GlosSI.sln b/GlosSI.sln index 4be7ecb..8102453 100644 --- a/GlosSI.sln +++ b/GlosSI.sln @@ -4,6 +4,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00 VisualStudioVersion = 17.3.32922.545 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GlosSITarget", "GlosSITarget\GlosSITarget.vcxproj", "{076E263E-0687-4435-836E-8F4EF6668843}" + ProjectSection(ProjectDependencies) = postProject + {74FBA967-AB7E-43EA-B561-3F4821954B3B} = {74FBA967-AB7E-43EA-B561-3F4821954B3B} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GlosSIConfig", "GlosSIConfig\GlosSIConfig.vcxproj", "{4B42920B-3CC6-475F-A5B3-441337968483}" EndProject @@ -11,6 +14,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UWPOverlayEnablerDLL", "UWP EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GlosSIWatchdog", "GlosSIWatchdog\GlosSIWatchdog.vcxproj", "{BF273B90-CB69-43C8-9AF6-F3256DAFD41E}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CEFInjectLib", "CEFInjectLib\CEFInjectLib.vcxproj", "{74FBA967-AB7E-43EA-B561-3F4821954B3B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -49,6 +54,14 @@ Global {BF273B90-CB69-43C8-9AF6-F3256DAFD41E}.Release|x64.Build.0 = Release|x64 {BF273B90-CB69-43C8-9AF6-F3256DAFD41E}.Release|x86.ActiveCfg = Release|Win32 {BF273B90-CB69-43C8-9AF6-F3256DAFD41E}.Release|x86.Build.0 = Release|Win32 + {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Debug|x64.ActiveCfg = Debug|x64 + {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Debug|x64.Build.0 = Debug|x64 + {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Debug|x86.ActiveCfg = Debug|Win32 + {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Debug|x86.Build.0 = Debug|Win32 + {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x64.ActiveCfg = Release|x64 + {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x64.Build.0 = Release|x64 + {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x86.ActiveCfg = Release|Win32 + {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 16e62f9..4767e1c 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -30,6 +30,7 @@ limitations under the License. #include #endif +#include SteamTarget::SteamTarget() : window_( @@ -61,6 +62,9 @@ SteamTarget::SteamTarget() int SteamTarget::run() { + + // CEFInject::InjectJs(L"Steam Shared Context presented by Valve", L"console.log('FUCK YEAH!')"); + if (!SteamOverlayDetector::IsSteamInjected()) { spdlog::warn("Steam-overlay not detected. Showing GlosSI-overlay!\n\ Application will not function!"); diff --git a/deps/easywsclient b/deps/easywsclient new file mode 160000 index 0000000..afc1d8c --- /dev/null +++ b/deps/easywsclient @@ -0,0 +1 @@ +Subproject commit afc1d8cfc584e0f1f4a77e8c0ce3e979d9fe7ce2 From 10b65a1a9b25f76ea553fbb5523b15bec75b39d1 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 7 Nov 2022 20:33:37 +0100 Subject: [PATCH 04/65] WIP: Experimental standalone (not launched via Steam (steam needs to be open!) mode --- GlosSITarget/SteamTarget.cpp | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 4767e1c..b26a818 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -72,6 +72,41 @@ Application will not function!"); if (!overlay_.expired()) overlay_.lock()->setEnabled(true); steam_overlay_present_ = false; + + + spdlog::warn("GlosSI not launched via Steam.\nEnabling EXPERIMENTAL global controller and overlay..."); + //SetEnvironmentVariable(L"SteamAppId", L"2934978560"); + SetEnvironmentVariable(L"SteamTenfoot", L"1"); + SetEnvironmentVariable(L"SteamTenfootHybrid", L"1"); + SetEnvironmentVariable(L"SteamGamepadUI", L"1"); + //SetEnvironmentVariable(L"SteamGameId", L"12605636929694728192"); + //SetEnvironmentVariable(L"SteamOverlayGameId", L"12605636929694728192"); + //SetEnvironmentVariable(L"SteamGameId", L"2934978560"); + //SetEnvironmentVariable(L"SteamOverlayGameId", L"2934978560"); + + system("start steam://open/bigpicture"); + auto steamwindow = FindWindow(L"SDL_app", nullptr); + int bla = 0; + while (!steamwindow && bla < 50) { + Sleep(100); + steamwindow = FindWindow(L"SDL_app", nullptr); + bla++; + } + if (steamwindow) { + LoadLibrary(L"C:\\Program Files (x86)\\Steam\\GameOverlayRenderer64.dll"); + + SendMessage(steamwindow, WM_CLOSE, 0, 0); + SendMessage(steamwindow, WM_QUIT, 0, 0); + SendMessage(steamwindow, WM_DESTROY, 0, 0); + } else { + spdlog::warn("Steam window not found!"); + } + + window_.setClickThrough(true); + if (!overlay_.expired()) + overlay_.lock()->setEnabled(false); + steam_overlay_present_ = true; + } else { spdlog::info("Steam-overlay detected."); From 70187090e71803d0698a9364681b8fdae1586f8c Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sat, 21 Jan 2023 23:56:26 +0100 Subject: [PATCH 05/65] GlosSITarget: Update experimental "stand-alone"-mode --- GlosSITarget/Settings.h | 5 +++ GlosSITarget/SteamTarget.cpp | 78 ++++++++++++++++++++---------------- 2 files changed, 48 insertions(+), 35 deletions(-) diff --git a/GlosSITarget/Settings.h b/GlosSITarget/Settings.h index 5254065..896eae9 100644 --- a/GlosSITarget/Settings.h +++ b/GlosSITarget/Settings.h @@ -73,6 +73,8 @@ inline struct Common { int version; std::wstring steamPath; std::wstring steamUserId; + std::wstring standaloneModeGameId; /* = L"12605636929694728192"; */ + bool standaloneUseGamepadUI = false; } common; inline std::filesystem::path settings_path_ = ""; @@ -209,6 +211,9 @@ inline void Parse(const nlohmann::basic_json<>& json) safeWStringParse(json, "steamPath", common.steamPath); safeWStringParse(json, "steamUserId", common.steamUserId); + + safeWStringParse(json, "standaloneModeGameId", common.standaloneModeGameId); + safeParseValue(json, "standaloneUseGamepadUI", common.standaloneUseGamepadUI); } catch (const nlohmann::json::exception& e) { spdlog::warn("Err parsing config: {}", e.what()); diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index b26a818..2e86a40 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -62,44 +62,46 @@ SteamTarget::SteamTarget() int SteamTarget::run() { - - // CEFInject::InjectJs(L"Steam Shared Context presented by Valve", L"console.log('FUCK YEAH!')"); - + auto closeBPM = false; + auto closeBPMTimer = sf::Clock{}; if (!SteamOverlayDetector::IsSteamInjected()) { - spdlog::warn("Steam-overlay not detected. Showing GlosSI-overlay!\n\ -Application will not function!"); - window_.setClickThrough(false); - if (!overlay_.expired()) - overlay_.lock()->setEnabled(true); - steam_overlay_present_ = false; - - spdlog::warn("GlosSI not launched via Steam.\nEnabling EXPERIMENTAL global controller and overlay..."); - //SetEnvironmentVariable(L"SteamAppId", L"2934978560"); - SetEnvironmentVariable(L"SteamTenfoot", L"1"); - SetEnvironmentVariable(L"SteamTenfootHybrid", L"1"); - SetEnvironmentVariable(L"SteamGamepadUI", L"1"); - //SetEnvironmentVariable(L"SteamGameId", L"12605636929694728192"); - //SetEnvironmentVariable(L"SteamOverlayGameId", L"12605636929694728192"); - //SetEnvironmentVariable(L"SteamGameId", L"2934978560"); - //SetEnvironmentVariable(L"SteamOverlayGameId", L"2934978560"); - - system("start steam://open/bigpicture"); - auto steamwindow = FindWindow(L"SDL_app", nullptr); - int bla = 0; - while (!steamwindow && bla < 50) { - Sleep(100); - steamwindow = FindWindow(L"SDL_app", nullptr); - bla++; + if (Settings::common.standaloneModeGameId == L"") { + spdlog::error("No game id set for standalone mode. Controller will use desktop-config!"); + } + + + SetEnvironmentVariable(L"SteamAppId", L"0"); + SetEnvironmentVariable(L"SteamClientLaunch", L"0"); + SetEnvironmentVariable(L"SteamEnv", L"1"); + SetEnvironmentVariable(L"SteamPath", getSteamPath().wstring().c_str()); + SetEnvironmentVariable(L"SteamTenfoot", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); + //SetEnvironmentVariable(L"SteamTenfootHybrid", L"1"); + SetEnvironmentVariable(L"SteamGamepadUI", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); + SetEnvironmentVariable(L"SteamGameId", Settings::common.standaloneModeGameId.c_str()); + SetEnvironmentVariable(L"SteamOverlayGameId", Settings::common.standaloneModeGameId.c_str()); + SetEnvironmentVariable(L"EnableConfiguratorSupport", L"15"); + SetEnvironmentVariable(L"SteamStreamingForceWindowedD3D9", L"1"); + + if (Settings::common.standaloneUseGamepadUI) { + system("start steam://open/bigpicture"); + auto steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); + auto timer = sf::Clock{}; + while (!steamwindow && timer.getElapsedTime().asSeconds() < 2) { + steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); + Sleep(50); + } + Sleep(6000); // DIRTY HACK to wait until BPM (GamepadUI) is initialized + // TODO: find way to force BPM even if BPM is not active + LoadLibrary((getSteamPath() / "GameOverlayRenderer64.dll").wstring().c_str()); + + // Overlay switches back to desktop one, once BPM is closed... Disable closing BPM for now. + // TODO: find way to force BPM even if BPM is not active + // closeBPM = true; + closeBPMTimer.restart(); } - if (steamwindow) { - LoadLibrary(L"C:\\Program Files (x86)\\Steam\\GameOverlayRenderer64.dll"); - - SendMessage(steamwindow, WM_CLOSE, 0, 0); - SendMessage(steamwindow, WM_QUIT, 0, 0); - SendMessage(steamwindow, WM_DESTROY, 0, 0); - } else { - spdlog::warn("Steam window not found!"); + else { + LoadLibrary( (getSteamPath() / "GameOverlayRenderer64.dll").wstring().c_str()); } window_.setClickThrough(true); @@ -172,6 +174,12 @@ Application will not function!"); detector_.update(); overlayHotkeyWorkaround(); window_.update(); + + if (closeBPM && closeBPMTimer.getElapsedTime().asSeconds() >= 3) { + system("start steam://close/bigpicture"); + closeBPM = false; + } + // Wait on shutdown; User might get confused if window closes to fast if anything with launchApp get's borked. if (delayed_shutdown_) { if (delay_shutdown_clock_.getElapsedTime().asSeconds() >= 3) { From b69bd4abdd451a26d11194ca8dabc2f8c159bfd1 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 22 Jan 2023 01:23:32 +0100 Subject: [PATCH 06/65] GlosSIConfig: Add some UI for experimental standalone mode setup --- GlosSIConfig/UIModel.cpp | 49 ++++++++++++++++++++ GlosSIConfig/UIModel.h | 6 +++ GlosSIConfig/qml/GlobalConf.qml | 79 +++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+) diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index a382f49..61edb12 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -206,6 +206,21 @@ uint32_t UIModel::getAppId(QVariant shortcut) return 0; } +Q_INVOKABLE QString UIModel::getGameId(QVariant shortcut) { + + /* + * enum SteamLaunchableType + { + App = 0, + GameMod = 1, + Shortcut = 2, + P2P = 3 + } + */ + uint64_t gameId = (((uint64_t)getAppId(shortcut) << 32) | ((uint32_t)2 << 24) | 0); + return QVariant(gameId).toString(); +} + bool UIModel::addToSteam(QVariant shortcut, const QString& shortcutspath, bool from_cmd) { QDir appDir = QGuiApplication::applicationDirPath(); @@ -404,6 +419,8 @@ QVariantMap UIModel::getDefaultConf() const QJsonValue::fromVariant(QString::fromStdWString(getSteamPath(false).wstring()))}, {"steamUserId", QJsonValue::fromVariant(QString::fromStdWString(getSteamUserId(false)))}, + {"standaloneModeGameId", ""}, + {"standaloneUseGamepadUI", false}, {"controller", QJsonObject{{"maxControllers", 1}, {"emulateDS4", false}, {"allowDesktopConfig", false}}}, {"devices", QJsonObject{ @@ -486,6 +503,38 @@ void UIModel::saveDefaultConf(QVariantMap conf) const file.close(); } +Q_INVOKABLE QVariant UIModel::standaloneShortcutConf() { + for (auto& target : targets_) { + const auto map = target.toMap(); + if (map["name"] == "GlosSI Standalone/Desktop") { + return target; + } + } + return QVariant(); +} + +Q_INVOKABLE bool UIModel::standaloneModeShortcutExists() { + const auto map = standaloneShortcutConf().toMap(); + if (map["name"] == "GlosSI Standalone/Desktop") { + return true; + } + return false; +} + +Q_INVOKABLE uint32_t UIModel::standaloneModeShortcutAppId() { + if (!standaloneModeShortcutExists()) { + return 0; + } + return getAppId(standaloneShortcutConf()); +} + +Q_INVOKABLE QString UIModel::standaloneModeShortcutGameId() { + if (!standaloneModeShortcutExists()) { + return ""; + } + return getGameId(standaloneShortcutConf()); +} + #ifdef _WIN32 QVariantList UIModel::uwpApps() { return UWPFetch::UWPAppList(); } #endif diff --git a/GlosSIConfig/UIModel.h b/GlosSIConfig/UIModel.h index 4edcb56..10cdb3a 100644 --- a/GlosSIConfig/UIModel.h +++ b/GlosSIConfig/UIModel.h @@ -50,6 +50,7 @@ class UIModel : public QObject { Q_INVOKABLE void deleteTarget(int index); Q_INVOKABLE bool isInSteam(QVariant shortcut) const; Q_INVOKABLE uint32_t getAppId(QVariant shortcut); + Q_INVOKABLE QString getGameId(QVariant shortcut); Q_INVOKABLE bool addToSteam(QVariant shortcut, const QString& shortcutspath, bool from_cmd = false); bool addToSteam(const QString& name, const QString& shortcutspath, bool from_cmd = false); Q_INVOKABLE bool removeFromSteam(const QString& name, const QString& shortcutspath, bool from_cmd = false); @@ -62,6 +63,11 @@ class UIModel : public QObject { Q_INVOKABLE QVariantMap getDefaultConf() const; Q_INVOKABLE void saveDefaultConf(QVariantMap conf) const; + + Q_INVOKABLE QVariant standaloneShortcutConf(); + Q_INVOKABLE bool standaloneModeShortcutExists(); + Q_INVOKABLE uint32_t standaloneModeShortcutAppId(); + Q_INVOKABLE QString standaloneModeShortcutGameId(); #ifdef _WIN32 Q_INVOKABLE QVariantList uwpApps(); diff --git a/GlosSIConfig/qml/GlobalConf.qml b/GlosSIConfig/qml/GlobalConf.qml index 29ec398..4cda0e7 100644 --- a/GlosSIConfig/qml/GlobalConf.qml +++ b/GlosSIConfig/qml/GlobalConf.qml @@ -118,6 +118,85 @@ Item { + "as well as settings applied when launching GlosSITarget without config") } + Item { + width: 1 + height: 4 + } + + RPane { + width: parent.width + radius: 4 + Material.elevation: 32 + bgOpacity: 0.97 + Column { + width: parent.width + height: parent.height + spacing: 4 + Label { + font.bold: true + font.pixelSize: 24 + text: qsTr("Experimental 🧪") + } + Row { + Row { + CheckBox { + id: standaloneUseGamepadUI + text: qsTr("Use BPM for standalone-/desktop-mode") + checked: config.standaloneUseGamepadUI + onCheckedChanged: config.standaloneUseGamepadUI = checked + } + } + } + Row { + leftPadding: 12 + Row { + spacing: 16 + Label { + topPadding: 8 + id: standAloneGameIdLabel + text: qsTr("StandaloneGameId") + } + FluentTextInput { + width: 128 + id: standAloneGameId + enabled: false + text: config.standaloneModeGameId + onTextChanged: config.standaloneModeGameId = text + } + Button { + id: standAloneGameIdButton + text: qsTr("Create standalone-/desktop-mode shortcut") + onClicked: { + const standaloneConf = uiModel.getDefaultConf(); + standaloneConf.name = "GlosSI Standalone/Desktop"; + standaloneConf.launch.launch = false; + uiModel.addTarget(standaloneConf); + if (uiModel.addToSteam(standaloneConf, "")) { + steamChangedDialog.open(); + } + const standaloneGID = uiModel.standaloneModeShortcutGameId(); + standAloneGameId.text = standaloneGID; + setTimeout(() => { + uiModel.saveDefaultConf(config); + done(); + }, 10); + } + highlighted: true + visible: !uiModel.standaloneModeShortcutExists() + } + Button { + id: standAloneGameIdConfigButton + text: qsTr("Open standalone-/desktop-mode controller config") + onClicked: { + Qt.openUrlExternally("steam://currentcontrollerconfig/" + uiModel.standaloneModeShortcutAppId() + "/"); + } + visible: uiModel.standaloneModeShortcutExists() + } + } + } + } + } + Item { width: 1 height: 32 From b33b2a0691f31136a82c4f66576c93835f3a930a Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 14:15:26 +0100 Subject: [PATCH 07/65] CEFInject: Return value of evaluated js --- CEFInjectLib/CEFInject.cpp | 19 ++++++++++++------- CEFInjectLib/CEFInject.h | 2 +- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp index c3a579d..c31d926 100644 --- a/CEFInjectLib/CEFInject.cpp +++ b/CEFInjectLib/CEFInject.cpp @@ -81,7 +81,7 @@ namespace CEFInject } - std::string InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port) + nlohmann::json InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port) { auto cli = internal::GetHttpClient(port); if (auto res = cli.Get("/json")) { @@ -98,7 +98,7 @@ namespace CEFInject rc = WSAStartup(MAKEWORD(2, 2), &wsaData); if (rc) { printf("WSAStartup Failed.\n"); - return {}; + return nullptr; } #endif @@ -116,13 +116,18 @@ namespace CEFInject {"expression", js} }} }.dump()); - std::string res; + nlohmann::json res = nullptr; + bool exit = false; while (ws->getReadyState() != easywsclient::WebSocket::CLOSED) { ws->poll(); - ws->dispatch([&ws, &res](const std::string& message) { - res = message; + ws->dispatch([&ws, & res, & exit](const std::string & message) { + res = nlohmann::json::parse(message)["result"]["result"]["value"]; + exit = true; }); - ws->close(); + if (exit) { + ws->close(); + return res; + } } #ifdef _WIN32 WSACleanup(); @@ -132,7 +137,7 @@ namespace CEFInject #ifdef _WIN32 WSACleanup(); #endif - return {}; + return nullptr; } } } diff --git a/CEFInjectLib/CEFInject.h b/CEFInjectLib/CEFInject.h index 82f4510..223fb65 100644 --- a/CEFInjectLib/CEFInject.h +++ b/CEFInjectLib/CEFInject.h @@ -27,6 +27,6 @@ namespace CEFInject bool CEFDebugAvailable(uint16_t port = 8080); std::vector AvailableTabs(uint16_t port = 8080); - std::string InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port = 8080); + nlohmann::json InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port = 8080); } \ No newline at end of file From 738803fa2efcbca1cbda21655e937a52a0695e57 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 16:00:06 +0100 Subject: [PATCH 08/65] Cleanup: consolidate utils and common code --- CEFInjectLib/CEFInject.cpp | 16 +- CEFInjectLib/CEFInject.h | 12 +- GlosSI.sln | 10 + GlosSIConfig/ExeImageProvider.h | 1 + GlosSIConfig/GlosSIConfig.vcxproj | 2 +- GlosSIConfig/GlosSIConfig.vcxproj.filters | 2 +- GlosSIConfig/Resource.rc | 24 +- GlosSIConfig/UIModel.cpp | 41 +-- GlosSIConfig/main.cpp | 31 +- GlosSITarget/AppLauncher.cpp | 24 +- GlosSITarget/DllInjector.h | 6 +- GlosSITarget/GlosSITarget.vcxproj | 8 +- GlosSITarget/GlosSITarget.vcxproj.filters | 22 +- GlosSITarget/HttpServer.cpp | 2 +- GlosSITarget/InputRedirector.cpp | 2 +- GlosSITarget/Overlay.cpp | 20 +- GlosSITarget/Resource.rc | 128 ++++++- GlosSITarget/Settings.h | 336 ------------------ GlosSITarget/SteamOverlayDetector.cpp | 2 +- GlosSITarget/SteamTarget.cpp | 3 +- GlosSITarget/SteamTarget.h | 2 +- GlosSITarget/TargetWindow.cpp | 2 +- GlosSITarget/UWPOverlayEnabler.h | 1 - GlosSITarget/UnhookUtil.h | 66 ---- GlosSITarget/main.cpp | 30 +- GlosSITarget/util.h | 76 ---- GlosSIWatchdog/GlosSIWatchdog.vcxproj | 4 +- GlosSIWatchdog/GlosSIWatchdog.vcxproj.filters | 4 +- GlosSIWatchdog/dllmain.cpp | 29 +- Installer/Installer.nsi | 41 ++- UWPOverlayEnablerDLL/dllmain.cpp | 20 +- {GlosSITarget => common}/HidHide.cpp | 197 +++++----- {GlosSITarget => common}/HidHide.h | 10 +- common/Settings.h | 300 ++++++++++++++++ {GlosSITarget => common}/UnhookUtil.cpp | 19 +- common/UnhookUtil.h | 66 ++++ common/common.vcxproj | 151 ++++++++ common/common.vcxproj.filters | 42 +++ common/nlohmann_json_wstring.h | 36 ++ common/util.h | 163 +++++++++ 40 files changed, 1134 insertions(+), 817 deletions(-) delete mode 100644 GlosSITarget/Settings.h delete mode 100644 GlosSITarget/UnhookUtil.h delete mode 100644 GlosSITarget/util.h rename {GlosSITarget => common}/HidHide.cpp (78%) rename {GlosSITarget => common}/HidHide.h (97%) create mode 100644 common/Settings.h rename {GlosSITarget => common}/UnhookUtil.cpp (88%) create mode 100644 common/UnhookUtil.h create mode 100644 common/common.vcxproj create mode 100644 common/common.vcxproj.filters create mode 100644 common/nlohmann_json_wstring.h create mode 100644 common/util.h diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp index c31d926..4270031 100644 --- a/CEFInjectLib/CEFInject.cpp +++ b/CEFInjectLib/CEFInject.cpp @@ -22,21 +22,7 @@ limitations under the License. #define _SSIZE_T_DEFINED #include // seems like a hack to me, but eh -#include -#include - -namespace nlohmann { - template <> - struct adl_serializer { - static void to_json(json& j, const std::wstring& str) { - j = std::wstring_convert>().to_bytes(str); - } - - static void from_json(const json& j, std::wstring& str) { - str = std::wstring_convert>().from_bytes(j.get()); - } - }; -} +#include "../common/nlohmann_json_wstring.h" namespace CEFInject { diff --git a/CEFInjectLib/CEFInject.h b/CEFInjectLib/CEFInject.h index 223fb65..fc077f6 100644 --- a/CEFInjectLib/CEFInject.h +++ b/CEFInjectLib/CEFInject.h @@ -23,10 +23,14 @@ namespace CEFInject { namespace internal { httplib::Client GetHttpClient(uint16_t port); + static inline uint16_t port_ = 8080; } - - bool CEFDebugAvailable(uint16_t port = 8080); - std::vector AvailableTabs(uint16_t port = 8080); - nlohmann::json InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port = 8080); + inline void setPort(uint16_t port) + { + internal::port_ = port; + } + bool CEFDebugAvailable(uint16_t port = internal::port_); + std::vector AvailableTabs(uint16_t port = internal::port_); + nlohmann::json InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port = internal::port_); } \ No newline at end of file diff --git a/GlosSI.sln b/GlosSI.sln index 8102453..736ba44 100644 --- a/GlosSI.sln +++ b/GlosSI.sln @@ -16,6 +16,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GlosSIWatchdog", "GlosSIWat EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CEFInjectLib", "CEFInjectLib\CEFInjectLib.vcxproj", "{74FBA967-AB7E-43EA-B561-3F4821954B3B}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common\common.vcxproj", "{DFED4B7E-D04C-442B-BB48-5B6068A6B31B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|x64 = Debug|x64 @@ -62,6 +64,14 @@ Global {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x64.Build.0 = Release|x64 {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x86.ActiveCfg = Release|Win32 {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x86.Build.0 = Release|Win32 + {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Debug|x64.ActiveCfg = Debug|x64 + {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Debug|x64.Build.0 = Debug|x64 + {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Debug|x86.ActiveCfg = Debug|Win32 + {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Debug|x86.Build.0 = Debug|Win32 + {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Release|x64.ActiveCfg = Release|x64 + {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Release|x64.Build.0 = Release|x64 + {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Release|x86.ActiveCfg = Release|Win32 + {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/GlosSIConfig/ExeImageProvider.h b/GlosSIConfig/ExeImageProvider.h index f712896..de8d0bf 100644 --- a/GlosSIConfig/ExeImageProvider.h +++ b/GlosSIConfig/ExeImageProvider.h @@ -2,6 +2,7 @@ #include #include #include +#include class ExeImageProvider : public QQuickImageProvider { public: ExeImageProvider() diff --git a/GlosSIConfig/GlosSIConfig.vcxproj b/GlosSIConfig/GlosSIConfig.vcxproj index 55a1b71..4fab583 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj +++ b/GlosSIConfig/GlosSIConfig.vcxproj @@ -136,7 +136,7 @@ - + diff --git a/GlosSIConfig/GlosSIConfig.vcxproj.filters b/GlosSIConfig/GlosSIConfig.vcxproj.filters index fc4e559..17fbafe 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj.filters +++ b/GlosSIConfig/GlosSIConfig.vcxproj.filters @@ -37,7 +37,7 @@ Source Files - + Source Files diff --git a/GlosSIConfig/Resource.rc b/GlosSIConfig/Resource.rc index 9062346..b01172c 100644 --- a/GlosSIConfig/Resource.rc +++ b/GlosSIConfig/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,1,2012005004400 - PRODUCTVERSION 0,1,1,2012005004400 + FILEVERSION 0,1,2,0010004309958 + PRODUCTVERSION 0,1,2,0010004309958 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - Config" - VALUE "FileVersion", "0.1.1.2-12-g5fe44d0" + VALUE "FileVersion", "0.1.2.0-10-g43c9958" VALUE "InternalName", "GlosSIConfig" VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSIConfig.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.1.2-12-g5fe44d0" + VALUE "ProductVersion", "0.1.2.0-10-g43c9958" END END BLOCK "VarFileInfo" @@ -1503,6 +1503,22 @@ IDI_ICON1 ICON "..\GlosSI_Icon.ico" + + + + + + + + + + + + + + + + diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index 61edb12..62c86aa 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -32,30 +32,18 @@ limitations under the License. #ifdef _WIN32 #include "UWPFetch.h" #include -#include #endif #include "ExeImageProvider.h" #include "../version.hpp" -#include "../../GlosSITarget/UnhookUtil.h" +#include "../common/UnhookUtil.h" +#include "../common/util.h" UIModel::UIModel() : QObject(nullptr) { - wchar_t* localAppDataFolder; - std::filesystem::path path; - if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) { - path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); - } - else { - path = std::filesystem::path(localAppDataFolder).parent_path(); - } - - path /= "Roaming"; - path /= "GlosSI"; - if (!std::filesystem::exists(path)) - std::filesystem::create_directories(path); + auto path = util::path::getDataDirPath(); qDebug() << "Version: " << getVersionString(); @@ -395,17 +383,8 @@ void UIModel::updateCheck() QVariantMap UIModel::getDefaultConf() const { - wchar_t* localAppDataFolder; - std::filesystem::path path; - if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) { - path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); - } - else { - path = std::filesystem::path(localAppDataFolder).parent_path(); - } + auto path = util::path::getDataDirPath(); - path /= "Roaming"; - path /= "GlosSI"; path /= "default.json"; QJsonObject defaults = { @@ -481,16 +460,8 @@ QVariantMap UIModel::getDefaultConf() const void UIModel::saveDefaultConf(QVariantMap conf) const { - wchar_t* localAppDataFolder; - std::filesystem::path path; - if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) { - path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); - } - else { - path = std::filesystem::path(localAppDataFolder).parent_path(); - } - path /= "Roaming"; - path /= "GlosSI"; + auto path = util::path::getDataDirPath(); + path /= "default.json"; QFile file(path); diff --git a/GlosSIConfig/main.cpp b/GlosSIConfig/main.cpp index ba303d3..1ef2e1b 100644 --- a/GlosSIConfig/main.cpp +++ b/GlosSIConfig/main.cpp @@ -22,15 +22,17 @@ limitations under the License. #include #include +#include "../common/util.h" + #ifdef _WIN32 #include #include #include -#include #pragma comment(lib, "Dwmapi.lib") #include "ExeImageProvider.h" #endif + #include "UIModel.h" #include "WinEventFilter.h" @@ -90,19 +92,7 @@ void myMessageHandler(QtMsgType type, const QMessageLogContext&, const QString& break; } - wchar_t* localAppDataFolder; - std::filesystem::path path; - if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) { - path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); - } - else { - path = std::filesystem::path(localAppDataFolder).parent_path(); - } - - path /= "Roaming"; - path /= "GlosSI"; - if (!std::filesystem::exists(path)) - std::filesystem::create_directories(path); + auto path = util::path::getDataDirPath(); QFile outFile(QString::fromStdWString(path) + "/glossiconfig.log"); outFile.open(QIODevice::WriteOnly | QIODevice::Append); @@ -120,18 +110,7 @@ int main(int argc, char* argv[]) #endif if (argc < 3) { - wchar_t* localAppDataFolder; - std::filesystem::path path; - if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) { - path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); - } - else { - path = std::filesystem::path(localAppDataFolder).parent_path(); - } - path /= "Roaming"; - path /= "GlosSI"; - if (!std::filesystem::exists(path)) - std::filesystem::create_directories(path); + auto path = util::path::getDataDirPath(); QFile outFile(QString::fromStdWString(path) + "/glossiconfig.log"); outFile.open(QIODevice::WriteOnly); diff --git a/GlosSITarget/AppLauncher.cpp b/GlosSITarget/AppLauncher.cpp index 140a28d..30f5fc1 100644 --- a/GlosSITarget/AppLauncher.cpp +++ b/GlosSITarget/AppLauncher.cpp @@ -27,13 +27,13 @@ limitations under the License. #pragma comment(lib, "Shell32.lib") #endif -#include "Settings.h" +#include "..\common\Settings.h" #include #include "Overlay.h" -#include "UnhookUtil.h" -#include "util.h" +#include "../common/UnhookUtil.h" +#include "../common/util.h" AppLauncher::AppLauncher( std::vector& process_hwnds, @@ -75,10 +75,10 @@ void AppLauncher::launchApp(const std::wstring& path, const std::wstring& args) if (ImGui::Begin("Launched Processes")) { ImGui::BeginChild("Inner##LaunchedProcs", {0.f, ImGui::GetItemRectSize().y - 64}, true); std::ranges::for_each(pids_, [](DWORD pid) { - ImGui::Text("%s | %d", std::wstring_convert>().to_bytes(glossi_util::GetProcName(pid)).c_str(), pid); + ImGui::Text("%s | %d", util::string::to_string(util::win::process::GetProcName(pid)).c_str(), pid); ImGui::SameLine(); if (ImGui::Button((" Kill ##" + std::to_string(pid)).c_str())) { - glossi_util::KillProcess(pid); + util::win::process::KillProcess(pid); } }); ImGui::EndChild(); @@ -101,7 +101,7 @@ void AppLauncher::update() getChildPids(pids_[0]); } if (!IsProcessRunning(pids_[0])) { - spdlog::info(L"Launched App \"{}\" with PID \"{}\" died", glossi_util::GetProcName(pids_[0]), pids_[0]); + spdlog::info(L"Launched App \"{}\" with PID \"{}\" died", util::win::process::GetProcName(pids_[0]), pids_[0]); if (Settings::launch.closeOnExit && !Settings::launch.waitForChildProcs && Settings::launch.launch) { spdlog::info("Configured to close on exit. Shutting down..."); shutdown_(); @@ -117,12 +117,12 @@ void AppLauncher::update() } const auto running = IsProcessRunning(pid); if (!running) - spdlog::trace(L"Child process \"{}\" with PID \"{}\" died", glossi_util::GetProcName(pid), pid); + spdlog::trace(L"Child process \"{}\" with PID \"{}\" died", util::win::process::GetProcName(pid), pid); return !running; }); auto filtered_pids = pids_ | std::ranges::views::filter([](DWORD pid) { - return std::ranges::find(Settings::launch.launcherProcesses, glossi_util::GetProcName(pid)) == Settings::launch.launcherProcesses.end(); + return std::ranges::find(Settings::launch.launcherProcesses, util::win::process::GetProcName(pid)) == Settings::launch.launcherProcesses.end(); }); if (has_extra_launchers_ && !filtered_pids.empty()) { launcher_has_launched_game_ = true; @@ -169,7 +169,7 @@ std::vector AppLauncher::launchedPids() [](DWORD pid) { return std::ranges::find( Settings::launch.launcherProcesses, - glossi_util::GetProcName(pid)) == Settings::launch.launcherProcesses.end(); + util::win::process::GetProcName(pid)) == Settings::launch.launcherProcesses.end(); })) { res.push_back(pid); } @@ -217,7 +217,7 @@ void AppLauncher::getChildPids(DWORD parent_pid) if (pe.th32ParentProcessID == parent_pid) { if (std::ranges::find(pids_, pe.th32ProcessID) == pids_.end()) { if (Settings::common.extendedLogging) { - spdlog::info(L"Found new child process \"{}\" with PID \"{}\"", glossi_util::GetProcName(pe.th32ProcessID), pe.th32ProcessID); + spdlog::info(L"Found new child process \"{}\" with PID \"{}\"", util::win::process::GetProcName(pe.th32ProcessID), pe.th32ProcessID); } pids_.push_back(pe.th32ProcessID); getChildPids(pe.th32ProcessID); @@ -263,7 +263,7 @@ void AppLauncher::getProcessHwnds() #ifdef _WIN32 bool AppLauncher::findLauncherPids() { - if (const auto pid = glossi_util::PidByName(L"EpicGamesLauncher.exe")) { + if (const auto pid = util::win::process::PidByName(L"EpicGamesLauncher.exe")) { spdlog::debug("Found EGS-Launcher running"); pids_.push_back(pid); return true; @@ -417,7 +417,7 @@ void AppLauncher::launchURL(const std::wstring& url, const std::wstring& args, c spdlog::debug("Epic Games launch; Couldn't find egs launcher PID"); pid_mutex_.lock(); - const auto pid = glossi_util::PidByName(L"EpicGamesLauncher.exe"); + const auto pid = util::win::process::PidByName(L"EpicGamesLauncher.exe"); if (!findLauncherPids()) { spdlog::debug("Did not find EGS-Launcher not running, retrying later..."); } diff --git a/GlosSITarget/DllInjector.h b/GlosSITarget/DllInjector.h index 09e4b50..764d181 100644 --- a/GlosSITarget/DllInjector.h +++ b/GlosSITarget/DllInjector.h @@ -1,10 +1,8 @@ #pragma once -#include -#include #include -#include "util.h" +#include "../common/util.h" namespace DllInjector { @@ -112,7 +110,7 @@ inline bool findModule(DWORD pid, std::wstring& lib_path, HMODULE& hMod) inline void injectDllInto(std::filesystem::path dllPath, const std::wstring& processName) { if (std::filesystem::exists(dllPath)) { - const auto explorer_pid = glossi_util::PidByName(processName); + const auto explorer_pid = util::win::process::PidByName(processName); if (explorer_pid != 0) { if (DllInjector::TakeDebugPrivilege()) { // No need to eject, as the dll is self-ejecting. diff --git a/GlosSITarget/GlosSITarget.vcxproj b/GlosSITarget/GlosSITarget.vcxproj index 4970649..a11bfc3 100644 --- a/GlosSITarget/GlosSITarget.vcxproj +++ b/GlosSITarget/GlosSITarget.vcxproj @@ -168,6 +168,8 @@ + + @@ -187,7 +189,6 @@ - @@ -195,7 +196,6 @@ - @@ -204,7 +204,6 @@ - @@ -213,13 +212,10 @@ - - - diff --git a/GlosSITarget/GlosSITarget.vcxproj.filters b/GlosSITarget/GlosSITarget.vcxproj.filters index b2bcd12..ddb734e 100644 --- a/GlosSITarget/GlosSITarget.vcxproj.filters +++ b/GlosSITarget/GlosSITarget.vcxproj.filters @@ -51,9 +51,6 @@ Source Files - - Source Files - Source Files @@ -114,10 +111,13 @@ Source Files\tray - + Source Files - + + Source Files + + Source Files @@ -137,9 +137,6 @@ Header Files - - Header Files - Header Files @@ -161,9 +158,6 @@ Header Files - - Header Files - Header Files @@ -182,12 +176,6 @@ Header Files - - Header Files - - - Header Files - Header Files diff --git a/GlosSITarget/HttpServer.cpp b/GlosSITarget/HttpServer.cpp index 790e2a3..37798e7 100644 --- a/GlosSITarget/HttpServer.cpp +++ b/GlosSITarget/HttpServer.cpp @@ -19,7 +19,7 @@ limitations under the License. #include #include "AppLauncher.h" -#include "Settings.h" +#include "..\common\Settings.h" HttpServer::HttpServer(AppLauncher& app_launcher, std::function close) : app_launcher_(app_launcher), close_(close) { diff --git a/GlosSITarget/InputRedirector.cpp b/GlosSITarget/InputRedirector.cpp index 20efb5b..7a8dc3d 100644 --- a/GlosSITarget/InputRedirector.cpp +++ b/GlosSITarget/InputRedirector.cpp @@ -20,7 +20,7 @@ limitations under the License. #include #include "Overlay.h" -#include "Settings.h" +#include "..\common\Settings.h" InputRedirector::InputRedirector() { diff --git a/GlosSITarget/Overlay.cpp b/GlosSITarget/Overlay.cpp index 5fbe97a..44e0581 100644 --- a/GlosSITarget/Overlay.cpp +++ b/GlosSITarget/Overlay.cpp @@ -17,13 +17,11 @@ limitations under the License. #include #include -#include -#include #include #include #include "Roboto.h" -#include "Settings.h" +#include "..\common\Settings.h" #include "GlosSI_logo.h" #include "../version.hpp" @@ -52,23 +50,11 @@ Overlay::Overlay( ImGui::SFML::UpdateFontTexture(); #ifdef _WIN32 - wchar_t* localAppDataFolder; - std::filesystem::path config_path; - if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) { - config_path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); - } - else { - config_path = std::filesystem::path(localAppDataFolder).parent_path(); - } - - config_path /= "Roaming"; - config_path /= "GlosSI"; - if (!std::filesystem::exists(config_path)) - std::filesystem::create_directories(config_path); + auto config_path = util::path::getDataDirPath(); config_path /= "imgui.ini"; // This assumes that char is utf8 and wchar_t is utf16, which is guaranteed on Windows. - config_file_name_ = std::wstring_convert>().to_bytes(config_path.wstring()); + config_file_name_ = util::string::to_string(config_path.wstring()); io.IniFilename = config_file_name_.data(); #endif diff --git a/GlosSITarget/Resource.rc b/GlosSITarget/Resource.rc index edcf04e..29f06bd 100644 --- a/GlosSITarget/Resource.rc +++ b/GlosSITarget/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,0,2045006300001 - PRODUCTVERSION 0,1,0,2045006300001 + FILEVERSION 0,1,2,0010004309958 + PRODUCTVERSION 0,1,2,0010004309958 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - SteamTarget" - VALUE "FileVersion", "0.1.0.2-45-g63fdab1" + VALUE "FileVersion", "0.1.2.0-10-g43c9958" VALUE "InternalName", "GlosSITarget" VALUE "LegalCopyright", "Copyright (C) 2021-2022 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSITarget.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.0.2-45-g63fdab1" + VALUE "ProductVersion", "0.1.2.0-10-g43c9958" END END BLOCK "VarFileInfo" @@ -220,6 +220,126 @@ IDI_ICON1 ICON "..\\GlosSI_Icon.ico" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GlosSITarget/Settings.h b/GlosSITarget/Settings.h deleted file mode 100644 index 896eae9..0000000 --- a/GlosSITarget/Settings.h +++ /dev/null @@ -1,336 +0,0 @@ -/* -Copyright 2021-2023 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. -*/ -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -#ifdef WIN32 -#define NOMINMAX -#include -#include -#include -#endif - -namespace Settings { - -inline struct Launch { - bool launch = false; - std::wstring launchPath; - std::wstring launchAppArgs; - bool closeOnExit = true; - bool waitForChildProcs = true; - bool isUWP = false; - bool ignoreLauncher = true; - bool killLauncher = false; - std::vector launcherProcesses{}; -} launch; - -inline struct Devices { - bool hideDevices = true; - bool realDeviceIds = false; -} devices; - -inline struct Window { - bool windowMode = false; - int maxFps = 0; - float scale = 0.f; - bool disableOverlay = false; - bool hideAltTab = true; - bool disableGlosSIOverlay = false; -} window; - -inline struct Controller { - int maxControllers = 1; - bool allowDesktopConfig = false; - bool emulateDS4 = false; -} controller; - -inline struct Common { - bool no_uwp_overlay = false; - bool disable_watchdog = false; - bool extendedLogging = false; - std::wstring name; - std::wstring icon; - int version; - std::wstring steamPath; - std::wstring steamUserId; - std::wstring standaloneModeGameId; /* = L"12605636929694728192"; */ - bool standaloneUseGamepadUI = false; -} common; - -inline std::filesystem::path settings_path_ = ""; - -inline bool checkIsUwp(const std::wstring& launch_path) -{ - if (launch_path.find(L"://") != std::wstring::npos) { - return false; - } - std::wsmatch m; - if (!std::regex_search(launch_path, m, std::wregex(L"^.{1,5}:"))) { - return true; - } - return false; -} - -#ifdef WIN32 -inline bool isWin10 = false; - -typedef LONG NTSTATUS, *PNTSTATUS; -#define STATUS_SUCCESS (0x00000000) - -typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); - -inline RTL_OSVERSIONINFOW GetRealOSVersion() -{ - HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); - if (hMod) { - RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); - if (fxPtr != nullptr) { - RTL_OSVERSIONINFOW rovi = {0}; - rovi.dwOSVersionInfoSize = sizeof(rovi); - if (STATUS_SUCCESS == fxPtr(&rovi)) { - return rovi; - } - } - } - RTL_OSVERSIONINFOW rovi = {0}; - return rovi; -} - -inline void checkWinVer() -{ - auto VN = GetRealOSVersion(); - isWin10 = VN.dwBuildNumber < 22000; - - if (isWin10) { - spdlog::info("Running on Windows 10; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber); - } - else { - spdlog::info("Running on Windows 11; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber); - } -} -#endif - -inline void Parse(const nlohmann::basic_json<>& json) -{ - auto safeParseValue = [](const auto& object, const auto& key, auto& value) { - try { - if (object.is_null() || object.empty() || object.at(key).empty() || object.at(key).is_null()) { - return; - } - value = object[key]; - } - catch (const nlohmann::json::exception& e) { - e.id == 403 - ? spdlog::trace("Err parsing \"{}\"; {}", key, e.what()) - : spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); - } - catch (const std::exception& e) { - spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); - } - }; - - auto safeWStringParse = [&safeParseValue](const auto& object, const auto& key, std::wstring& value) { - std::string meh; - safeParseValue(object, key, meh); - if (!meh.empty()) { - // This assumes that char is utf8 and wchar_t is utf16, which is guaranteed on Windows. - value = std::wstring_convert>().from_bytes(meh); - } - }; - - int version; - safeParseValue(json, "version", version); - if (version != 1) { // TODO: versioning stuff - spdlog::warn("Config version doesn't match application version."); - } - - // TODO: make this as much generic as fits in about the same amount of code if one would parse every value separately. - try { - if (auto launchconf = json["launch"]; !launchconf.is_null() && !launchconf.empty() && launchconf.is_object()) { - safeParseValue(launchconf, "launch", launch.launch); - safeWStringParse(launchconf, "launchPath", launch.launchPath); - safeWStringParse(launchconf, "launchAppArgs", launch.launchAppArgs); - safeParseValue(launchconf, "closeOnExit", launch.closeOnExit); - safeParseValue(launchconf, "waitForChildProcs", launch.waitForChildProcs); - safeParseValue(launchconf, "killLauncher", launch.killLauncher); - safeParseValue(launchconf, "ignoreLauncher", launch.ignoreLauncher); - - if (auto launcherProcs = launchconf["launcherProcesses"]; - !launcherProcs.is_null() && !launcherProcs.empty() && launcherProcs.is_array()) { - launch.launcherProcesses.clear(); - launch.launcherProcesses.reserve(launcherProcs.size()); - for (auto& proc : launcherProcs) { - launch.launcherProcesses.push_back(std::wstring_convert>().from_bytes(proc)); - } - } - } - - if (auto devconf = json["devices"]; !devconf.is_null() && !devconf.empty() && devconf.is_object()) { - safeParseValue(devconf, "hideDevices", devices.hideDevices); - safeParseValue(devconf, "realDeviceIds", devices.realDeviceIds); - } - - if (auto winconf = json["window"]; !winconf.is_null() && !winconf.empty() && winconf.is_object()) { - safeParseValue(winconf, "windowMode", window.windowMode); - safeParseValue(winconf, "maxFps", window.maxFps); - safeParseValue(winconf, "scale", window.scale); - safeParseValue(winconf, "disableOverlay", window.disableOverlay); - safeParseValue(winconf, "hideAltTab", window.hideAltTab); - safeParseValue(winconf, "disableGlosSIOverlay", window.disableGlosSIOverlay); - } - - if (auto controllerConf = json["controller"]; !controllerConf.is_null() && !controllerConf.empty() && controllerConf.is_object()) { - safeParseValue(controllerConf, "maxControllers", controller.maxControllers); - safeParseValue(controllerConf, "allowDesktopConfig", controller.allowDesktopConfig); - safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4); - } - safeParseValue(json, "extendedLogging", common.extendedLogging); - safeWStringParse(json, "name", common.name); - safeWStringParse(json, "icon", common.icon); - safeParseValue(json, "version", common.version); - - safeWStringParse(json, "steamPath", common.steamPath); - safeWStringParse(json, "steamUserId", common.steamUserId); - - safeWStringParse(json, "standaloneModeGameId", common.standaloneModeGameId); - safeParseValue(json, "standaloneUseGamepadUI", common.standaloneUseGamepadUI); - } - catch (const nlohmann::json::exception& e) { - spdlog::warn("Err parsing config: {}", e.what()); - } - catch (const std::exception& e) { - spdlog::warn("Err parsing config: {}", e.what()); - } - if (launch.launch) { - launch.isUWP = checkIsUwp(launch.launchPath); - } -} - -inline void Parse(const std::vector& args) -{ - std::wstring configName; - for (const auto& arg : args) { - if (arg.empty()) { - continue; - } - if (arg == L"-disableuwpoverlay") { - common.no_uwp_overlay = true; - } - else if (arg == L"-disablewatchdog") { - common.disable_watchdog = true; - } - else if (arg == L"-ignorelauncher") { - launch.ignoreLauncher = true; - } - else if (arg == L"-window") { - window.windowMode = true; - } - else { - configName += L" " + std::wstring(arg.begin(), arg.end()); - } - } - if (!configName.empty()) { - if (configName[0] == L' ') { - configName.erase(configName.begin()); - } - if (!configName.ends_with(L".json")) { - configName += L".json"; - } - } - wchar_t* localAppDataFolder; - std::filesystem::path path; - if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) { - path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); - } - else { - path = std::filesystem::path(localAppDataFolder).parent_path(); - } - - path /= "Roaming"; - path /= "GlosSI"; - if (!configName.empty()) { - path /= "Targets"; - path /= configName; - } - else { - spdlog::info("No config file specified, using default"); - path /= "default.json"; - } - - std::ifstream json_file; - json_file.open(path); - if (!json_file.is_open()) { - spdlog::error(L"Couldn't open settings file {}", path.wstring()); - spdlog::debug(L"Using sane defaults..."); - return; - } - settings_path_ = path; - const auto& json = nlohmann::json::parse(json_file); - Parse(json); - - spdlog::debug("Read config file \"{}\"; config: {}", path.string(), json.dump()); - json_file.close(); -} - -inline nlohmann::json toJson() -{ - nlohmann::json json; - json["version"] = 1; - json["launch"]["launch"] = launch.launch; - json["launch"]["launchPath"] = std::wstring_convert>().to_bytes(launch.launchPath); - json["launch"]["launchAppArgs"] = std::wstring_convert>().to_bytes(launch.launchAppArgs); - json["launch"]["closeOnExit"] = launch.closeOnExit; - json["launch"]["waitForChildProcs"] = launch.waitForChildProcs; - json["devices"]["hideDevices"] = devices.hideDevices; - json["devices"]["realDeviceIds"] = devices.realDeviceIds; - json["window"]["windowMode"] = window.windowMode; - json["window"]["maxFps"] = window.maxFps; - json["window"]["scale"] = window.scale; - json["window"]["disableOverlay"] = window.disableOverlay; - json["window"]["hideAltTab"] = window.hideAltTab; - json["controller"]["maxControllers"] = controller.maxControllers; - json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig; - json["controller"]["emulateDS4"] = controller.emulateDS4; - - json["extendedLogging"] = common.extendedLogging; - json["name"] = std::wstring_convert>().to_bytes(common.name); - json["icon"] = std::wstring_convert>().to_bytes(common.icon); - json["version"] = common.version; - return json; -} - -inline void StoreSettings() -{ - const auto& json = toJson(); - - std::ofstream json_file; - json_file.open(settings_path_); - if (!json_file.is_open()) { - spdlog::error(L"Couldn't open settings file {}", settings_path_.wstring()); - return; - } - json_file << json.dump(4); - json_file.close(); -} - -} // namespace Settings diff --git a/GlosSITarget/SteamOverlayDetector.cpp b/GlosSITarget/SteamOverlayDetector.cpp index 0b25c61..72ffd87 100644 --- a/GlosSITarget/SteamOverlayDetector.cpp +++ b/GlosSITarget/SteamOverlayDetector.cpp @@ -17,7 +17,7 @@ limitations under the License. #include -#include "Settings.h" +#include "..\common\Settings.h" #ifdef _WIN32 #define NOMINMAX diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 2e86a40..2c4f338 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -15,7 +15,7 @@ limitations under the License. */ #include "SteamTarget.h" -#include "Settings.h" +#include "..\common\Settings.h" #include "steam_sf_keymap.h" #include @@ -65,6 +65,7 @@ int SteamTarget::run() auto closeBPM = false; auto closeBPMTimer = sf::Clock{}; if (!SteamOverlayDetector::IsSteamInjected()) { + return 1; spdlog::warn("GlosSI not launched via Steam.\nEnabling EXPERIMENTAL global controller and overlay..."); if (Settings::common.standaloneModeGameId == L"") { spdlog::error("No game id set for standalone mode. Controller will use desktop-config!"); diff --git a/GlosSITarget/SteamTarget.h b/GlosSITarget/SteamTarget.h index 735a547..c4220b9 100644 --- a/GlosSITarget/SteamTarget.h +++ b/GlosSITarget/SteamTarget.h @@ -21,7 +21,7 @@ limitations under the License. #include "TargetWindow.h" #ifdef _WIN32 -#include "HidHide.h" +#include "../common/HidHide.h" #include "InputRedirector.h" #include #endif diff --git a/GlosSITarget/TargetWindow.cpp b/GlosSITarget/TargetWindow.cpp index 53ebe65..71e4295 100644 --- a/GlosSITarget/TargetWindow.cpp +++ b/GlosSITarget/TargetWindow.cpp @@ -30,7 +30,7 @@ limitations under the License. #include "ProcessPriority.h" -#include "Settings.h" +#include "..\common\Settings.h" #if !defined(WM_DPICHANGED) #define WM_DPICHANGED 0x02E0 diff --git a/GlosSITarget/UWPOverlayEnabler.h b/GlosSITarget/UWPOverlayEnabler.h index 1aab02d..c580ba6 100644 --- a/GlosSITarget/UWPOverlayEnabler.h +++ b/GlosSITarget/UWPOverlayEnabler.h @@ -5,7 +5,6 @@ #include "DllInjector.h" #include "Overlay.h" -#include "util.h" namespace UWPOverlayEnabler { diff --git a/GlosSITarget/UnhookUtil.h b/GlosSITarget/UnhookUtil.h deleted file mode 100644 index 04f2b33..0000000 --- a/GlosSITarget/UnhookUtil.h +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright 2021-2023 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. -*/ -#pragma once - -#define NOMINMAX -#include -#include - -#include -#include - -namespace UnhookUtil { -void UnPatchHook(const std::string& name, HMODULE module); - -std::string ReadOriginalBytes(const std::string& name, const std::wstring& moduleName); - -static inline const std::vector JUMP_INSTR_OPCODES = { - 0xE9, - 0xE8, - 0xEB, - 0xEA, - 0xFF}; - -// Valve Hooks various functions and hides Gaming devices like this. -// To be able to query them, unpatch the hook with the original bytes... - -// Bytes here are just fallbacks; originalbytes will get read from GlosSIConfig and stored in %APPDATA%\GlosSI\unhook_bytes - -// 22000 ^= Windows build number -static inline const std::map UNHOOK_BYTES_ORIGINAL_22000 = { - {"SetupDiEnumDeviceInfo", "\x48\x89\x5C\x24\x08"}, - {"SetupDiGetClassDevsW", "\x48\x89\x5C\x24\x08"}, - {"HidD_GetPreparsedData", "\x48\x89\x5C\x24\x18"}, - {"HidP_GetCaps", "\x4C\x8B\xD1\x48\x85\xC9"}, - {"HidD_GetAttributes", "\x40\x53\x48\x83\xEC"}, - {"HidD_GetProductString", "\x48\x83\xEC\x48\x48"}, - {"HidP_GetUsages", "\x4C\x89\x4C\x24\x20"}, - {"HidP_GetData", "\x4C\x89\x44\x24\x18"}, - {"HidP_GetValueCaps", "\x48\x83\xEC\x48\x49"}, - {"HidP_GetUsageValue", "\x40\x53\x55\x56\x48"}, - {"HidP_GetButtonCaps", "\x48\x83\xEC\x48\x49"}, - // Valve hooks "CreateProcess" to detect child-processes - {"CreateProcessW", "\x4C\x8B\xDC\x48\x83"}, -}; - -// SetupApi.dll is different on Win10 than on Win11 -static inline const std::map UNHOOK_BYTES_ORIGINAL_WIN10 = { - {"SetupDiEnumDeviceInfo", "\x40\x53\x56\x57\x41\x54\x41\x55"}, - {"SetupDiGetClassDevsW", "\x48\x8B\xC4\x48\x89\x58\x08"}, -}; - - -} // namespace UnhookUtil diff --git a/GlosSITarget/main.cpp b/GlosSITarget/main.cpp index 71e609f..ef40546 100644 --- a/GlosSITarget/main.cpp +++ b/GlosSITarget/main.cpp @@ -30,7 +30,7 @@ limitations under the License. #include "SteamTarget.h" #include "OverlayLogSink.h" -#include "Settings.h" +#include "..\common\Settings.h" #include #include "../version.hpp" @@ -71,19 +71,7 @@ LONG Win32FaultHandler(struct _EXCEPTION_POINTERS* ExInfo) MINIDUMP_EXCEPTION_INFORMATION M; HANDLE hDump_File; - wchar_t* localAppDataFolder; - std::filesystem::path path; - if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) { - path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); - } - else { - path = std::filesystem::path(localAppDataFolder).parent_path(); - } - - path /= "Roaming"; - path /= "GlosSI"; - if (!std::filesystem::exists(path)) - std::filesystem::create_directories(path); + auto path = util::path::getDataDirPath(); path /= "glossitarget.dmp"; M.ThreadId = GetCurrentThreadId(); @@ -127,19 +115,7 @@ int main(int argc, char* argv[]) const auto console_sink = std::make_shared(); console_sink->set_level(spdlog::level::trace); #ifdef _WIN32 - wchar_t* localAppDataFolder; - std::filesystem::path path; - if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) { - path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); - } - else { - path = std::filesystem::path(localAppDataFolder).parent_path(); - } - - path /= "Roaming"; - path /= "GlosSI"; - if (!std::filesystem::exists(path)) - std::filesystem::create_directories(path); + auto path = util::path::getDataDirPath(); path /= "glossitarget.log"; // For "path.wstring()" to be usable here, SPDLOG_WCHAR_FILENAMES must be defined. const auto file_sink = std::make_shared(path.wstring(), true); diff --git a/GlosSITarget/util.h b/GlosSITarget/util.h deleted file mode 100644 index 1644f8b..0000000 --- a/GlosSITarget/util.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -Copyright 2021-2023 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. -*/ -#pragma once -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#include - -namespace glossi_util { - -inline DWORD PidByName(const std::wstring& name) -{ - PROCESSENTRY32 entry; - entry.dwSize = sizeof(PROCESSENTRY32); - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); - if (Process32First(snapshot, &entry) == TRUE) { - while (Process32Next(snapshot, &entry) == TRUE) { - if (std::wstring(entry.szExeFile).find(name) != std::string::npos) { - return entry.th32ProcessID; - } - } - } - CloseHandle(snapshot); - return 0; -} - -inline std::wstring GetProcName(DWORD pid) -{ - PROCESSENTRY32 processInfo; - processInfo.dwSize = sizeof(processInfo); - const HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); - if (processesSnapshot == INVALID_HANDLE_VALUE) { - spdlog::trace("util::GetProcName: can't get a process snapshot"); - return L""; - } - - for (BOOL bok = Process32First(processesSnapshot, &processInfo); - bok; - bok = Process32Next(processesSnapshot, &processInfo)) { - if (pid == processInfo.th32ProcessID) { - CloseHandle(processesSnapshot); - return processInfo.szExeFile; - } - } - CloseHandle(processesSnapshot); - return L""; -} - -inline bool KillProcess(DWORD pid) -{ - auto res = true; - if (const auto proc = OpenProcess(PROCESS_TERMINATE, FALSE, pid)) { - spdlog::debug("Terminating process: {}", pid); - res = TerminateProcess(proc, 0); - if (!res) { - spdlog::error("Failed to terminate process: {}", pid); - } - CloseHandle(proc); - } - return res; -} - -} // namespace glossi_util diff --git a/GlosSIWatchdog/GlosSIWatchdog.vcxproj b/GlosSIWatchdog/GlosSIWatchdog.vcxproj index 01a1dbb..8e27f62 100644 --- a/GlosSIWatchdog/GlosSIWatchdog.vcxproj +++ b/GlosSIWatchdog/GlosSIWatchdog.vcxproj @@ -145,8 +145,8 @@ - - + + diff --git a/GlosSIWatchdog/GlosSIWatchdog.vcxproj.filters b/GlosSIWatchdog/GlosSIWatchdog.vcxproj.filters index 367978c..9e476d6 100644 --- a/GlosSIWatchdog/GlosSIWatchdog.vcxproj.filters +++ b/GlosSIWatchdog/GlosSIWatchdog.vcxproj.filters @@ -18,10 +18,10 @@ Source Files - + Source Files - + Source Files diff --git a/GlosSIWatchdog/dllmain.cpp b/GlosSIWatchdog/dllmain.cpp index d154509..f885b1c 100644 --- a/GlosSIWatchdog/dllmain.cpp +++ b/GlosSIWatchdog/dllmain.cpp @@ -15,10 +15,9 @@ limitations under the License. */ #include -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX -#include -#include + +#include "../common/util.h" + #include @@ -29,9 +28,8 @@ limitations under the License. #include #include "../version.hpp" -#include "../GlosSITarget/Settings.h" -#include "../GlosSITarget/HidHide.h" -#include "../GlosSITarget/util.h" +#include "../common/Settings.h" +#include "../common/HidHide.h" bool IsProcessRunning(DWORD pid) { @@ -66,20 +64,7 @@ void fetchSettings(httplib::Client& http_client, int retried_count = 0) { DWORD WINAPI watchdog(HMODULE hModule) { - wchar_t* localAppDataFolder; - std::filesystem::path configDirPath; - if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) { - configDirPath = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); - } - else { - configDirPath = std::filesystem::path(localAppDataFolder).parent_path(); - } - - configDirPath /= "Roaming"; - configDirPath /= "GlosSI"; - if (!std::filesystem::exists(configDirPath)) - std::filesystem::create_directories(configDirPath); - + auto configDirPath = util::path::getDataDirPath(); auto logPath = configDirPath; logPath /= "GlosSIWatchdog.log"; const auto file_sink = std::make_shared(logPath.wstring(), true); @@ -143,7 +128,7 @@ DWORD WINAPI watchdog(HMODULE hModule) } if (IsProcessRunning(pid)) { - glossi_util::KillProcess(pid); + util::win::process::KillProcess(pid); } else { diff --git a/Installer/Installer.nsi b/Installer/Installer.nsi index c0a11bb..65c96aa 100644 --- a/Installer/Installer.nsi +++ b/Installer/Installer.nsi @@ -3,7 +3,7 @@ !define APP_NAME "GlosSI" !define COMP_NAME "Peter Repukat - Flatspotsoftware" !define WEB_SITE "https://glossi.flatspot.pictures/" -!define VERSION "0.0.9.1-48-geb4ae9c" +!define VERSION "0.1.2.0-10-g43c9958" !define COPYRIGHT "Peter Repukat - FlatspotSoftware © 2017-2022" !define DESCRIPTION "SteamInput compatibility tool" !define INSTALLER_NAME "GlosSI-Installer.exe" @@ -193,3 +193,42 @@ SectionEnd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/UWPOverlayEnablerDLL/dllmain.cpp b/UWPOverlayEnablerDLL/dllmain.cpp index 497ff4a..9ee6065 100644 --- a/UWPOverlayEnablerDLL/dllmain.cpp +++ b/UWPOverlayEnablerDLL/dllmain.cpp @@ -44,7 +44,8 @@ There are two (known to me, at time of writing) ways to get a working overlay fo #define WIN32_LEAN_AND_MEAN #include -#include + +#include "../common/util.h" #define SUBHOOK_STATIC #include @@ -141,22 +142,7 @@ BOOL APIENTRY DllMain( HMODULE hModule, { if (ul_reason_for_call == DLL_PROCESS_ATTACH) { - wchar_t* localAppDataFolder; - std::filesystem::path configDirPath; - if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) { - configDirPath = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); - } - else { - configDirPath = std::filesystem::path(localAppDataFolder).parent_path(); - } - - configDirPath /= "Roaming"; - configDirPath /= "GlosSI"; - if (!std::filesystem::exists(configDirPath)) - std::filesystem::create_directories(configDirPath); - - - + auto configDirPath = util::path::getDataDirPath(); auto logPath = configDirPath; logPath /= "UWPOverlayEnabler.log"; const auto file_sink = std::make_shared(logPath.string(), true); diff --git a/GlosSITarget/HidHide.cpp b/common/HidHide.cpp similarity index 78% rename from GlosSITarget/HidHide.cpp rename to common/HidHide.cpp index 4b9cbb3..a901932 100644 --- a/GlosSITarget/HidHide.cpp +++ b/common/HidHide.cpp @@ -21,6 +21,8 @@ limitations under the License. #include "HidHide.h" #include +#define SPDLOG_WCHAR_TO_UTF8_SUPPORT +#define SPDLOG_WCHAR_FILENAMES #include #include @@ -29,7 +31,7 @@ limitations under the License. #include #ifndef WATCHDOG -#include "Overlay.h" +#include "../GlosSITarget/Overlay.h" #endif #include "Settings.h" @@ -41,7 +43,7 @@ limitations under the License. #include -#include "UnhookUtil.h" +#include "../common/UnhookUtil.h" #pragma comment(lib, "Setupapi.lib") @@ -52,7 +54,7 @@ DEFINE_GUID(GUID_DEVINTERFACE_XUSB, 0xEC87F1E3, 0xC13B, 0x4100, 0xB5, 0xF7, 0x8B // {00000000-0000-0000-FFFF-FFFFFFFFFFFF} the system container id DEFINE_GUID(GUID_CONTAINER_ID_SYSTEM, 0x00000000, 0x0000, 0x0000, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF); -HidHide::HidHide(){}; +HidHide::HidHide() {}; void HidHide::openCtrlDevice() { @@ -85,7 +87,7 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path) spdlog::info("Hiding devices is disabled; Not un-patching valve hooks, not looking for HidHide"); return; } - + spdlog::debug("Setting up device hiding..."); UnPatchValveHooks(); @@ -112,12 +114,12 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path) for (const auto& exe : whitelist_executeables_) { auto path = std::regex_replace(steam_path_string, std::wregex(L"(.:)(\\/|\\\\)"), dos_device + L"\\"); - path = std::regex_replace(path, std::wregex(L"\\/"), L"\\") + L"\\" + std::wstring{exe}; + path = std::regex_replace(path, std::wregex(L"\\/"), L"\\") + L"\\" + std::wstring{ exe }; if (std::ranges::none_of(whitelist, [&path](auto ep) { // make copy! - auto p = path; // non-const(!) copy of path - std::ranges::transform(path, p.begin(), tolower); - std::ranges::transform(ep, ep.begin(), tolower); - return p == ep; + auto p = path; // non-const(!) copy of path + std::ranges::transform(path, p.begin(), tolower); + std::ranges::transform(ep, ep.begin(), tolower); + return p == ep; })) { whitelist.push_back(path); } @@ -125,7 +127,7 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path) if (Settings::common.extendedLogging) { std::ranges::for_each(whitelist, [](const auto& exe) { spdlog::trace(L"Whitelisted executable: {}", exe); - }); + }); } setAppWhiteList(whitelist); @@ -133,13 +135,13 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path) if (Settings::common.extendedLogging) { std::ranges::for_each(avail_devices_, [](const auto& dev) { spdlog::trace(L"AvailDevice device: {}", dev.name); - }); + }); } blacklisted_devices_ = getBlackListDevices(); for (const auto& dev : avail_devices_) { if (std::ranges::none_of(blacklisted_devices_, [&dev](const auto& blackdev) { - return blackdev == dev.device_instance_path || blackdev == dev.base_container_device_instance_path; + return blackdev == dev.device_instance_path || blackdev == dev.base_container_device_instance_path; })) { // Valve emulated gamepad PID/VID; mirrord by ViGEm if (!(dev.vendor_id == 0x28de && (dev.product_id == 0x11FF || dev.product_id == 0x028E))) { @@ -160,7 +162,7 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path) if (Settings::common.extendedLogging) { std::ranges::for_each(blacklisted_devices_, [](const auto& dev) { spdlog::trace(L"Blacklisted device: {}", dev); - }); + }); } } closeCtrlDevice(); @@ -199,80 +201,81 @@ void HidHide::enableOverlayElement() { Overlay::AddOverlayElem([this](bool window_has_focus, ImGuiID dockspace_id) { ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver); - if (ImGui::Begin("Hidden Devices")) { - if (device_hiding_setup_) { - if (window_has_focus && (overlay_elem_clock_.getElapsedTime().asSeconds() > OVERLAY_ELEM_REFRESH_INTERVAL_S_)) { - // UnPatchValveHooks(); - openCtrlDevice(); - bool hidehide_state_store = hidhide_active_; - if (Settings::common.extendedLogging) { - spdlog::debug("Refreshing HID devices"); - } - if (hidhide_active_) { - setActive(false); - } - avail_devices_ = GetHidDeviceList(); - if (Settings::common.extendedLogging) { - std::ranges::for_each(avail_devices_, [](const auto& dev) { - spdlog::trace(L"AvailDevice device: {}", dev.name); + if (ImGui::Begin("Hidden Devices")) { + if (device_hiding_setup_) { + if (window_has_focus && (overlay_elem_clock_.getElapsedTime().asSeconds() > OVERLAY_ELEM_REFRESH_INTERVAL_S_)) { + // UnPatchValveHooks(); + openCtrlDevice(); + bool hidehide_state_store = hidhide_active_; + if (Settings::common.extendedLogging) { + spdlog::debug("Refreshing HID devices"); + } + if (hidhide_active_) { + setActive(false); + } + avail_devices_ = GetHidDeviceList(); + if (Settings::common.extendedLogging) { + std::ranges::for_each(avail_devices_, [](const auto& dev) { + spdlog::trace(L"AvailDevice device: {}", dev.name); }); - } - blacklisted_devices_ = getBlackListDevices(); - if (hidehide_state_store && Settings::devices.hideDevices) { - setActive(true); - } - closeCtrlDevice(); - overlay_elem_clock_.restart(); } - ImGui::BeginChild("Inner", {0.f, ImGui::GetItemRectSize().y - 64}, true); - std::ranges::for_each(avail_devices_, [this](const auto& device) { - std::string label = (std::string(device.name.begin(), std::ranges::find(device.name, L'\0')) + "##" + std::string(device.device_instance_path.begin(), device.device_instance_path.end())); - const auto findDeviceFn = [&device](const auto& blackdev) { - return device.device_instance_path == blackdev || device.base_container_device_instance_path == blackdev; - }; - bool hidden = std::ranges::find_if(blacklisted_devices_, findDeviceFn) != blacklisted_devices_.end(); - if (ImGui::Checkbox(label.data(), &hidden)) { - openCtrlDevice(); - if (hidden) { - if (std::ranges::none_of(blacklisted_devices_, findDeviceFn)) { - if (!device.device_instance_path.empty()) { - blacklisted_devices_.push_back(device.device_instance_path); - } - if (!device.device_instance_path.empty()) { - blacklisted_devices_.push_back(device.base_container_device_instance_path); - } - } - } - else { - blacklisted_devices_.erase(std::ranges::remove_if(blacklisted_devices_, findDeviceFn).begin(), - blacklisted_devices_.end()); + blacklisted_devices_ = getBlackListDevices(); + if (hidehide_state_store && Settings::devices.hideDevices) { + setActive(true); + } + closeCtrlDevice(); + overlay_elem_clock_.restart(); + } + ImGui::BeginChild("Inner", { 0.f, ImGui::GetItemRectSize().y - 64 }, true); + std::ranges::for_each(avail_devices_, [this](const auto& device) { + std::string label = (std::string(device.name.begin(), std::ranges::find(device.name, L'\0')) + "##" + std::string(device.device_instance_path.begin(), device.device_instance_path.end())); + const auto findDeviceFn = [&device](const auto& blackdev) { + return device.device_instance_path == blackdev || device.base_container_device_instance_path == blackdev; + }; + bool hidden = std::ranges::find_if(blacklisted_devices_, findDeviceFn) != blacklisted_devices_.end(); + if (ImGui::Checkbox(label.data(), &hidden)) { + openCtrlDevice(); + if (hidden) { + if (std::ranges::none_of(blacklisted_devices_, findDeviceFn)) { + if (!device.device_instance_path.empty()) { + blacklisted_devices_.push_back(device.device_instance_path); } - setBlacklistDevices(blacklisted_devices_); - if (Settings::common.extendedLogging) { - std::ranges::for_each(blacklisted_devices_, [](const auto& dev) { - spdlog::trace(L"Blacklisted device: {}", dev); - }); + if (!device.device_instance_path.empty()) { + blacklisted_devices_.push_back(device.base_container_device_instance_path); } - closeCtrlDevice(); } - }); - ImGui::EndChild(); - } else { - ImGui::Text("Enable \"Hide Devices\" to see a list of gaming-devices"); - } - if (ImGui::Checkbox("Hide devices", &Settings::devices.hideDevices)) { - if (!device_hiding_setup_) { - hideDevices(steam_path_); } - if (hidhide_active_ != Settings::devices.hideDevices) { - openCtrlDevice(); - setActive(Settings::devices.hideDevices); - closeCtrlDevice(); + else { + blacklisted_devices_.erase(std::ranges::remove_if(blacklisted_devices_, findDeviceFn).begin(), + blacklisted_devices_.end()); } + setBlacklistDevices(blacklisted_devices_); + if (Settings::common.extendedLogging) { + std::ranges::for_each(blacklisted_devices_, [](const auto& dev) { + spdlog::trace(L"Blacklisted device: {}", dev); + }); + } + closeCtrlDevice(); + } + }); + ImGui::EndChild(); + } + else { + ImGui::Text("Enable \"Hide Devices\" to see a list of gaming-devices"); + } + if (ImGui::Checkbox("Hide devices", &Settings::devices.hideDevices)) { + if (!device_hiding_setup_) { + hideDevices(steam_path_); + } + if (hidhide_active_ != Settings::devices.hideDevices) { + openCtrlDevice(); + setActive(Settings::devices.hideDevices); + closeCtrlDevice(); } } - ImGui::End(); - }); + } + ImGui::End(); + }); } #endif @@ -280,7 +283,7 @@ std::wstring HidHide::DosDeviceForVolume(const std::wstring& volume) { std::vector buffer(UNICODE_STRING_MAX_CHARS); QueryDosDeviceW(volume.c_str(), buffer.data(), static_cast(buffer.size())); - return {buffer.data()}; + return { buffer.data() }; } std::vector HidHide::getAppWhiteList() const @@ -291,7 +294,7 @@ std::vector HidHide::getAppWhiteList() const } std::vector buffer(bytes_needed); if (!DeviceIoControl( - hidhide_handle, static_cast(IOCTL_TYPE::GET_WHITELIST), nullptr, 0, buffer.data(), static_cast(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) { + hidhide_handle, static_cast(IOCTL_TYPE::GET_WHITELIST), nullptr, 0, buffer.data(), static_cast(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) { spdlog::error("Couldn't retrieve HidHide Whitelist"); return std::vector{}; } @@ -306,7 +309,7 @@ std::vector HidHide::getBlackListDevices() const } std::vector buffer(bytes_needed); if (!DeviceIoControl( - hidhide_handle, static_cast(IOCTL_TYPE::GET_BLACKLIST), nullptr, 0, buffer.data(), static_cast(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) { + hidhide_handle, static_cast(IOCTL_TYPE::GET_BLACKLIST), nullptr, 0, buffer.data(), static_cast(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) { spdlog::error("Couldn't retrieve HidHide Blacklist"); return std::vector{}; } @@ -318,7 +321,7 @@ bool HidHide::getActive() DWORD bytes_needed; BOOLEAN res; if (!DeviceIoControl( - hidhide_handle, static_cast(IOCTL_TYPE::GET_ACTIVE), nullptr, 0, &res, sizeof(BOOLEAN), &bytes_needed, nullptr)) { + hidhide_handle, static_cast(IOCTL_TYPE::GET_ACTIVE), nullptr, 0, &res, sizeof(BOOLEAN), &bytes_needed, nullptr)) { spdlog::error("Couldn't retrieve HidHide State"); return false; } @@ -331,7 +334,7 @@ void HidHide::setAppWhiteList(const std::vector& whitelist) const DWORD bytes_needed; auto buffer = StringListToMultiString(whitelist); if (!DeviceIoControl( - hidhide_handle, static_cast(IOCTL_TYPE::SET_WHITELIST), buffer.data(), static_cast(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) { + hidhide_handle, static_cast(IOCTL_TYPE::SET_WHITELIST), buffer.data(), static_cast(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) { spdlog::error("Couldn't set HidHide WhiteList"); } } @@ -341,7 +344,7 @@ void HidHide::setBlacklistDevices(const std::vector& blacklist) co DWORD bytes_needed; auto buffer = StringListToMultiString(blacklist); if (!DeviceIoControl( - hidhide_handle, static_cast(IOCTL_TYPE::SET_BLACKLIST), buffer.data(), static_cast(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) { + hidhide_handle, static_cast(IOCTL_TYPE::SET_BLACKLIST), buffer.data(), static_cast(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) { spdlog::error("Couldn't set HidHide BlackList"); } } @@ -350,7 +353,7 @@ void HidHide::setActive(bool active) { DWORD bytes_needed; if (!DeviceIoControl( - hidhide_handle, static_cast(IOCTL_TYPE::SET_ACTIVE), &active, sizeof(BOOLEAN), nullptr, 0, &bytes_needed, nullptr)) { + hidhide_handle, static_cast(IOCTL_TYPE::SET_ACTIVE), &active, sizeof(BOOLEAN), nullptr, 0, &bytes_needed, nullptr)) { spdlog::error("Couldn't set HidHide State"); return; } @@ -394,9 +397,9 @@ std::vector HidHide::StringListToMultiString(const std::vector{}, [](auto acc, const auto& curr) { acc.insert(acc.end(), curr.begin(), curr.end()); - acc.push_back(L'\0'); - return acc; - }); + acc.push_back(L'\0'); + return acc; + }); res.push_back(L'\0'); return res; } @@ -426,7 +429,7 @@ std::vector HidHide::GetHidDeviceList() std::ranges::remove_if( device_instance_paths, [](const auto& dev) { return !DevicePresent(dev); }) - .begin(), + .begin(), device_instance_paths.end()); GUID hid_device_interface_guid{}; @@ -443,7 +446,7 @@ std::vector HidHide::GetHidDeviceList() std::ranges::remove_if( res, [](const auto& dev) { return !dev.gaming_device; }) - .begin(), + .begin(), res.end()); return res; @@ -504,8 +507,8 @@ HidHide::SmallHidInfo HidHide::GetDeviceInfo(const DeviceInstancePath& instance_ std::wstring buffer; buffer.resize(127 * sizeof WCHAR); res.name = (HidD_GetProductString(device_object.get(), buffer.data(), static_cast(sizeof(WCHAR) * buffer.size())) - ? buffer - : L""); + ? buffer + : L""); for (size_t i = 0; i < res.name.size(); ++i) { if (res.name[i] == L'\0') { res.name.resize(i + 1); @@ -566,13 +569,13 @@ std::filesystem::path HidHide::SymbolicLink(GUID const& interface_guid, DeviceIn std::vector buffer(needed); // Acquire the detailed data containing the symbolic link (aka. device path) - auto& [cbSize, DevicePath]{*reinterpret_cast(buffer.data())}; + auto& [cbSize, DevicePath] {*reinterpret_cast(buffer.data())}; cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W); if (!SetupDiGetDeviceInterfaceDetailW(handle.get(), &device_interface_data, reinterpret_cast(buffer.data()), static_cast(buffer.size()), nullptr, nullptr)) { spdlog::error(L"Couldn't get Device interface details; device: {}", instance_path); return {}; } - return {std::wstring(DevicePath)}; + return { std::wstring(DevicePath) }; } HidHide::DeviceInstancePath HidHide::BaseContainerDeviceInstancePath(DeviceInstancePath const& device_instance_path) @@ -580,7 +583,7 @@ HidHide::DeviceInstancePath HidHide::BaseContainerDeviceInstancePath(DeviceInsta const GUID base_container_id(BaseContainerId(device_instance_path)); if ((GUID_NULL == base_container_id) || (GUID_CONTAINER_ID_SYSTEM == base_container_id)) return (std::wstring{}); - for (auto it{device_instance_path};;) { + for (auto it{ device_instance_path };;) { if (const auto device_instance_path_parent = DeviceInstancePathParent(it); (base_container_id == BaseContainerId(device_instance_path_parent))) it = device_instance_path_parent; else @@ -597,7 +600,7 @@ GUID HidHide::BaseContainerId(DeviceInstancePath const& device_instance_path) DEVINST devInst{}; DEVPROPTYPE devPropType{}; GUID buffer{}; - ULONG needed{sizeof(buffer)}; + ULONG needed{ sizeof(buffer) }; if (const auto result = CM_Locate_DevNodeW(&devInst, const_cast(device_instance_path.c_str()), CM_LOCATE_DEVNODE_PHANTOM); (CR_SUCCESS != result)) { spdlog::error(L"Couldn't locate device DevNode; Device {}; Code: {}", device_instance_path, result); return {}; @@ -624,7 +627,7 @@ HidHide::DeviceInstancePath HidHide::DeviceInstancePathParent(DeviceInstancePath DEVINST dev_inst_parent{}; std::wstring res; res.resize(UNICODE_STRING_MAX_CHARS); - ULONG needed{static_cast(res.size())}; + ULONG needed{ static_cast(res.size()) }; if (const auto result = CM_Locate_DevNodeW(&dev_inst, const_cast(device_instance_path.c_str()), CM_LOCATE_DEVNODE_PHANTOM); (CR_SUCCESS != result)) { spdlog::error(L"Couldn't locate device DevNode; Device {}; Code: {}", device_instance_path, result); return {}; diff --git a/GlosSITarget/HidHide.h b/common/HidHide.h similarity index 97% rename from GlosSITarget/HidHide.h rename to common/HidHide.h index 20689f9..e42a331 100644 --- a/GlosSITarget/HidHide.h +++ b/common/HidHide.h @@ -34,7 +34,7 @@ limitations under the License. #endif class HidHide { - private: +private: using DeviceInstancePath = std::wstring; using SetupDiDestroyDeviceInfoListPtr = std::unique_ptr, decltype(&SetupDiDestroyDeviceInfoList)>; using CloseHandlePtr = std::unique_ptr, decltype(&CloseHandle)>; @@ -62,7 +62,7 @@ class HidHide { bool gaming_device = false; }; - public: +public: HidHide(); void openCtrlDevice(); @@ -72,7 +72,7 @@ class HidHide { void disableHidHide(); // TODO: MAYBE: restore hidhide state/lists when app closes. not only disable device_hiding - private: +private: HANDLE hidhide_handle = nullptr; std::filesystem::path steam_path_; @@ -89,12 +89,12 @@ class HidHide { std::vector blacklisted_devices_; std::vector avail_devices_; bool hidhide_active_ = false; - static constexpr int OVERLAY_ELEM_REFRESH_INTERVAL_S_ = 5; + static constexpr int OVERLAY_ELEM_REFRESH_INTERVAL_S_ = 5; static inline constexpr std::array whitelist_executeables_{ L"GameOverlayUI.exe", L"steam.exe", - L"streaming_client.exe"}; + L"streaming_client.exe" }; static [[nodiscard]] std::wstring DosDeviceForVolume(const std::wstring& volume); diff --git a/common/Settings.h b/common/Settings.h new file mode 100644 index 0000000..09bbe0c --- /dev/null +++ b/common/Settings.h @@ -0,0 +1,300 @@ +/* +Copyright 2021-2023 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. +*/ +#pragma once + +#include +#include +#include +#include +#include + +#ifdef WIN32 +#define NOMINMAX +#include +#endif + +#include "../common/nlohmann_json_wstring.h" +#include "../common/util.h" + + +namespace Settings { + + inline struct Launch { + bool launch = false; + std::wstring launchPath; + std::wstring launchAppArgs; + bool closeOnExit = true; + bool waitForChildProcs = true; + bool isUWP = false; + bool ignoreLauncher = true; + bool killLauncher = false; + std::vector launcherProcesses{}; + } launch; + + inline struct Devices { + bool hideDevices = true; + bool realDeviceIds = false; + } devices; + + inline struct Window { + bool windowMode = false; + int maxFps = 0; + float scale = 0.f; + bool disableOverlay = false; + bool hideAltTab = true; + bool disableGlosSIOverlay = false; + } window; + + inline struct Controller { + int maxControllers = 1; + bool allowDesktopConfig = false; + bool emulateDS4 = false; + } controller; + + inline struct Common { + bool no_uwp_overlay = false; + bool disable_watchdog = false; + bool extendedLogging = false; + std::wstring name; + std::wstring icon; + int version; + std::wstring steamPath; + std::wstring steamUserId; + std::wstring standaloneModeGameId; /* = L"12605636929694728192"; */ + bool standaloneUseGamepadUI = false; + } common; + + inline std::filesystem::path settings_path_ = ""; + + inline bool checkIsUwp(const std::wstring& launch_path) + { + if (launch_path.find(L"://") != std::wstring::npos) { + return false; + } + std::wsmatch m; + if (!std::regex_search(launch_path, m, std::wregex(L"^.{1,5}:"))) { + return true; + } + return false; + } + +#ifdef WIN32 + inline bool isWin10 = false; + + inline void checkWinVer() + { + auto VN = util::win::GetRealOSVersion(); + isWin10 = VN.dwBuildNumber < 22000; + + if (isWin10) { + spdlog::info("Running on Windows 10; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber); + } + else { + spdlog::info("Running on Windows 11; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber); + } + } +#endif + + inline void Parse(const nlohmann::basic_json<>& json) + { + constexpr auto safeParseValue = [](const auto & object, const auto & key, T & value) { + try { + if (object.is_null() || object.empty() || object.at(key).empty() || object.at(key).is_null()) { + return; + } + if constexpr (std::is_same_v) { + value = util::string::to_wstring(object[key].get()); + } + else { + value = object[key]; + } + } + catch (const nlohmann::json::exception& e) { + e.id == 403 + ? spdlog::trace("Err parsing \"{}\"; {}", key, e.what()) + : spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); + } + catch (const std::exception& e) { + spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); + } + }; + + int version; + safeParseValue(json, "version", version); + if (version != 1) { // TODO: versioning stuff + spdlog::warn("Config version doesn't match application version."); + } + + // TODO: make this as much generic as fits in about the same amount of code if one would parse every value separately. + try { + if (auto launchconf = json["launch"]; !launchconf.is_null() && !launchconf.empty() && launchconf.is_object()) { + safeParseValue(launchconf, "launch", launch.launch); + safeParseValue(launchconf, "launchPath", launch.launchPath); + safeParseValue(launchconf, "launchAppArgs", launch.launchAppArgs); + safeParseValue(launchconf, "closeOnExit", launch.closeOnExit); + safeParseValue(launchconf, "waitForChildProcs", launch.waitForChildProcs); + safeParseValue(launchconf, "killLauncher", launch.killLauncher); + safeParseValue(launchconf, "ignoreLauncher", launch.ignoreLauncher); + + if (auto launcherProcs = launchconf["launcherProcesses"]; + !launcherProcs.is_null() && !launcherProcs.empty() && launcherProcs.is_array()) { + launch.launcherProcesses.clear(); + launch.launcherProcesses.reserve(launcherProcs.size()); + for (auto& proc : launcherProcs) { + launch.launcherProcesses.push_back(util::string::to_wstring(proc)); + } + } + } + + if (auto devconf = json["devices"]; !devconf.is_null() && !devconf.empty() && devconf.is_object()) { + safeParseValue(devconf, "hideDevices", devices.hideDevices); + safeParseValue(devconf, "realDeviceIds", devices.realDeviceIds); + } + + if (auto winconf = json["window"]; !winconf.is_null() && !winconf.empty() && winconf.is_object()) { + safeParseValue(winconf, "windowMode", window.windowMode); + safeParseValue(winconf, "maxFps", window.maxFps); + safeParseValue(winconf, "scale", window.scale); + safeParseValue(winconf, "disableOverlay", window.disableOverlay); + safeParseValue(winconf, "hideAltTab", window.hideAltTab); + safeParseValue(winconf, "disableGlosSIOverlay", window.disableGlosSIOverlay); + } + + if (auto controllerConf = json["controller"]; !controllerConf.is_null() && !controllerConf.empty() && controllerConf.is_object()) { + safeParseValue(controllerConf, "maxControllers", controller.maxControllers); + safeParseValue(controllerConf, "allowDesktopConfig", controller.allowDesktopConfig); + safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4); + } + safeParseValue(json, "extendedLogging", common.extendedLogging); + safeParseValue(json, "name", common.name); + safeParseValue(json, "icon", common.icon); + safeParseValue(json, "version", common.version); + + safeParseValue(json, "steamPath", common.steamPath); + safeParseValue(json, "steamUserId", common.steamUserId); + + safeParseValue(json, "standaloneModeGameId", common.standaloneModeGameId); + safeParseValue(json, "standaloneUseGamepadUI", common.standaloneUseGamepadUI); + } + catch (const nlohmann::json::exception& e) { + spdlog::warn("Err parsing config: {}", e.what()); + } + catch (const std::exception& e) { + spdlog::warn("Err parsing config: {}", e.what()); + } + if (launch.launch) { + launch.isUWP = checkIsUwp(launch.launchPath); + } + } + + inline void Parse(const std::vector& args) + { + std::wstring configName; + for (const auto& arg : args) { + if (arg.empty()) { + continue; + } + if (arg == L"-disableuwpoverlay") { + common.no_uwp_overlay = true; + } + else if (arg == L"-disablewatchdog") { + common.disable_watchdog = true; + } + else if (arg == L"-ignorelauncher") { + launch.ignoreLauncher = true; + } + else if (arg == L"-window") { + window.windowMode = true; + } + else { + configName += L" " + std::wstring(arg.begin(), arg.end()); + } + } + if (!configName.empty()) { + if (configName[0] == L' ') { + configName.erase(configName.begin()); + } + if (!configName.ends_with(L".json")) { + configName += L".json"; + } + } + auto path = util::path::getDataDirPath(); + if (!configName.empty()) { + path /= "Targets"; + path /= configName; + } + else { + spdlog::info("No config file specified, using default"); + path /= "default.json"; + } + + std::ifstream json_file; + json_file.open(path); + if (!json_file.is_open()) { + spdlog::error(L"Couldn't open settings file {}", path.wstring()); + spdlog::debug(L"Using sane defaults..."); + return; + } + settings_path_ = path; + const auto& json = nlohmann::json::parse(json_file); + Parse(json); + + spdlog::debug("Read config file \"{}\"; config: {}", path.string(), json.dump()); + json_file.close(); + } + + inline nlohmann::json toJson() + { + nlohmann::json json; + json["version"] = 1; + json["launch"]["launch"] = launch.launch; + json["launch"]["launchPath"] = launch.launchPath; + json["launch"]["launchAppArgs"] = launch.launchAppArgs; + json["launch"]["closeOnExit"] = launch.closeOnExit; + json["launch"]["waitForChildProcs"] = launch.waitForChildProcs; + json["devices"]["hideDevices"] = devices.hideDevices; + json["devices"]["realDeviceIds"] = devices.realDeviceIds; + json["window"]["windowMode"] = window.windowMode; + json["window"]["maxFps"] = window.maxFps; + json["window"]["scale"] = window.scale; + json["window"]["disableOverlay"] = window.disableOverlay; + json["window"]["hideAltTab"] = window.hideAltTab; + json["controller"]["maxControllers"] = controller.maxControllers; + json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig; + json["controller"]["emulateDS4"] = controller.emulateDS4; + + json["extendedLogging"] = common.extendedLogging; + json["name"] = common.name; + json["icon"] = common.icon; + json["version"] = common.version; + return json; + } + + inline void StoreSettings() + { + const auto& json = toJson(); + + std::ofstream json_file; + json_file.open(settings_path_); + if (!json_file.is_open()) { + spdlog::error(L"Couldn't open settings file {}", settings_path_.wstring()); + return; + } + json_file << json.dump(4); + json_file.close(); + } + +} // namespace Settings diff --git a/GlosSITarget/UnhookUtil.cpp b/common/UnhookUtil.cpp similarity index 88% rename from GlosSITarget/UnhookUtil.cpp rename to common/UnhookUtil.cpp index e300a0c..f5d04d4 100644 --- a/GlosSITarget/UnhookUtil.cpp +++ b/common/UnhookUtil.cpp @@ -13,7 +13,9 @@ 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. */ -#include "UnhookUtil.h" +#include "../common/UnhookUtil.h" + +#include "util.h" #ifndef CONFIGAPP #include @@ -28,17 +30,7 @@ void UnhookUtil::UnPatchHook(const std::string& name, HMODULE module) std::map original_bytes_from_file; - wchar_t* localAppDataFolder; - std::filesystem::path configDirPath; - if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) { - configDirPath = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); - } - else { - configDirPath = std::filesystem::path(localAppDataFolder).parent_path(); - } - - configDirPath /= "Roaming"; - configDirPath /= "GlosSI"; + auto configDirPath = util::path::getDataDirPath(); if (std::filesystem::exists(configDirPath)) { auto unhook_file_path = configDirPath / "unhook_bytes"; if (std::filesystem::exists(unhook_file_path)) { @@ -56,7 +48,8 @@ void UnhookUtil::UnPatchHook(const std::string& name, HMODULE module) ifile.read(&buff, sizeof(char)); if (buff != ':') { funcName.push_back(buff); - } else { + } + else { char bytes[8]; ifile.read(bytes, sizeof(char) * 8); ifile.read(&buff, sizeof(char)); // newline diff --git a/common/UnhookUtil.h b/common/UnhookUtil.h new file mode 100644 index 0000000..6ed996c --- /dev/null +++ b/common/UnhookUtil.h @@ -0,0 +1,66 @@ +/* +Copyright 2021-2023 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. +*/ +#pragma once + +#define NOMINMAX +#include +#include + +#include +#include + +namespace UnhookUtil { + void UnPatchHook(const std::string& name, HMODULE module); + + std::string ReadOriginalBytes(const std::string& name, const std::wstring& moduleName); + + static inline const std::vector JUMP_INSTR_OPCODES = { + 0xE9, + 0xE8, + 0xEB, + 0xEA, + 0xFF }; + + // Valve Hooks various functions and hides Gaming devices like this. + // To be able to query them, unpatch the hook with the original bytes... + + // Bytes here are just fallbacks; originalbytes will get read from GlosSIConfig and stored in %APPDATA%\GlosSI\unhook_bytes + + // 22000 ^= Windows build number + static inline const std::map UNHOOK_BYTES_ORIGINAL_22000 = { + {"SetupDiEnumDeviceInfo", "\x48\x89\x5C\x24\x08"}, + {"SetupDiGetClassDevsW", "\x48\x89\x5C\x24\x08"}, + {"HidD_GetPreparsedData", "\x48\x89\x5C\x24\x18"}, + {"HidP_GetCaps", "\x4C\x8B\xD1\x48\x85\xC9"}, + {"HidD_GetAttributes", "\x40\x53\x48\x83\xEC"}, + {"HidD_GetProductString", "\x48\x83\xEC\x48\x48"}, + {"HidP_GetUsages", "\x4C\x89\x4C\x24\x20"}, + {"HidP_GetData", "\x4C\x89\x44\x24\x18"}, + {"HidP_GetValueCaps", "\x48\x83\xEC\x48\x49"}, + {"HidP_GetUsageValue", "\x40\x53\x55\x56\x48"}, + {"HidP_GetButtonCaps", "\x48\x83\xEC\x48\x49"}, + // Valve hooks "CreateProcess" to detect child-processes + {"CreateProcessW", "\x4C\x8B\xDC\x48\x83"}, + }; + + // SetupApi.dll is different on Win10 than on Win11 + static inline const std::map UNHOOK_BYTES_ORIGINAL_WIN10 = { + {"SetupDiEnumDeviceInfo", "\x40\x53\x56\x57\x41\x54\x41\x55"}, + {"SetupDiGetClassDevsW", "\x48\x8B\xC4\x48\x89\x58\x08"}, + }; + + +} // namespace UnhookUtil diff --git a/common/common.vcxproj b/common/common.vcxproj new file mode 100644 index 0000000..5e38350 --- /dev/null +++ b/common/common.vcxproj @@ -0,0 +1,151 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 16.0 + Win32Proj + {dfed4b7e-d04c-442b-bb48-5b6068a6b31b} + common + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Utility + true + v143 + Unicode + + + Utility + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + ..\deps\json\include;..\deps\spdlog\include;..\deps\SFML\include;..\deps\imgui;$(IncludePath) + + + ..\deps\json\include;..\deps\spdlog\include;..\deps\SFML\include;..\deps\imgui;$(IncludePath) + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + true + true + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + + + Console + true + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + stdcpp20 + + + Console + true + true + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/common/common.vcxproj.filters b/common/common.vcxproj.filters new file mode 100644 index 0000000..cd5e20b --- /dev/null +++ b/common/common.vcxproj.filters @@ -0,0 +1,42 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files + + + Source Files + + + \ No newline at end of file diff --git a/common/nlohmann_json_wstring.h b/common/nlohmann_json_wstring.h new file mode 100644 index 0000000..5fa1f82 --- /dev/null +++ b/common/nlohmann_json_wstring.h @@ -0,0 +1,36 @@ +/* +Copyright 2021-2023 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. +*/ + +#pragma once + + +#include + +#include "util.h" + + +namespace nlohmann { + template <> + struct adl_serializer { + static void to_json(json& j, const std::wstring& str) { + j = util::string::to_string(str); + } + + static void from_json(const json& j, std::wstring& str) { + str = util::string::to_wstring(j.get()); + } + }; +} diff --git a/common/util.h b/common/util.h new file mode 100644 index 0000000..52cadc6 --- /dev/null +++ b/common/util.h @@ -0,0 +1,163 @@ +/* +Copyright 2021-2023 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. +*/ + +#pragma once + +#include +#include + +#ifdef _WIN32 +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#include +#include +#include +#endif + +#include + +#ifdef SPDLOG_H +#include +#endif + +namespace util { + namespace string + { + template + inline std::wstring to_wstring(const T& t) + { + std::wstring_convert> converter; + return converter.from_bytes(t); + } + + template + inline std::string to_string(const T& t) + { + std::wstring_convert> converter; + return converter.to_bytes(t); + } + } + + namespace path + { + inline std::filesystem::path getDataDirPath() + { + wchar_t* localAppDataFolder; + std::filesystem::path path; + if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &localAppDataFolder) != S_OK) { + path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path(); + } + else { + path = std::filesystem::path(localAppDataFolder).parent_path(); + } + + path /= "Roaming"; + path /= "GlosSI"; + if (!std::filesystem::exists(path)) + std::filesystem::create_directories(path); + return path; + } + } + +#ifdef _WIN32 + namespace win + { + + typedef LONG NTSTATUS, * PNTSTATUS; +#define STATUS_SUCCESS (0x00000000) + + typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW); + + inline RTL_OSVERSIONINFOW GetRealOSVersion() + { + HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll"); + if (hMod) { + RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion"); + if (fxPtr != nullptr) { + RTL_OSVERSIONINFOW rovi = { 0 }; + rovi.dwOSVersionInfoSize = sizeof(rovi); + if (STATUS_SUCCESS == fxPtr(&rovi)) { + return rovi; + } + } + } + RTL_OSVERSIONINFOW rovi = { 0 }; + return rovi; + } + namespace process + { + inline DWORD PidByName(const std::wstring& name) + { + PROCESSENTRY32 entry; + entry.dwSize = sizeof(PROCESSENTRY32); + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + if (Process32First(snapshot, &entry) == TRUE) { + while (Process32Next(snapshot, &entry) == TRUE) { + if (std::wstring(entry.szExeFile).find(name) != std::string::npos) { + return entry.th32ProcessID; + } + } + } + CloseHandle(snapshot); + return 0; + } + + inline std::wstring GetProcName(DWORD pid) + { + PROCESSENTRY32 processInfo; + processInfo.dwSize = sizeof(processInfo); + const HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + if (processesSnapshot == INVALID_HANDLE_VALUE) { +#ifdef SPDLOG_H + spdlog::trace("util::GetProcName: can't get a process snapshot"); +#endif + return L""; + } + + for (BOOL bok = Process32First(processesSnapshot, &processInfo); + bok; + bok = Process32Next(processesSnapshot, &processInfo)) { + if (pid == processInfo.th32ProcessID) { + CloseHandle(processesSnapshot); + return processInfo.szExeFile; + } + } + CloseHandle(processesSnapshot); + return L""; + } + + inline bool KillProcess(DWORD pid) + { + auto res = true; + if (const auto proc = OpenProcess(PROCESS_TERMINATE, FALSE, pid)) { +#ifdef SPDLOG_H + spdlog::debug("Terminating process: {}", pid); +#endif + res = TerminateProcess(proc, 0); + if (!res) { +#ifdef SPDLOG_H + spdlog::error("Failed to terminate process: {}", pid); +#endif + } + CloseHandle(proc); + } + return res; + } + } + } +#endif + } \ No newline at end of file From f7df3deb7db3a1c0aa2ad05e7ba0a8a1ff17c689 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 16:43:01 +0100 Subject: [PATCH 09/65] Cleanup --- GlosSIConfig/Resource.rc | 1437 +-------------------------------- GlosSITarget/HttpServer.cpp | 5 +- GlosSITarget/Resource.rc | 261 +----- GlosSITarget/SteamTarget.cpp | 229 +----- GlosSITarget/SteamTarget.h | 27 +- Installer/Installer.nsi | 43 +- common/Settings.h | 535 ++++++------ common/UnhookUtil.cpp | 2 + common/common.vcxproj | 14 +- common/common.vcxproj.filters | 3 + common/steam_util.h | 181 +++++ common/util.h | 6 +- 12 files changed, 543 insertions(+), 2200 deletions(-) create mode 100644 common/steam_util.h diff --git a/GlosSIConfig/Resource.rc b/GlosSIConfig/Resource.rc index b01172c..1f1efe3 100644 --- a/GlosSIConfig/Resource.rc +++ b/GlosSIConfig/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,2,0010004309958 - PRODUCTVERSION 0,1,2,0010004309958 + FILEVERSION 0,1,2,0011001030052 + PRODUCTVERSION 0,1,2,0011001030052 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - Config" - VALUE "FileVersion", "0.1.2.0-10-g43c9958" + VALUE "FileVersion", "0.1.2.0-11-g1f3fb52" VALUE "InternalName", "GlosSIConfig" VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSIConfig.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.2.0-10-g43c9958" + VALUE "ProductVersion", "0.1.2.0-11-g1f3fb52" END END BLOCK "VarFileInfo" @@ -107,1432 +107,3 @@ IDI_ICON1 ICON "..\GlosSI_Icon.ico" ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/GlosSITarget/HttpServer.cpp b/GlosSITarget/HttpServer.cpp index 37798e7..ef0a49b 100644 --- a/GlosSITarget/HttpServer.cpp +++ b/GlosSITarget/HttpServer.cpp @@ -17,11 +17,12 @@ limitations under the License. #include #include +#include #include "AppLauncher.h" -#include "..\common\Settings.h" +#include "../common/Settings.h" -HttpServer::HttpServer(AppLauncher& app_launcher, std::function close) : app_launcher_(app_launcher), close_(close) +HttpServer::HttpServer(AppLauncher& app_launcher, std::function close) : app_launcher_(app_launcher), close_(std::move(close)) { } diff --git a/GlosSITarget/Resource.rc b/GlosSITarget/Resource.rc index 29f06bd..806d632 100644 --- a/GlosSITarget/Resource.rc +++ b/GlosSITarget/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,2,0010004309958 - PRODUCTVERSION 0,1,2,0010004309958 + FILEVERSION 0,1,2,0011001030052 + PRODUCTVERSION 0,1,2,0011001030052 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - SteamTarget" - VALUE "FileVersion", "0.1.2.0-10-g43c9958" + VALUE "FileVersion", "0.1.2.0-11-g1f3fb52" VALUE "InternalName", "GlosSITarget" VALUE "LegalCopyright", "Copyright (C) 2021-2022 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSITarget.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.2.0-10-g43c9958" + VALUE "ProductVersion", "0.1.2.0-11-g1f3fb52" END END BLOCK "VarFileInfo" @@ -107,256 +107,3 @@ IDI_ICON1 ICON "..\\GlosSI_Icon.ico" ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 2c4f338..30592e6 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -23,7 +23,6 @@ limitations under the License. #include #include #include -#include #ifdef _WIN32 #include "UWPOverlayEnabler.h" @@ -36,7 +35,7 @@ SteamTarget::SteamTarget() : window_( [this] { run_ = false; }, [this] { toggleGlossiOverlay(); }, - getScreenshotHotkey(), + util::steam::getScreenshotHotkey(steam_path_, steam_user_id_), [this]() { target_window_handle_ = window_.getSystemHandle(); overlay_ = window_.getOverlay(); @@ -65,26 +64,24 @@ int SteamTarget::run() auto closeBPM = false; auto closeBPMTimer = sf::Clock{}; if (!SteamOverlayDetector::IsSteamInjected()) { - return 1; spdlog::warn("GlosSI not launched via Steam.\nEnabling EXPERIMENTAL global controller and overlay..."); if (Settings::common.standaloneModeGameId == L"") { spdlog::error("No game id set for standalone mode. Controller will use desktop-config!"); } - SetEnvironmentVariable(L"SteamAppId", L"0"); SetEnvironmentVariable(L"SteamClientLaunch", L"0"); SetEnvironmentVariable(L"SteamEnv", L"1"); - SetEnvironmentVariable(L"SteamPath", getSteamPath().wstring().c_str()); + SetEnvironmentVariable(L"SteamPath", steam_path_.wstring().c_str()); SetEnvironmentVariable(L"SteamTenfoot", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); - //SetEnvironmentVariable(L"SteamTenfootHybrid", L"1"); + // SetEnvironmentVariable(L"SteamTenfootHybrid", L"1"); SetEnvironmentVariable(L"SteamGamepadUI", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); SetEnvironmentVariable(L"SteamGameId", Settings::common.standaloneModeGameId.c_str()); SetEnvironmentVariable(L"SteamOverlayGameId", Settings::common.standaloneModeGameId.c_str()); SetEnvironmentVariable(L"EnableConfiguratorSupport", L"15"); SetEnvironmentVariable(L"SteamStreamingForceWindowedD3D9", L"1"); - if (Settings::common.standaloneUseGamepadUI) { + if (Settings::common.standaloneUseGamepadUI) { system("start steam://open/bigpicture"); auto steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); auto timer = sf::Clock{}; @@ -94,22 +91,21 @@ int SteamTarget::run() } Sleep(6000); // DIRTY HACK to wait until BPM (GamepadUI) is initialized // TODO: find way to force BPM even if BPM is not active - LoadLibrary((getSteamPath() / "GameOverlayRenderer64.dll").wstring().c_str()); - - // Overlay switches back to desktop one, once BPM is closed... Disable closing BPM for now. - // TODO: find way to force BPM even if BPM is not active + LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str()); + + // Overlay switches back to desktop one, once BPM is closed... Disable closing BPM for now. + // TODO: find way to force BPM even if BPM is not active // closeBPM = true; closeBPMTimer.restart(); } else { - LoadLibrary( (getSteamPath() / "GameOverlayRenderer64.dll").wstring().c_str()); + LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str()); } window_.setClickThrough(true); if (!overlay_.expired()) overlay_.lock()->setEnabled(false); steam_overlay_present_ = true; - } else { spdlog::info("Steam-overlay detected."); @@ -130,7 +126,7 @@ int SteamTarget::run() } #endif } - getXBCRebindingEnabled(); + util::steam::getXBCRebindingEnabled(steam_path_, steam_user_id_); run_ = true; @@ -145,29 +141,7 @@ int SteamTarget::run() keepControllerConfig(true); -#ifdef _WIN32 - HICON icon = 0; - TCHAR path[MAX_PATH]; - GetModuleFileName(nullptr, path, MAX_PATH); - icon = (HICON)LoadImage( - 0, - path, - IMAGE_ICON, - GetSystemMetrics(SM_CXSMICON), - GetSystemMetrics(SM_CYSMICON), - LR_LOADFROMFILE | LR_LOADMAP3DCOLORS); - if (!icon) { - ExtractIconEx(path, 0, &icon, nullptr, 1); - } - Tray::Tray tray{"GlosSITarget", icon}; -#else - Tray::Tray tray{"GlosSITarget", "ico.png"}; -#endif - - tray.addEntry(Tray::Button{ - "Quit", [this, &tray]() { - run_ = false; - }}); + const auto tray = createTrayMenu(); server_.run(); @@ -176,9 +150,9 @@ int SteamTarget::run() overlayHotkeyWorkaround(); window_.update(); - if (closeBPM && closeBPMTimer.getElapsedTime().asSeconds() >= 3) { - system("start steam://close/bigpicture"); - closeBPM = false; + if (closeBPM && closeBPMTimer.getElapsedTime().asSeconds() >= 3) { + system("start steam://close/bigpicture"); + closeBPM = false; } // Wait on shutdown; User might get confused if window closes to fast if anything with launchApp get's borked. @@ -191,7 +165,7 @@ int SteamTarget::run() launcher_.update(); } } - tray.exit(); + tray->exit(); server_.stop(); #ifdef _WIN32 @@ -290,154 +264,11 @@ void SteamTarget::focusWindow(WindowHandle hndl) #endif } -std::filesystem::path SteamTarget::getSteamPath() const -{ -#ifdef _WIN32 - try { - // TODO: check if keys/value exist - // steam should always be open and have written reg values... - winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam"}; - const auto res = key.GetStringValue(L"SteamPath"); - spdlog::info(L"Detected Steam Path: {}", res); - return res; - } - catch (const winreg::RegException& e) { - spdlog::error("Couldn't get Steam path from Registry; {}", e.what()); - } - return Settings::common.steamPath; -#else - return L""; // TODO -#endif -} - -std::wstring SteamTarget::getSteamUserId() const -{ -#ifdef _WIN32 - try { - // TODO: check if keys/value exist - // steam should always be open and have written reg values... - winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess"}; - const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser")); - spdlog::info(L"Detected Steam UserId: {}", res); - return res; - } - catch (const winreg::RegException& e) { - spdlog::error("Couldn't get Steam path from Registry; {}", e.what()); - } - return Settings::common.steamUserId; -#else - return L""; // TODO -#endif -} - -std::vector SteamTarget::getOverlayHotkey() -{ - const auto config_path = std::wstring(steam_path_) + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_); - if (!std::filesystem::exists(config_path)) { - spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); - return {"Shift", "KEY_TAB"}; // default - } - std::ifstream config_file(config_path); - auto root = tyti::vdf::read(config_file); - - std::shared_ptr> children = root.childs["system"]; - if (!children || children->attribs.empty() || !children->attribs.contains("InGameOverlayShortcutKey")) { - spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab"); - return {"Shift", "KEY_TAB"}; // default - } - auto hotkeys = children->attribs.at("InGameOverlayShortcutKey"); - - // has anyone more than 4 keys to open overlay?! - std::smatch m; - if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) { - spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab"); - return {"Shift", "KEY_TAB"}; // default - } - - std::vector res; - for (auto i = 1; i < m.size(); i++) { - const auto s = std::string(m[i]); - if (!s.empty()) { - res.push_back(s); - } - } - if (res.empty()) { - spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab"); - return {"Shift", "KEY_TAB"}; // default - } - spdlog::info("Detected Overlay hotkey(s): {}", std::accumulate( - res.begin() + 1, res.end(), res[0], - [](auto acc, const auto curr) { return acc += "+" + curr; })); - return res; -} - -std::vector SteamTarget::getScreenshotHotkey() -{ - const auto config_path = std::wstring(steam_path_) + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_); - if (!std::filesystem::exists(config_path)) { - spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); - return {"KEY_F12"}; // default - } - std::ifstream config_file(config_path); - auto root = tyti::vdf::read(config_file); - - std::shared_ptr> children = root.childs["system"]; - if (!children || children->attribs.empty() || !children->attribs.contains("InGameOverlayScreenshotHotKey")) { - spdlog::warn("Couldn't detect overlay hotkey, using default: F12"); - return {"KEY_F12"}; // default - } - auto hotkeys = children->attribs.at("InGameOverlayScreenshotHotKey"); - - // has anyone more than 4 keys to screenshot?! - std::smatch m; - if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) { - spdlog::warn("Couldn't detect overlay hotkey, using default: F12"); - return {"KEY_F12"}; // default - } - - std::vector res; - for (auto i = 1; i < m.size(); i++) { - const auto s = std::string(m[i]); - if (!s.empty()) { - res.push_back(s); - } - } - if (res.empty()) { - spdlog::warn("Couldn't detect overlay hotkey, using default: F12"); - return {"KEY_F12"}; // default - } - spdlog::info("Detected screenshot hotkey(s): {}", std::accumulate( - res.begin() + 1, res.end(), res[0], - [](auto acc, const auto curr) { return acc += "+" + curr; })); - return res; -} - -bool SteamTarget::getXBCRebindingEnabled() -{ - const auto config_path = std::wstring(steam_path_) + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_); - if (!std::filesystem::exists(config_path)) { - spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); - return false; - } - std::ifstream config_file(config_path); - auto root = tyti::vdf::read(config_file); - - if (root.attribs.empty() || !root.attribs.contains("SteamController_XBoxSupport")) { - spdlog::warn("\"Xbox Configuration Support\" is disabled in Steam. This may cause doubled Inputs!"); - return false; - } - auto xbsup = root.attribs.at("SteamController_XBoxSupport"); - if (xbsup != "1") { - spdlog::warn("\"Xbox Configuration Support\" is disabled in Steam. This may cause doubled Inputs!"); - } - return xbsup == "1"; -} - /* * The "magic" that keeps a controller-config forced (without hooking into Steam) * * Hook into own process and detour "GetForegroundWindow" - * Deatour function always returns HWND of own application window + * Detour function always returns HWND of own application window * Steam now doesn't detect application changes and keeps the game-specific input config without reverting to desktop-conf */ void SteamTarget::keepControllerConfig(bool keep) @@ -488,6 +319,34 @@ HWND SteamTarget::keepFgWindowHookFn() } #endif +std::unique_ptr SteamTarget::createTrayMenu() +{ +#ifdef _WIN32 + HICON icon = 0; + TCHAR path[MAX_PATH]; + GetModuleFileName(nullptr, path, MAX_PATH); + icon = (HICON)LoadImage( + 0, + path, + IMAGE_ICON, + GetSystemMetrics(SM_CXSMICON), + GetSystemMetrics(SM_CYSMICON), + LR_LOADFROMFILE | LR_LOADMAP3DCOLORS); + if (!icon) { + ExtractIconEx(path, 0, &icon, nullptr, 1); + } + auto tray = std::make_unique("GlosSITarget", icon); +#else + auto tray = std::make_unique("GlosSITarget", "ico.png"); +#endif + + tray->addEntry(Tray::Button{ + "Quit", [this, &tray]() { + run_ = false; + }}); + return tray; +} + void SteamTarget::overlayHotkeyWorkaround() { static bool pressed = false; diff --git a/GlosSITarget/SteamTarget.h b/GlosSITarget/SteamTarget.h index c4220b9..0ce56ef 100644 --- a/GlosSITarget/SteamTarget.h +++ b/GlosSITarget/SteamTarget.h @@ -26,13 +26,17 @@ limitations under the License. #include #endif +#include + #include "AppLauncher.h" #include "Overlay.h" #include "HttpServer.h" +#include "../common/steam_util.h" -#include - +namespace Tray { +class Tray; +} class SteamTarget { public: explicit SteamTarget(); @@ -42,15 +46,9 @@ class SteamTarget { void onOverlayChanged(bool overlay_open); void toggleGlossiOverlay(); void focusWindow(WindowHandle hndl); - std::filesystem::path getSteamPath() const; - std::wstring getSteamUserId() const; - std::filesystem::path steam_path_ = getSteamPath(); - std::wstring steam_user_id_ = getSteamUserId(); - - std::vector getOverlayHotkey(); - std::vector getScreenshotHotkey(); - bool getXBCRebindingEnabled(); + std::filesystem::path steam_path_ = util::steam::getSteamPath(); + std::wstring steam_user_id_ = util::steam::getSteamUserId(); bool steam_overlay_present_ = false; @@ -65,6 +63,8 @@ class SteamTarget { static inline HWND last_real_hwnd_ = nullptr; #endif + std::unique_ptr createTrayMenu(); + /* * Run once per frame * detects steam configured overlay hotkey, and simulates key presses to window @@ -74,7 +74,7 @@ class SteamTarget { void overlayHotkeyWorkaround(); bool run_ = false; - std::vector overlay_hotkey_ = getOverlayHotkey(); + std::vector overlay_hotkey_ = util::steam::getOverlayHotkey(steam_path_, steam_user_id_); #ifdef _WIN32 HidHide hidhide_; @@ -94,9 +94,4 @@ class SteamTarget { bool delayed_shutdown_ = false; sf::Clock delay_shutdown_clock_; - - static constexpr std::wstring_view user_data_path_ = L"/userdata/"; - static constexpr std::wstring_view config_file_name_ = L"/config/localconfig.vdf"; - static constexpr std::string_view overlay_hotkey_name_ = "InGameOverlayShortcutKey "; - static constexpr std::string_view screenshot_hotkey_name_ = "InGameOverlayScreenshotHotKey "; }; diff --git a/Installer/Installer.nsi b/Installer/Installer.nsi index 65c96aa..80553ae 100644 --- a/Installer/Installer.nsi +++ b/Installer/Installer.nsi @@ -3,7 +3,7 @@ !define APP_NAME "GlosSI" !define COMP_NAME "Peter Repukat - Flatspotsoftware" !define WEB_SITE "https://glossi.flatspot.pictures/" -!define VERSION "0.1.2.0-10-g43c9958" +!define VERSION "0.1.2.0-11-g1f3fb52" !define COPYRIGHT "Peter Repukat - FlatspotSoftware © 2017-2022" !define DESCRIPTION "SteamInput compatibility tool" !define INSTALLER_NAME "GlosSI-Installer.exe" @@ -191,44 +191,3 @@ DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}" DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}" SectionEnd - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/common/Settings.h b/common/Settings.h index 09bbe0c..f0e4fa6 100644 --- a/common/Settings.h +++ b/common/Settings.h @@ -15,11 +15,13 @@ limitations under the License. */ #pragma once +#define SPDLOG_WCHAR_TO_UTF8_SUPPORT +#define SPDLOG_WCHAR_FILENAMES +#include #include #include #include #include -#include #ifdef WIN32 #define NOMINMAX @@ -32,269 +34,280 @@ limitations under the License. namespace Settings { - inline struct Launch { - bool launch = false; - std::wstring launchPath; - std::wstring launchAppArgs; - bool closeOnExit = true; - bool waitForChildProcs = true; - bool isUWP = false; - bool ignoreLauncher = true; - bool killLauncher = false; - std::vector launcherProcesses{}; - } launch; - - inline struct Devices { - bool hideDevices = true; - bool realDeviceIds = false; - } devices; - - inline struct Window { - bool windowMode = false; - int maxFps = 0; - float scale = 0.f; - bool disableOverlay = false; - bool hideAltTab = true; - bool disableGlosSIOverlay = false; - } window; - - inline struct Controller { - int maxControllers = 1; - bool allowDesktopConfig = false; - bool emulateDS4 = false; - } controller; - - inline struct Common { - bool no_uwp_overlay = false; - bool disable_watchdog = false; - bool extendedLogging = false; - std::wstring name; - std::wstring icon; - int version; - std::wstring steamPath; - std::wstring steamUserId; - std::wstring standaloneModeGameId; /* = L"12605636929694728192"; */ - bool standaloneUseGamepadUI = false; - } common; - - inline std::filesystem::path settings_path_ = ""; - - inline bool checkIsUwp(const std::wstring& launch_path) - { - if (launch_path.find(L"://") != std::wstring::npos) { - return false; - } - std::wsmatch m; - if (!std::regex_search(launch_path, m, std::wregex(L"^.{1,5}:"))) { - return true; - } - return false; - } + inline struct Launch { + bool launch = false; + std::wstring launchPath; + std::wstring launchAppArgs; + bool closeOnExit = true; + bool waitForChildProcs = true; + bool isUWP = false; + bool ignoreLauncher = true; + bool killLauncher = false; + std::vector launcherProcesses{}; + } launch; + + inline struct Devices { + bool hideDevices = true; + bool realDeviceIds = false; + } devices; + + inline struct Window { + bool windowMode = false; + int maxFps = 0; + float scale = 0.f; + bool disableOverlay = false; + bool hideAltTab = true; + bool disableGlosSIOverlay = false; + } window; + + inline struct Controller { + int maxControllers = 1; + bool allowDesktopConfig = false; + bool emulateDS4 = false; + } controller; + + inline struct Common { + bool no_uwp_overlay = false; + bool disable_watchdog = false; + bool extendedLogging = false; + std::wstring name; + std::wstring icon; + int version; + std::wstring steamPath; + std::wstring steamUserId; + std::wstring standaloneModeGameId; /* = L"12605636929694728192"; */ + bool standaloneUseGamepadUI = false; + bool allowStandAlone = true; + } common; + + inline const std::map> cmd_args = { + {L"-disableuwpoverlay", [&]() { common.no_uwp_overlay = true; }}, + {L"-disablewatchdog", [&]() { common.disable_watchdog = true; }}, + {L"-ignorelauncher", [&]() { launch.ignoreLauncher = true; }}, + {L"-window", [&]() { window.windowMode = true; }}, + {L"-extendedLogging", [&]() { common.extendedLogging = true; }}, + {L"-standaloneUseGamepadUI", [&]() { common.standaloneUseGamepadUI = true; }}, + {L"-disallowStandAlone", [&]() { common.allowStandAlone = false; }}, + }; + + inline std::filesystem::path settings_path_ = ""; + + inline bool checkIsUwp(const std::wstring& launch_path) + { + if (launch_path.find(L"://") != std::wstring::npos) { + return false; + } + std::wsmatch m; + if (!std::regex_search(launch_path, m, std::wregex(L"^.{1,5}:"))) { + return true; + } + return false; + } #ifdef WIN32 - inline bool isWin10 = false; - - inline void checkWinVer() - { - auto VN = util::win::GetRealOSVersion(); - isWin10 = VN.dwBuildNumber < 22000; - - if (isWin10) { - spdlog::info("Running on Windows 10; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber); - } - else { - spdlog::info("Running on Windows 11; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber); - } - } + inline bool isWin10 = false; + + inline void checkWinVer() + { + auto VN = util::win::GetRealOSVersion(); + isWin10 = VN.dwBuildNumber < 22000; + + if (isWin10) { + spdlog::info("Running on Windows 10; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber); + } + else { + spdlog::info("Running on Windows 11; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber); + } + } #endif - inline void Parse(const nlohmann::basic_json<>& json) - { - constexpr auto safeParseValue = [](const auto & object, const auto & key, T & value) { - try { - if (object.is_null() || object.empty() || object.at(key).empty() || object.at(key).is_null()) { - return; - } - if constexpr (std::is_same_v) { - value = util::string::to_wstring(object[key].get()); - } - else { - value = object[key]; - } - } - catch (const nlohmann::json::exception& e) { - e.id == 403 - ? spdlog::trace("Err parsing \"{}\"; {}", key, e.what()) - : spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); - } - catch (const std::exception& e) { - spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); - } - }; - - int version; - safeParseValue(json, "version", version); - if (version != 1) { // TODO: versioning stuff - spdlog::warn("Config version doesn't match application version."); - } - - // TODO: make this as much generic as fits in about the same amount of code if one would parse every value separately. - try { - if (auto launchconf = json["launch"]; !launchconf.is_null() && !launchconf.empty() && launchconf.is_object()) { - safeParseValue(launchconf, "launch", launch.launch); - safeParseValue(launchconf, "launchPath", launch.launchPath); - safeParseValue(launchconf, "launchAppArgs", launch.launchAppArgs); - safeParseValue(launchconf, "closeOnExit", launch.closeOnExit); - safeParseValue(launchconf, "waitForChildProcs", launch.waitForChildProcs); - safeParseValue(launchconf, "killLauncher", launch.killLauncher); - safeParseValue(launchconf, "ignoreLauncher", launch.ignoreLauncher); - - if (auto launcherProcs = launchconf["launcherProcesses"]; - !launcherProcs.is_null() && !launcherProcs.empty() && launcherProcs.is_array()) { - launch.launcherProcesses.clear(); - launch.launcherProcesses.reserve(launcherProcs.size()); - for (auto& proc : launcherProcs) { - launch.launcherProcesses.push_back(util::string::to_wstring(proc)); - } - } - } - - if (auto devconf = json["devices"]; !devconf.is_null() && !devconf.empty() && devconf.is_object()) { - safeParseValue(devconf, "hideDevices", devices.hideDevices); - safeParseValue(devconf, "realDeviceIds", devices.realDeviceIds); - } - - if (auto winconf = json["window"]; !winconf.is_null() && !winconf.empty() && winconf.is_object()) { - safeParseValue(winconf, "windowMode", window.windowMode); - safeParseValue(winconf, "maxFps", window.maxFps); - safeParseValue(winconf, "scale", window.scale); - safeParseValue(winconf, "disableOverlay", window.disableOverlay); - safeParseValue(winconf, "hideAltTab", window.hideAltTab); - safeParseValue(winconf, "disableGlosSIOverlay", window.disableGlosSIOverlay); - } - - if (auto controllerConf = json["controller"]; !controllerConf.is_null() && !controllerConf.empty() && controllerConf.is_object()) { - safeParseValue(controllerConf, "maxControllers", controller.maxControllers); - safeParseValue(controllerConf, "allowDesktopConfig", controller.allowDesktopConfig); - safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4); - } - safeParseValue(json, "extendedLogging", common.extendedLogging); - safeParseValue(json, "name", common.name); - safeParseValue(json, "icon", common.icon); - safeParseValue(json, "version", common.version); - - safeParseValue(json, "steamPath", common.steamPath); - safeParseValue(json, "steamUserId", common.steamUserId); - - safeParseValue(json, "standaloneModeGameId", common.standaloneModeGameId); - safeParseValue(json, "standaloneUseGamepadUI", common.standaloneUseGamepadUI); - } - catch (const nlohmann::json::exception& e) { - spdlog::warn("Err parsing config: {}", e.what()); - } - catch (const std::exception& e) { - spdlog::warn("Err parsing config: {}", e.what()); - } - if (launch.launch) { - launch.isUWP = checkIsUwp(launch.launchPath); - } - } - - inline void Parse(const std::vector& args) - { - std::wstring configName; - for (const auto& arg : args) { - if (arg.empty()) { - continue; - } - if (arg == L"-disableuwpoverlay") { - common.no_uwp_overlay = true; - } - else if (arg == L"-disablewatchdog") { - common.disable_watchdog = true; - } - else if (arg == L"-ignorelauncher") { - launch.ignoreLauncher = true; - } - else if (arg == L"-window") { - window.windowMode = true; - } - else { - configName += L" " + std::wstring(arg.begin(), arg.end()); - } - } - if (!configName.empty()) { - if (configName[0] == L' ') { - configName.erase(configName.begin()); - } - if (!configName.ends_with(L".json")) { - configName += L".json"; - } - } - auto path = util::path::getDataDirPath(); - if (!configName.empty()) { - path /= "Targets"; - path /= configName; - } - else { - spdlog::info("No config file specified, using default"); - path /= "default.json"; - } - - std::ifstream json_file; - json_file.open(path); - if (!json_file.is_open()) { - spdlog::error(L"Couldn't open settings file {}", path.wstring()); - spdlog::debug(L"Using sane defaults..."); - return; - } - settings_path_ = path; - const auto& json = nlohmann::json::parse(json_file); - Parse(json); - - spdlog::debug("Read config file \"{}\"; config: {}", path.string(), json.dump()); - json_file.close(); - } - - inline nlohmann::json toJson() - { - nlohmann::json json; - json["version"] = 1; - json["launch"]["launch"] = launch.launch; - json["launch"]["launchPath"] = launch.launchPath; - json["launch"]["launchAppArgs"] = launch.launchAppArgs; - json["launch"]["closeOnExit"] = launch.closeOnExit; - json["launch"]["waitForChildProcs"] = launch.waitForChildProcs; - json["devices"]["hideDevices"] = devices.hideDevices; - json["devices"]["realDeviceIds"] = devices.realDeviceIds; - json["window"]["windowMode"] = window.windowMode; - json["window"]["maxFps"] = window.maxFps; - json["window"]["scale"] = window.scale; - json["window"]["disableOverlay"] = window.disableOverlay; - json["window"]["hideAltTab"] = window.hideAltTab; - json["controller"]["maxControllers"] = controller.maxControllers; - json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig; - json["controller"]["emulateDS4"] = controller.emulateDS4; - - json["extendedLogging"] = common.extendedLogging; - json["name"] = common.name; - json["icon"] = common.icon; - json["version"] = common.version; - return json; - } - - inline void StoreSettings() - { - const auto& json = toJson(); - - std::ofstream json_file; - json_file.open(settings_path_); - if (!json_file.is_open()) { - spdlog::error(L"Couldn't open settings file {}", settings_path_.wstring()); - return; - } - json_file << json.dump(4); - json_file.close(); - } + inline void Parse(const nlohmann::basic_json<>& json) + { + constexpr auto safeParseValue = [](const auto & object, const auto & key, T & value) { + try { + if (object.is_null() || object.empty() || object.at(key).empty() || object.at(key).is_null()) { + return; + } + if constexpr (std::is_same_v) { + value = util::string::to_wstring(object[key].get()); + } + else { + value = object[key]; + } + } + catch (const nlohmann::json::exception& e) { + if constexpr (std::is_same_v) { + e.id == 403 + ? spdlog::trace("Err parsing \"{}\"; {}; Using default: {}", key, e.what(), util::string::to_string(value)) + : spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); + } + else + { + e.id == 403 + ? spdlog::trace("Err parsing \"{}\"; {}; Using default: {}", key, e.what(), value) + : spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); + } + } + catch (const std::exception& e) { + spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); + } + }; + + int version; + safeParseValue(json, "version", version); + if (version != 1) { // TODO: versioning stuff + spdlog::warn("Config version doesn't match application version."); + } + + // TODO: make this as much generic as fits in about the same amount of code if one would parse every value separately. + try { + if (const auto launchconf = json["launch"]; !launchconf.is_null() && !launchconf.empty() && launchconf.is_object()) { + safeParseValue(launchconf, "launch", launch.launch); + safeParseValue(launchconf, "launchPath", launch.launchPath); + safeParseValue(launchconf, "launchAppArgs", launch.launchAppArgs); + safeParseValue(launchconf, "closeOnExit", launch.closeOnExit); + safeParseValue(launchconf, "waitForChildProcs", launch.waitForChildProcs); + safeParseValue(launchconf, "killLauncher", launch.killLauncher); + safeParseValue(launchconf, "ignoreLauncher", launch.ignoreLauncher); + + if (const auto launcherProcs = launchconf["launcherProcesses"]; + !launcherProcs.is_null() && !launcherProcs.empty() && launcherProcs.is_array()) { + launch.launcherProcesses.clear(); + launch.launcherProcesses.reserve(launcherProcs.size()); + for (auto& proc : launcherProcs) { + launch.launcherProcesses.push_back(util::string::to_wstring(proc)); + } + } + } + + if (const auto devconf = json["devices"]; !devconf.is_null() && !devconf.empty() && devconf.is_object()) { + safeParseValue(devconf, "hideDevices", devices.hideDevices); + safeParseValue(devconf, "realDeviceIds", devices.realDeviceIds); + } + + if (const auto winconf = json["window"]; !winconf.is_null() && !winconf.empty() && winconf.is_object()) { + safeParseValue(winconf, "windowMode", window.windowMode); + safeParseValue(winconf, "maxFps", window.maxFps); + safeParseValue(winconf, "scale", window.scale); + safeParseValue(winconf, "disableOverlay", window.disableOverlay); + safeParseValue(winconf, "hideAltTab", window.hideAltTab); + safeParseValue(winconf, "disableGlosSIOverlay", window.disableGlosSIOverlay); + } + + if (const auto controllerConf = json["controller"]; !controllerConf.is_null() && !controllerConf.empty() && controllerConf.is_object()) { + safeParseValue(controllerConf, "maxControllers", controller.maxControllers); + safeParseValue(controllerConf, "allowDesktopConfig", controller.allowDesktopConfig); + safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4); + } + safeParseValue(json, "extendedLogging", common.extendedLogging); + safeParseValue(json, "name", common.name); + safeParseValue(json, "icon", common.icon); + safeParseValue(json, "version", common.version); + + safeParseValue(json, "steamPath", common.steamPath); + safeParseValue(json, "steamUserId", common.steamUserId); + + safeParseValue(json, "standaloneModeGameId", common.standaloneModeGameId); + safeParseValue(json, "standaloneUseGamepadUI", common.standaloneUseGamepadUI); + } + catch (const nlohmann::json::exception& e) { + spdlog::warn("Err parsing config: {}", e.what()); + } + catch (const std::exception& e) { + spdlog::warn("Err parsing config: {}", e.what()); + } + if (launch.launch) { + launch.isUWP = checkIsUwp(launch.launchPath); + } + } + + inline void Parse(const std::vector& args) + { + std::wstring configName; + for (const auto& arg : args) { + if (arg.empty()) { + continue; + } + if (cmd_args.contains(arg)) + { + cmd_args.at(arg)(); + } + else { + configName += L" " + std::wstring(arg.begin(), arg.end()); + } + } + if (!configName.empty()) { + if (configName[0] == L' ') { + configName.erase(configName.begin()); + } + if (!configName.ends_with(L".json")) { + configName += L".json"; + } + } + auto path = util::path::getDataDirPath(); + if (!configName.empty()) { + path /= "Targets"; + path /= configName; + } + else { + spdlog::info("No config file specified, using default"); + path /= "default.json"; + } + + std::ifstream json_file; + json_file.open(path); + if (!json_file.is_open()) { + spdlog::error(L"Couldn't open settings file {}", path.wstring()); + spdlog::debug(L"Using sane defaults..."); + return; + } + settings_path_ = path; + const auto& json = nlohmann::json::parse(json_file); + Parse(json); + + spdlog::debug("Read config file \"{}\"; config: {}", path.string(), json.dump()); + json_file.close(); + } + + inline nlohmann::json toJson() + { + nlohmann::json json; + json["version"] = 1; + json["launch"]["launch"] = launch.launch; + json["launch"]["launchPath"] = launch.launchPath; + json["launch"]["launchAppArgs"] = launch.launchAppArgs; + json["launch"]["closeOnExit"] = launch.closeOnExit; + json["launch"]["waitForChildProcs"] = launch.waitForChildProcs; + json["devices"]["hideDevices"] = devices.hideDevices; + json["devices"]["realDeviceIds"] = devices.realDeviceIds; + json["window"]["windowMode"] = window.windowMode; + json["window"]["maxFps"] = window.maxFps; + json["window"]["scale"] = window.scale; + json["window"]["disableOverlay"] = window.disableOverlay; + json["window"]["hideAltTab"] = window.hideAltTab; + json["controller"]["maxControllers"] = controller.maxControllers; + json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig; + json["controller"]["emulateDS4"] = controller.emulateDS4; + + json["extendedLogging"] = common.extendedLogging; + json["name"] = common.name; + json["icon"] = common.icon; + json["version"] = common.version; + return json; + } + + inline void StoreSettings() + { + const auto& json = toJson(); + + std::ofstream json_file; + json_file.open(settings_path_); + if (!json_file.is_open()) { + spdlog::error(L"Couldn't open settings file {}", settings_path_.wstring()); + return; + } + json_file << json.dump(4); + json_file.close(); + } } // namespace Settings diff --git a/common/UnhookUtil.cpp b/common/UnhookUtil.cpp index f5d04d4..1b77540 100644 --- a/common/UnhookUtil.cpp +++ b/common/UnhookUtil.cpp @@ -18,6 +18,8 @@ limitations under the License. #include "util.h" #ifndef CONFIGAPP +#define SPDLOG_WCHAR_TO_UTF8_SUPPORT +#define SPDLOG_WCHAR_FILENAMES #include #include "Settings.h" diff --git a/common/common.vcxproj b/common/common.vcxproj index 5e38350..cf0b1d2 100644 --- a/common/common.vcxproj +++ b/common/common.vcxproj @@ -1,5 +1,6 @@ + Debug @@ -71,10 +72,10 @@ - ..\deps\json\include;..\deps\spdlog\include;..\deps\SFML\include;..\deps\imgui;$(IncludePath) + ..\deps\json\include;..\deps\spdlog\include;..\deps\SFML\include;..\deps\imgui;..\deps\WinReg;..\deps\ValveFileVDF;$(IncludePath) - ..\deps\json\include;..\deps\spdlog\include;..\deps\SFML\include;..\deps\imgui;$(IncludePath) + ..\deps\json\include;..\deps\spdlog\include;..\deps\SFML\include;..\deps\imgui;..\deps\WinReg;..\deps\ValveFileVDF;$(IncludePath) @@ -138,6 +139,7 @@ + @@ -147,5 +149,13 @@ + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/common/common.vcxproj.filters b/common/common.vcxproj.filters index cd5e20b..aa49593 100644 --- a/common/common.vcxproj.filters +++ b/common/common.vcxproj.filters @@ -30,6 +30,9 @@ Header Files + + Header Files + diff --git a/common/steam_util.h b/common/steam_util.h new file mode 100644 index 0000000..43c60cb --- /dev/null +++ b/common/steam_util.h @@ -0,0 +1,181 @@ +/* +Copyright 2021-2023 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. +*/ +#pragma once + +#define SPDLOG_WCHAR_TO_UTF8_SUPPORT +#define SPDLOG_WCHAR_FILENAMES +#include +#include +#include + + +#include "util.h" +#include "Settings.h" + +namespace util +{ + namespace steam + { + + static constexpr std::wstring_view user_data_path = L"/userdata/"; + static constexpr std::wstring_view config_file_name = L"/config/localconfig.vdf"; + static constexpr std::string_view overlay_hotkey_name = "InGameOverlayShortcutKey "; + static constexpr std::string_view screenshot_hotkey_name = "InGameOverlayScreenshotHotKey "; + + inline std::filesystem::path getSteamPath() + { +#ifdef _WIN32 + try { + // TODO: check if keys/value exist + // steam should always be open and have written reg values... + winreg::RegKey key{ HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam" }; + const auto res = key.GetStringValue(L"SteamPath"); + spdlog::info(L"Detected Steam Path: {}", res); + return res; + } + catch (const winreg::RegException& e) { + spdlog::error("Couldn't get Steam path from Registry; {}", e.what()); + } + return Settings::common.steamPath; +#else + return L""; // TODO +#endif + } + + inline std::wstring getSteamUserId() + { +#ifdef _WIN32 + try { + // TODO: check if keys/value exist + // steam should always be open and have written reg values... + winreg::RegKey key{ HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess" }; + const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser")); + spdlog::info(L"Detected Steam UserId: {}", res); + return res; + } + catch (const winreg::RegException& e) { + spdlog::error("Couldn't get Steam path from Registry; {}", e.what()); + } + return Settings::common.steamUserId; +#else + return L""; // TODO +#endif + } + + inline std::vector getOverlayHotkey(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamPath()) + { + const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name); + if (!std::filesystem::exists(config_path)) { + spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); + return { "Shift", "KEY_TAB" }; // default + } + std::ifstream config_file(config_path); + auto root = tyti::vdf::read(config_file); + + std::shared_ptr> children = root.childs["system"]; + if (!children || children->attribs.empty() || !children->attribs.contains("InGameOverlayShortcutKey")) { + spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab"); + return { "Shift", "KEY_TAB" }; // default + } + auto hotkeys = children->attribs.at("InGameOverlayShortcutKey"); + + // has anyone more than 4 keys to open overlay?! + std::smatch m; + if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) { + spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab"); + return { "Shift", "KEY_TAB" }; // default + } + + std::vector res; + for (auto i = 1; i < m.size(); i++) { + const auto s = std::string(m[i]); + if (!s.empty()) { + res.push_back(s); + } + } + if (res.empty()) { + spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab"); + return { "Shift", "KEY_TAB" }; // default + } + spdlog::info("Detected Overlay hotkey(s): {}", std::accumulate( + res.begin() + 1, res.end(), res[0], + [](auto acc, const auto curr) { return acc += "+" + curr; })); + return res; + } + + inline std::vector getScreenshotHotkey(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamPath()) + { + const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name); + if (!std::filesystem::exists(config_path)) { + spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); + return { "KEY_F12" }; // default + } + std::ifstream config_file(config_path); + auto root = tyti::vdf::read(config_file); + + std::shared_ptr> children = root.childs["system"]; + if (!children || children->attribs.empty() || !children->attribs.contains("InGameOverlayScreenshotHotKey")) { + spdlog::warn("Couldn't detect overlay hotkey, using default: F12"); + return { "KEY_F12" }; // default + } + auto hotkeys = children->attribs.at("InGameOverlayScreenshotHotKey"); + + // has anyone more than 4 keys to screenshot?! + std::smatch m; + if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) { + spdlog::warn("Couldn't detect overlay hotkey, using default: F12"); + return { "KEY_F12" }; // default + } + + std::vector res; + for (auto i = 1; i < m.size(); i++) { + const auto s = std::string(m[i]); + if (!s.empty()) { + res.push_back(s); + } + } + if (res.empty()) { + spdlog::warn("Couldn't detect overlay hotkey, using default: F12"); + return { "KEY_F12" }; // default + } + spdlog::info("Detected screenshot hotkey(s): {}", std::accumulate( + res.begin() + 1, res.end(), res[0], + [](auto acc, const auto curr) { return acc += "+" + curr; })); + return res; + } + + inline bool getXBCRebindingEnabled(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamPath()) + { + const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name); + if (!std::filesystem::exists(config_path)) { + spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); + return false; + } + std::ifstream config_file(config_path); + auto root = tyti::vdf::read(config_file); + + if (root.attribs.empty() || !root.attribs.contains("SteamController_XBoxSupport")) { + spdlog::warn("\"Xbox Configuration Support\" is disabled in Steam. This may cause doubled Inputs!"); + return false; + } + auto xbsup = root.attribs.at("SteamController_XBoxSupport"); + if (xbsup != "1") { + spdlog::warn("\"Xbox Configuration Support\" is disabled in Steam. This may cause doubled Inputs!"); + } + return xbsup == "1"; + } + } +} \ No newline at end of file diff --git a/common/util.h b/common/util.h index 52cadc6..b12da09 100644 --- a/common/util.h +++ b/common/util.h @@ -31,6 +31,8 @@ limitations under the License. #include #ifdef SPDLOG_H +#define SPDLOG_WCHAR_TO_UTF8_SUPPORT +#define SPDLOG_WCHAR_FILENAMES #include #endif @@ -158,6 +160,6 @@ namespace util { return res; } } - } + } #endif - } \ No newline at end of file +} \ No newline at end of file From da0b99d67983120cd38815a9500c50ae35844928 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 17:03:34 +0100 Subject: [PATCH 10/65] Increase time regular Steam overlay can be opened before opening GlosSIOverlay: 1s -> 2.5s --- GlosSITarget/SteamTarget.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GlosSITarget/SteamTarget.h b/GlosSITarget/SteamTarget.h index 0ce56ef..2aea62f 100644 --- a/GlosSITarget/SteamTarget.h +++ b/GlosSITarget/SteamTarget.h @@ -89,7 +89,7 @@ class SteamTarget { static inline WindowHandle target_window_handle_ = nullptr; sf::Clock overlay_trigger_clock_; - uint32_t overlay_trigger_max_seconds_ = 1; + float overlay_trigger_max_seconds_ = 2.5; bool overlay_trigger_flag_ = false; bool delayed_shutdown_ = false; From 2a8df5fdc300807cf3baee42fe188dbe4c169ed7 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 17:16:43 +0100 Subject: [PATCH 11/65] Update GlosSIWindow default refresh rate Dont just use screen rerfresh-rate anymore (as it's a waste with high refresh monitors) and scale it down a bit --- GlosSITarget/TargetWindow.cpp | 16 ++++++++++++++-- GlosSITarget/TargetWindow.h | 1 + 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/GlosSITarget/TargetWindow.cpp b/GlosSITarget/TargetWindow.cpp index 71e4295..f744b43 100644 --- a/GlosSITarget/TargetWindow.cpp +++ b/GlosSITarget/TargetWindow.cpp @@ -73,7 +73,7 @@ TargetWindow::TargetWindow( } if (Settings::window.maxFps < 15 && Settings::window.maxFps > 0) { Settings::window.maxFps = 0; - setFpsLimit(screen_refresh_rate_); + setFpsLimit(TargetWindow::calcAutoRefreshRate(screen_refresh_rate_)); } else { setFpsLimit(Settings::window.maxFps); } @@ -307,6 +307,18 @@ WORD TargetWindow::GetWindowDPI(HWND hWnd) } #endif +unsigned int TargetWindow::calcAutoRefreshRate(unsigned int rate) +{ + unsigned int auto_refresh_rate = rate; + while (auto_refresh_rate > 60) { + auto_refresh_rate /= 2; + } + if (auto_refresh_rate < 30) { + auto_refresh_rate = 30; + } + return auto_refresh_rate; +} + void TargetWindow::createWindow() { toggle_window_mode_after_frame_ = false; @@ -379,7 +391,7 @@ void TargetWindow::createWindow() screen_refresh_rate_ = 60; } else { - setFpsLimit(dev_mode.dmDisplayFrequency); + setFpsLimit(TargetWindow::calcAutoRefreshRate(dev_mode.dmDisplayFrequency)); screen_refresh_rate_ = dev_mode.dmDisplayFrequency; } diff --git a/GlosSITarget/TargetWindow.h b/GlosSITarget/TargetWindow.h index cf151a6..b87f79c 100644 --- a/GlosSITarget/TargetWindow.h +++ b/GlosSITarget/TargetWindow.h @@ -79,6 +79,7 @@ class TargetWindow { std::shared_ptr overlay_; + static unsigned int calcAutoRefreshRate(unsigned int rate); void createWindow(); bool toggle_window_mode_after_frame_ = false; From 04653b2fb7778df805a3bdf10d1372ece2197793 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 17:22:28 +0100 Subject: [PATCH 12/65] Cleanup --- CEFInjectLib/CEFInject.cpp | 9 ++++----- GlosSITarget/SteamTarget.cpp | 5 +---- GlosSITarget/TargetWindow.cpp | 2 +- 3 files changed, 6 insertions(+), 10 deletions(-) diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp index 4270031..48b3aa9 100644 --- a/CEFInjectLib/CEFInject.cpp +++ b/CEFInjectLib/CEFInject.cpp @@ -75,8 +75,6 @@ namespace CEFInject const auto json = nlohmann::json::parse(res->body); for (const auto& tab : json) { if (tab["title"].get().starts_with(tabname)) { - - #ifdef _WIN32 INT rc; WSADATA wsaData; @@ -106,10 +104,10 @@ namespace CEFInject bool exit = false; while (ws->getReadyState() != easywsclient::WebSocket::CLOSED) { ws->poll(); - ws->dispatch([&ws, & res, & exit](const std::string & message) { + ws->dispatch([&ws, &res, &exit](const std::string& message) { res = nlohmann::json::parse(message)["result"]["result"]["value"]; - exit = true; - }); + exit = true; + }); if (exit) { ws->close(); return res; @@ -128,6 +126,7 @@ namespace CEFInject } } } + return nullptr; } } \ No newline at end of file diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 30592e6..df5878a 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -15,13 +15,10 @@ limitations under the License. */ #include "SteamTarget.h" -#include "..\common\Settings.h" +#include "../common/Settings.h" #include "steam_sf_keymap.h" #include -#include -#include -#include #include #ifdef _WIN32 diff --git a/GlosSITarget/TargetWindow.cpp b/GlosSITarget/TargetWindow.cpp index f744b43..41e296c 100644 --- a/GlosSITarget/TargetWindow.cpp +++ b/GlosSITarget/TargetWindow.cpp @@ -398,7 +398,7 @@ void TargetWindow::createWindow() overlay_ = std::make_shared( window_, [this]() { close(); }, toggle_overlay_state_, Settings::window.windowMode); - spdlog::debug("auto screen sCale: {}", dpi/96.f); + spdlog::debug("auto screen Scale: {}", dpi/96.f); ImGuiIO& io = ImGui::GetIO(); io.FontGlobalScale = dpi / 96.f; ImGui::SFML::UpdateFontTexture(); From baa7aec0c8526b38e4bd9dd06ad61bed540f5c67 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 17:31:44 +0100 Subject: [PATCH 13/65] Settings: don't log default value if setting is not in json --- common/Settings.h | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/common/Settings.h b/common/Settings.h index f0e4fa6..2becff9 100644 --- a/common/Settings.h +++ b/common/Settings.h @@ -136,17 +136,10 @@ namespace Settings { } } catch (const nlohmann::json::exception& e) { - if constexpr (std::is_same_v) { - e.id == 403 - ? spdlog::trace("Err parsing \"{}\"; {}; Using default: {}", key, e.what(), util::string::to_string(value)) - : spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); - } - else - { - e.id == 403 - ? spdlog::trace("Err parsing \"{}\"; {}; Using default: {}", key, e.what(), value) - : spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); - } + e.id == 403 + ? spdlog::trace("Err parsing \"{}\"; {}", key, e.what()) + : spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); + } catch (const std::exception& e) { spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); From 531857003e2fb5007e47b43d798b6d0213c9ab0c Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 17:40:44 +0100 Subject: [PATCH 14/65] =?UTF-8?q?Settings:=20Fix=20crash=20if=20"launcherP?= =?UTF-8?q?rocesses"=20is=20empty=20=F0=9F=99=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- common/Settings.h | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/common/Settings.h b/common/Settings.h index 2becff9..3f40fe9 100644 --- a/common/Settings.h +++ b/common/Settings.h @@ -163,12 +163,14 @@ namespace Settings { safeParseValue(launchconf, "killLauncher", launch.killLauncher); safeParseValue(launchconf, "ignoreLauncher", launch.ignoreLauncher); - if (const auto launcherProcs = launchconf["launcherProcesses"]; - !launcherProcs.is_null() && !launcherProcs.empty() && launcherProcs.is_array()) { - launch.launcherProcesses.clear(); - launch.launcherProcesses.reserve(launcherProcs.size()); - for (auto& proc : launcherProcs) { - launch.launcherProcesses.push_back(util::string::to_wstring(proc)); + if (launchconf.contains("launcherProcesses") && launchconf["launcherProcesses"].is_array()) { + if (const auto launcherProcs = launchconf["launcherProcesses"]; + !launcherProcs.is_null() && !launcherProcs.empty() && launcherProcs.is_array()) { + launch.launcherProcesses.clear(); + launch.launcherProcesses.reserve(launcherProcs.size()); + for (auto& proc : launcherProcs) { + launch.launcherProcesses.push_back(util::string::to_wstring(proc)); + } } } } From d89c84478faa84b65f7961d8c9f3d87eedd16fd7 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 17:44:01 +0100 Subject: [PATCH 15/65] Add some fancy tail replacement scripts for viewing logs in craptastic visual studio --- tail_config_logs.ps1 | 1 + tail_target_logs.ps1 | 1 + 2 files changed, 2 insertions(+) create mode 100644 tail_config_logs.ps1 create mode 100644 tail_target_logs.ps1 diff --git a/tail_config_logs.ps1 b/tail_config_logs.ps1 new file mode 100644 index 0000000..491758b --- /dev/null +++ b/tail_config_logs.ps1 @@ -0,0 +1 @@ +Get-Content "$env:appdata\GlosSI\glossiconfig.log" -Wait \ No newline at end of file diff --git a/tail_target_logs.ps1 b/tail_target_logs.ps1 new file mode 100644 index 0000000..1fbc438 --- /dev/null +++ b/tail_target_logs.ps1 @@ -0,0 +1 @@ +Get-Content "$env:appdata\GlosSI\GlosSItarget.log" -Wait \ No newline at end of file From f68e3070d092b6fe48b734f95fbd1c321918ac10 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 19:08:19 +0100 Subject: [PATCH 16/65] Add (Partial) VSCode cpp config --- .vscode/tasks.json | 41 ++++++++++++++++ CEFInjectLib/.vscode/c_cpp_properties.json | 25 ++++++++++ CEFInjectLib/.vscode/tasks.json | 43 +++++++++++++++++ GlosSIConfig/.vscode/tasks.json | 43 +++++++++++++++++ GlosSIConfig/Resource.rc | 8 ++-- GlosSITarget/.vscode/c_cpp_properties.json | 37 ++++++++++++++ GlosSITarget/.vscode/launch.json | 22 ++++----- GlosSITarget/.vscode/settings.json | 3 -- GlosSITarget/.vscode/tasks.json | 56 ++++++++++++++++------ GlosSITarget/Resource.rc | 8 ++-- GlosSIWatchdog/.vscode/tasks.json | 41 ++++++++++++++++ Installer/Installer.nsi | 2 +- UWPOverlayEnablerDLL/.vscode/tasks.json | 41 ++++++++++++++++ common/.vscode/c_cpp_properties.json | 33 +++++++++++++ glosc.code-workspace | 15 ------ glossi.code-workspace | 42 ++++++++++++++++ 16 files changed, 405 insertions(+), 55 deletions(-) create mode 100644 .vscode/tasks.json create mode 100644 CEFInjectLib/.vscode/c_cpp_properties.json create mode 100644 CEFInjectLib/.vscode/tasks.json create mode 100644 GlosSIConfig/.vscode/tasks.json create mode 100644 GlosSITarget/.vscode/c_cpp_properties.json delete mode 100644 GlosSITarget/.vscode/settings.json create mode 100644 GlosSIWatchdog/.vscode/tasks.json create mode 100644 UWPOverlayEnablerDLL/.vscode/tasks.json create mode 100644 common/.vscode/c_cpp_properties.json delete mode 100644 glosc.code-workspace create mode 100644 glossi.code-workspace diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..931bf3d --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "Build Solution (Debug)", + "command": "msbuild.exe", + "args": [ + "GlosSI.sln", + "/target:Build", + "/p:Configuration=Debug", + "/p:Platform=x64" + ], + "options": { + "cwd": "${workspaceFolder}", + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + }, + }, + { + "type": "shell", + "label": "Re-Build Solution (Debug)", + "command": "msbuild.exe", + "args": [ + "GlosSI.sln", + "/target:Rebuild", + "/p:Configuration=Debug", + "/p:Platform=x64" + ], + "options": { + "cwd": "${workspaceFolder}", + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + }, + } + ] + } \ No newline at end of file diff --git a/CEFInjectLib/.vscode/c_cpp_properties.json b/CEFInjectLib/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..7ce2b99 --- /dev/null +++ b/CEFInjectLib/.vscode/c_cpp_properties.json @@ -0,0 +1,25 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/../deps/json/include", + "${workspaceFolder}/../deps/cpp-httplib", + "${workspaceFolder}/../deps/easywsclient" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE", + "_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING" + ], + "windowsSdkVersion": "10.0.18362.0", + "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe", + "cStandard": "c11", + "cppStandard": "c++20", + "intelliSenseMode": "msvc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/CEFInjectLib/.vscode/tasks.json b/CEFInjectLib/.vscode/tasks.json new file mode 100644 index 0000000..fda20d0 --- /dev/null +++ b/CEFInjectLib/.vscode/tasks.json @@ -0,0 +1,43 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "Build CEFInjectLib (Debug)", + "command": "msbuild.exe", + "args": [ + "GlosSI.sln", + "/target:CEFInjectLib", + "/p:Configuration=Debug", + "/p:Platform=x64" + ], + "options": { + "cwd": "${workspaceFolder}/..", + + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + }, + }, + { + "type": "shell", + "label": "Re-Build CEFInjectLib (Debug)", + "command": "msbuild.exe", + "args": [ + "GlosSI.sln", + "/target:CEFInjectLib:Rebuild", + "/p:Configuration=Debug", + "/p:Platform=x64" + ], + "options": { + "cwd": "${workspaceFolder}/..", + + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + }, + } + ] + } \ No newline at end of file diff --git a/GlosSIConfig/.vscode/tasks.json b/GlosSIConfig/.vscode/tasks.json new file mode 100644 index 0000000..8581e9e --- /dev/null +++ b/GlosSIConfig/.vscode/tasks.json @@ -0,0 +1,43 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "Build GlosSIConfig (Debug)", + "command": "msbuild.exe", + "args": [ + "GlosSI.sln", + "/target:GlosSIConfig", + "/p:Configuration=Debug", + "/p:Platform=x64" + ], + "options": { + "cwd": "${workspaceFolder}/..", + + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + }, + }, + { + "type": "shell", + "label": "Re-Build GlosSIConfig (Debug)", + "command": "msbuild.exe", + "args": [ + "GlosSI.sln", + "/target:GlosSIConfig:Rebuild", + "/p:Configuration=Debug", + "/p:Platform=x64" + ], + "options": { + "cwd": "${workspaceFolder}/..", + + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + }, + } + ] + } \ No newline at end of file diff --git a/GlosSIConfig/Resource.rc b/GlosSIConfig/Resource.rc index 1f1efe3..cc7ca13 100644 --- a/GlosSIConfig/Resource.rc +++ b/GlosSIConfig/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,2,0011001030052 - PRODUCTVERSION 0,1,2,0011001030052 + FILEVERSION 0,1,2,0018002705604 + PRODUCTVERSION 0,1,2,0018002705604 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - Config" - VALUE "FileVersion", "0.1.2.0-11-g1f3fb52" + VALUE "FileVersion", "0.1.2.0-18-g27056b4" VALUE "InternalName", "GlosSIConfig" VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSIConfig.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.2.0-11-g1f3fb52" + VALUE "ProductVersion", "0.1.2.0-18-g27056b4" END END BLOCK "VarFileInfo" diff --git a/GlosSITarget/.vscode/c_cpp_properties.json b/GlosSITarget/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..b4fe885 --- /dev/null +++ b/GlosSITarget/.vscode/c_cpp_properties.json @@ -0,0 +1,37 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/../deps/SFML/include", + "${workspaceFolder}/../deps/WinReg", + "${workspaceFolder}/../deps/spdlog/include", + "${workspaceFolder}/../deps/ValveFileVDF", + "${workspaceFolder}/../deps/subhook", + "${workspaceFolder}/../deps/ViGEmClient/include", + "${workspaceFolder}/../deps/imgui", + "${workspaceFolder}/../deps/imgui-sfml", + "${workspaceFolder}/../deps/json/include", + "${workspaceFolder}/../deps/traypp/tray/include", + "${workspaceFolder}/../deps/cpp-httplib", + "${workspaceFolder}/../CEFInjectLib" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE", + "SPDLOG_WCHAR_TO_UTF8_SUPPORT", + "SPDLOG_WCHAR_FILENAMES", + "_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING", + "SUBHOOK_STATIC" + ], + "windowsSdkVersion": "10.0.18362.0", + "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe", + "cStandard": "c11", + "cppStandard": "c++20", + "intelliSenseMode": "msvc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/GlosSITarget/.vscode/launch.json b/GlosSITarget/.vscode/launch.json index 67e0af6..cfbec53 100644 --- a/GlosSITarget/.vscode/launch.json +++ b/GlosSITarget/.vscode/launch.json @@ -5,24 +5,18 @@ "version": "0.2.0", "configurations": [ { - "name": "(gdb) Launch", - "type": "cppdbg", + "name": "Launch GlosSITarget", + "type": "cppvsdbg", "request": "launch", - "program": "${workspaceFolder}/build/GlosSITarget", - "preLaunchTask": "Build", - "args": [], + "program": "${workspaceFolder}/../x64/Debug/GlosSITarget.exe", + "preLaunchTask": "Build GlosSITarget (Debug)", + "args": [ + "-window" + ], "stopAtEntry": false, - "cwd": "${workspaceFolder}/build", + "cwd": "${workspaceFolder}/../x64/Debug", "environment": [], "externalConsole": false, - "MIMode": "gdb", - "setupCommands": [ - { - "description": "Enable pretty-printing for gdb", - "text": "-enable-pretty-printing", - "ignoreFailures": true - } - ] } ] } \ No newline at end of file diff --git a/GlosSITarget/.vscode/settings.json b/GlosSITarget/.vscode/settings.json deleted file mode 100644 index b4d8c35..0000000 --- a/GlosSITarget/.vscode/settings.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools" -} \ No newline at end of file diff --git a/GlosSITarget/.vscode/tasks.json b/GlosSITarget/.vscode/tasks.json index 4e6bbb9..567aa16 100644 --- a/GlosSITarget/.vscode/tasks.json +++ b/GlosSITarget/.vscode/tasks.json @@ -1,15 +1,43 @@ { - "version": "2.0.0", - "tasks": [ - { - "type": "cmake", - "command": "build", - "label": "Build", - "group": { - "kind": "build", - "isDefault": true - }, - "problemMatcher": [] - } - ] -} \ No newline at end of file + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "Build GlosSITarget (Debug)", + "command": "msbuild.exe", + "args": [ + "GlosSI.sln", + "/target:GlosSITarget", + "/p:Configuration=Debug", + "/p:Platform=x64" + ], + "options": { + "cwd": "${workspaceFolder}/..", + + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + }, + }, + { + "type": "shell", + "label": "Re-Build GlosSITarget (Debug)", + "command": "msbuild.exe", + "args": [ + "GlosSI.sln", + "/target:GlosSITarget:Rebuild", + "/p:Configuration=Debug", + "/p:Platform=x64" + ], + "options": { + "cwd": "${workspaceFolder}/..", + + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + }, + } + ] + } \ No newline at end of file diff --git a/GlosSITarget/Resource.rc b/GlosSITarget/Resource.rc index 806d632..8346603 100644 --- a/GlosSITarget/Resource.rc +++ b/GlosSITarget/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,2,0011001030052 - PRODUCTVERSION 0,1,2,0011001030052 + FILEVERSION 0,1,2,0018002705604 + PRODUCTVERSION 0,1,2,0018002705604 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - SteamTarget" - VALUE "FileVersion", "0.1.2.0-11-g1f3fb52" + VALUE "FileVersion", "0.1.2.0-18-g27056b4" VALUE "InternalName", "GlosSITarget" VALUE "LegalCopyright", "Copyright (C) 2021-2022 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSITarget.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.2.0-11-g1f3fb52" + VALUE "ProductVersion", "0.1.2.0-18-g27056b4" END END BLOCK "VarFileInfo" diff --git a/GlosSIWatchdog/.vscode/tasks.json b/GlosSIWatchdog/.vscode/tasks.json new file mode 100644 index 0000000..b23e6df --- /dev/null +++ b/GlosSIWatchdog/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "Build GlosSIWatchdog (Debug)", + "command": "msbuild.exe", + "args": [ + "GlosSI.sln", + "/target:GlosSIWatchdog", + "/p:Configuration=Debug", + "/p:Platform=x64" + ], + "options": { + "cwd": "${workspaceFolder}/..", + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + }, + }, + { + "type": "shell", + "label": "Re-Build GlosSIWatchdog (Debug)", + "command": "msbuild.exe", + "args": [ + "GlosSI.sln", + "/target:GlosSIWatchdog:Rebuild", + "/p:Configuration=Debug", + "/p:Platform=x64" + ], + "options": { + "cwd": "${workspaceFolder}/..", + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + }, + } + ] + } \ No newline at end of file diff --git a/Installer/Installer.nsi b/Installer/Installer.nsi index 80553ae..0d9a1dc 100644 --- a/Installer/Installer.nsi +++ b/Installer/Installer.nsi @@ -3,7 +3,7 @@ !define APP_NAME "GlosSI" !define COMP_NAME "Peter Repukat - Flatspotsoftware" !define WEB_SITE "https://glossi.flatspot.pictures/" -!define VERSION "0.1.2.0-11-g1f3fb52" +!define VERSION "0.1.2.0-18-g27056b4" !define COPYRIGHT "Peter Repukat - FlatspotSoftware © 2017-2022" !define DESCRIPTION "SteamInput compatibility tool" !define INSTALLER_NAME "GlosSI-Installer.exe" diff --git a/UWPOverlayEnablerDLL/.vscode/tasks.json b/UWPOverlayEnablerDLL/.vscode/tasks.json new file mode 100644 index 0000000..85afe38 --- /dev/null +++ b/UWPOverlayEnablerDLL/.vscode/tasks.json @@ -0,0 +1,41 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "type": "shell", + "label": "Build UWPOverlayEnablerDLL (Debug)", + "command": "msbuild.exe", + "args": [ + "GlosSI.sln", + "/target:UWPOverlayEnablerDLL", + "/p:Configuration=Debug", + "/p:Platform=x64" + ], + "options": { + "cwd": "${workspaceFolder}/..", + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + }, + }, + { + "type": "shell", + "label": "Re-Build UWPOverlayEnablerDLL (Debug)", + "command": "msbuild.exe", + "args": [ + "GlosSI.sln", + "/target:UWPOverlayEnablerDLL:Rebuild", + "/p:Configuration=Debug", + "/p:Platform=x64" + ], + "options": { + "cwd": "${workspaceFolder}/..", + }, + "problemMatcher": ["$msCompile"], + "group": { + "kind": "build", + }, + } + ] + } \ No newline at end of file diff --git a/common/.vscode/c_cpp_properties.json b/common/.vscode/c_cpp_properties.json new file mode 100644 index 0000000..a007661 --- /dev/null +++ b/common/.vscode/c_cpp_properties.json @@ -0,0 +1,33 @@ +{ + "configurations": [ + { + "name": "Win32", + "includePath": [ + "${workspaceFolder}/**", + "${workspaceFolder}/../deps/SFML/include", + "${workspaceFolder}/../deps/WinReg", + "${workspaceFolder}/../deps/spdlog/include", + "${workspaceFolder}/../deps/ValveFileVDF", + "${workspaceFolder}/../deps/subhook", + "${workspaceFolder}/../deps/ViGEmClient/include", + "${workspaceFolder}/../deps/imgui", + "${workspaceFolder}/../deps/imgui-sfml", + "${workspaceFolder}/../deps/json/include", + "${workspaceFolder}/../deps/traypp/tray/include", + "${workspaceFolder}/../deps/cpp-httplib", + "${workspaceFolder}/../CEFInjectLib" + ], + "defines": [ + "_DEBUG", + "UNICODE", + "_UNICODE" + ], + "windowsSdkVersion": "10.0.18362.0", + "compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe", + "cStandard": "c11", + "cppStandard": "c++20", + "intelliSenseMode": "msvc-x64" + } + ], + "version": 4 +} \ No newline at end of file diff --git a/glosc.code-workspace b/glosc.code-workspace deleted file mode 100644 index ba57d0b..0000000 --- a/glosc.code-workspace +++ /dev/null @@ -1,15 +0,0 @@ -{ - "folders": [ - { - "path": "GlosSIConfig" - }, - { - "path": "GlosSITarget" - }, - { - "name": "root", - "path": "." - } - ], - "settings": {} -} \ No newline at end of file diff --git a/glossi.code-workspace b/glossi.code-workspace new file mode 100644 index 0000000..8a21769 --- /dev/null +++ b/glossi.code-workspace @@ -0,0 +1,42 @@ +{ + "folders": [ + { + "path": "common" + }, + { + "path": "CEFInjectLib" + }, + { + "path": "GlosSIConfig" + }, + { + "path": "GlosSITarget" + }, + { + "path": "GlosSIWatchdog" + }, + { + "path": "UWPOverlayEnablerDLL" + }, + { + "path": "Installer" + }, + { + "name": "root", + "path": "." + }, + ], + "settings": { + // uncomment if not already in user settings and wanting to debug/build c++ code (vs 2022 required!) + // "terminal.integrated.profiles.windows": { + // "Developer PowerShell for VS 2022": { + // "source": "PowerShell", + // "icon": "terminal-powershell", + // "args": [ + // "-c", + // "$vsPath = & '${env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe' -property installationpath; Import-Module \"$vsPath/Common7/Tools/Microsoft.VisualStudio.DevShell.dll\"; Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation; powershell.exe" + // ] + // } + // }, + } +} \ No newline at end of file From cf23edafdbc0a5dc3ce55b19608f70fece777501 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 21:57:35 +0100 Subject: [PATCH 17/65] CEFInject: Scope js injection --- CEFInjectLib/CEFInject.cpp | 8 ++++++-- CEFInjectLib/CEFInject.h | 1 + 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp index 48b3aa9..af74b00 100644 --- a/CEFInjectLib/CEFInject.cpp +++ b/CEFInjectLib/CEFInject.cpp @@ -69,6 +69,11 @@ namespace CEFInject nlohmann::json InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port) { + return InjectJs_Unscoped(tabname, L"(function(){\n" + js + L"\n})()", port); + } + + nlohmann::json InjectJs_Unscoped(const std::wstring &tabname, const std::wstring &js, uint16_t port) + { auto cli = internal::GetHttpClient(port); if (auto res = cli.Get("/json")) { if (res->status == 200) { @@ -127,6 +132,5 @@ namespace CEFInject } } return nullptr; - } - + } } \ No newline at end of file diff --git a/CEFInjectLib/CEFInject.h b/CEFInjectLib/CEFInject.h index fc077f6..8c85118 100644 --- a/CEFInjectLib/CEFInject.h +++ b/CEFInjectLib/CEFInject.h @@ -32,5 +32,6 @@ namespace CEFInject bool CEFDebugAvailable(uint16_t port = internal::port_); std::vector AvailableTabs(uint16_t port = internal::port_); nlohmann::json InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port = internal::port_); + nlohmann::json InjectJs_Unscoped(const std::wstring& tabname, const std::wstring& js, uint16_t port = internal::port_); } \ No newline at end of file From e6db32827325ef510ad405c0a98930c28077ae69 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 21:58:03 +0100 Subject: [PATCH 18/65] Settings: Fix cli-switches not overriding .json config --- common/Settings.h | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/common/Settings.h b/common/Settings.h index 3f40fe9..83dcc1d 100644 --- a/common/Settings.h +++ b/common/Settings.h @@ -219,13 +219,14 @@ namespace Settings { inline void Parse(const std::vector& args) { std::wstring configName; + std::vector> cli_overrides; for (const auto& arg : args) { if (arg.empty()) { continue; } if (cmd_args.contains(arg)) { - cmd_args.at(arg)(); + cli_overrides.push_back(cmd_args.at(arg)); } else { configName += L" " + std::wstring(arg.begin(), arg.end()); @@ -254,12 +255,18 @@ namespace Settings { if (!json_file.is_open()) { spdlog::error(L"Couldn't open settings file {}", path.wstring()); spdlog::debug(L"Using sane defaults..."); + for (const auto& ovr : cli_overrides) { + ovr(); + } return; } settings_path_ = path; const auto& json = nlohmann::json::parse(json_file); Parse(json); + for (const auto& ovr : cli_overrides) { + ovr(); + } spdlog::debug("Read config file \"{}\"; config: {}", path.string(), json.dump()); json_file.close(); } From 00127dcd2bd82b9c755633cff350d3ebebbc8986 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 21:58:40 +0100 Subject: [PATCH 19/65] HttpLib/SteamUtils: Endpoint to get Steam User config as JSON --- GlosSITarget/HttpServer.cpp | 23 ++- common/steam_util.h | 318 ++++++++++++++++++++---------------- 2 files changed, 197 insertions(+), 144 deletions(-) diff --git a/GlosSITarget/HttpServer.cpp b/GlosSITarget/HttpServer.cpp index ef0a49b..b6da48b 100644 --- a/GlosSITarget/HttpServer.cpp +++ b/GlosSITarget/HttpServer.cpp @@ -21,6 +21,7 @@ limitations under the License. #include "AppLauncher.h" #include "../common/Settings.h" +#include "../common/steam_util.h" HttpServer::HttpServer(AppLauncher& app_launcher, std::function close) : app_launcher_(app_launcher), close_(std::move(close)) { @@ -28,12 +29,17 @@ HttpServer::HttpServer(AppLauncher& app_launcher, std::function close) : void HttpServer::run() { - server_.Get("/launched-pids", [this](const httplib::Request& req, httplib::Response& res) { + auto setCorsHeader = [](httplib::Response& res) { + res.set_header("Access-Control-Allow-Origin", "*"); + }; + server_.Get("/launched-pids", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { const nlohmann::json j = app_launcher_.launchedPids(); res.set_content(j.dump(), "text/json"); + setCorsHeader(res); }); - server_.Post("/launched-pids", [this](const httplib::Request& req, httplib::Response& res) { + server_.Post("/launched-pids", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { + setCorsHeader(res); try { const nlohmann::json postbody = nlohmann::json::parse(req.body); app_launcher_.addPids(postbody.get>()); @@ -63,21 +69,28 @@ void HttpServer::run() res.set_content(j.dump(), "text/json"); }); - server_.Post("/quit", [this](const httplib::Request& req, httplib::Response& res) { + server_.Post("/quit", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { + setCorsHeader(res); close_(); }); - server_.Get("/settings", [this](const httplib::Request& req, httplib::Response& res) { + server_.Get("/settings", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { + setCorsHeader(res); res.set_content(Settings::toJson().dump(), "text/json"); }); + server_.Get("/steam_settings", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { + setCorsHeader(res); + res.set_content(util::steam::getSteamConfig().dump(4), "text/json"); + }); + server_thread_ = std::thread([this]() { if (!server_.listen("0.0.0.0", port_)) { spdlog::error("Couldn't start http-server"); return; } spdlog::debug("Started http-server on port {}", static_cast(port_)); - }); +}); } void HttpServer::stop() diff --git a/common/steam_util.h b/common/steam_util.h index 43c60cb..86c99af 100644 --- a/common/steam_util.h +++ b/common/steam_util.h @@ -30,152 +30,192 @@ namespace util namespace steam { - static constexpr std::wstring_view user_data_path = L"/userdata/"; - static constexpr std::wstring_view config_file_name = L"/config/localconfig.vdf"; - static constexpr std::string_view overlay_hotkey_name = "InGameOverlayShortcutKey "; - static constexpr std::string_view screenshot_hotkey_name = "InGameOverlayScreenshotHotKey "; + static constexpr std::wstring_view user_data_path = L"/userdata/"; + static constexpr std::wstring_view config_file_name = L"/config/localconfig.vdf"; + static constexpr std::string_view overlay_hotkey_name = "InGameOverlayShortcutKey "; + static constexpr std::string_view screenshot_hotkey_name = "InGameOverlayScreenshotHotKey "; - inline std::filesystem::path getSteamPath() - { + inline std::filesystem::path getSteamPath() + { #ifdef _WIN32 - try { - // TODO: check if keys/value exist - // steam should always be open and have written reg values... - winreg::RegKey key{ HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam" }; - const auto res = key.GetStringValue(L"SteamPath"); - spdlog::info(L"Detected Steam Path: {}", res); - return res; - } - catch (const winreg::RegException& e) { - spdlog::error("Couldn't get Steam path from Registry; {}", e.what()); - } - return Settings::common.steamPath; + try { + // TODO: check if keys/value exist + // steam should always be open and have written reg values... + winreg::RegKey key{ HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam" }; + const auto res = key.GetStringValue(L"SteamPath"); + spdlog::info(L"Detected Steam Path: {}", res); + return res; + } + catch (const winreg::RegException& e) { + spdlog::error("Couldn't get Steam path from Registry; {}", e.what()); + } + return Settings::common.steamPath; #else - return L""; // TODO + return L""; // TODO #endif - } + } - inline std::wstring getSteamUserId() - { + inline std::wstring getSteamUserId() + { #ifdef _WIN32 - try { - // TODO: check if keys/value exist - // steam should always be open and have written reg values... - winreg::RegKey key{ HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess" }; - const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser")); - spdlog::info(L"Detected Steam UserId: {}", res); - return res; - } - catch (const winreg::RegException& e) { - spdlog::error("Couldn't get Steam path from Registry; {}", e.what()); - } - return Settings::common.steamUserId; + try { + // TODO: check if keys/value exist + // steam should always be open and have written reg values... + winreg::RegKey key{ HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess" }; + const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser")); + spdlog::info(L"Detected Steam UserId: {}", res); + return res; + } + catch (const winreg::RegException& e) { + spdlog::error("Couldn't get Steam path from Registry; {}", e.what()); + } + return Settings::common.steamUserId; #else - return L""; // TODO + return L""; // TODO #endif - } - - inline std::vector getOverlayHotkey(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamPath()) - { - const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name); - if (!std::filesystem::exists(config_path)) { - spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); - return { "Shift", "KEY_TAB" }; // default - } - std::ifstream config_file(config_path); - auto root = tyti::vdf::read(config_file); - - std::shared_ptr> children = root.childs["system"]; - if (!children || children->attribs.empty() || !children->attribs.contains("InGameOverlayShortcutKey")) { - spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab"); - return { "Shift", "KEY_TAB" }; // default - } - auto hotkeys = children->attribs.at("InGameOverlayShortcutKey"); - - // has anyone more than 4 keys to open overlay?! - std::smatch m; - if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) { - spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab"); - return { "Shift", "KEY_TAB" }; // default - } - - std::vector res; - for (auto i = 1; i < m.size(); i++) { - const auto s = std::string(m[i]); - if (!s.empty()) { - res.push_back(s); - } - } - if (res.empty()) { - spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab"); - return { "Shift", "KEY_TAB" }; // default - } - spdlog::info("Detected Overlay hotkey(s): {}", std::accumulate( - res.begin() + 1, res.end(), res[0], - [](auto acc, const auto curr) { return acc += "+" + curr; })); - return res; - } - - inline std::vector getScreenshotHotkey(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamPath()) - { - const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name); - if (!std::filesystem::exists(config_path)) { - spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); - return { "KEY_F12" }; // default - } - std::ifstream config_file(config_path); - auto root = tyti::vdf::read(config_file); - - std::shared_ptr> children = root.childs["system"]; - if (!children || children->attribs.empty() || !children->attribs.contains("InGameOverlayScreenshotHotKey")) { - spdlog::warn("Couldn't detect overlay hotkey, using default: F12"); - return { "KEY_F12" }; // default - } - auto hotkeys = children->attribs.at("InGameOverlayScreenshotHotKey"); - - // has anyone more than 4 keys to screenshot?! - std::smatch m; - if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) { - spdlog::warn("Couldn't detect overlay hotkey, using default: F12"); - return { "KEY_F12" }; // default - } - - std::vector res; - for (auto i = 1; i < m.size(); i++) { - const auto s = std::string(m[i]); - if (!s.empty()) { - res.push_back(s); - } - } - if (res.empty()) { - spdlog::warn("Couldn't detect overlay hotkey, using default: F12"); - return { "KEY_F12" }; // default - } - spdlog::info("Detected screenshot hotkey(s): {}", std::accumulate( - res.begin() + 1, res.end(), res[0], - [](auto acc, const auto curr) { return acc += "+" + curr; })); - return res; - } - - inline bool getXBCRebindingEnabled(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamPath()) - { - const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name); - if (!std::filesystem::exists(config_path)) { - spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); - return false; - } - std::ifstream config_file(config_path); - auto root = tyti::vdf::read(config_file); - - if (root.attribs.empty() || !root.attribs.contains("SteamController_XBoxSupport")) { - spdlog::warn("\"Xbox Configuration Support\" is disabled in Steam. This may cause doubled Inputs!"); - return false; - } - auto xbsup = root.attribs.at("SteamController_XBoxSupport"); - if (xbsup != "1") { - spdlog::warn("\"Xbox Configuration Support\" is disabled in Steam. This may cause doubled Inputs!"); - } - return xbsup == "1"; - } + } + + inline std::vector getOverlayHotkey(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamPath()) + { + const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name); + if (!std::filesystem::exists(config_path)) { + spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); + return { "Shift", "KEY_TAB" }; // default + } + std::ifstream config_file(config_path); + auto root = tyti::vdf::read(config_file); + + std::shared_ptr> children = root.childs["system"]; + if (!children || children->attribs.empty() || !children->attribs.contains("InGameOverlayShortcutKey")) { + spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab"); + return { "Shift", "KEY_TAB" }; // default + } + auto hotkeys = children->attribs.at("InGameOverlayShortcutKey"); + + // has anyone more than 4 keys to open overlay?! + std::smatch m; + if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) { + spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab"); + return { "Shift", "KEY_TAB" }; // default + } + + std::vector res; + for (auto i = 1; i < m.size(); i++) { + const auto s = std::string(m[i]); + if (!s.empty()) { + res.push_back(s); + } + } + if (res.empty()) { + spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab"); + return { "Shift", "KEY_TAB" }; // default + } + spdlog::info("Detected Overlay hotkey(s): {}", std::accumulate( + res.begin() + 1, res.end(), res[0], + [](auto acc, const auto curr) { return acc += "+" + curr; })); + return res; + } + + inline std::vector getScreenshotHotkey(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamPath()) + { + const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name); + if (!std::filesystem::exists(config_path)) { + spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); + return { "KEY_F12" }; // default + } + std::ifstream config_file(config_path); + auto root = tyti::vdf::read(config_file); + + std::shared_ptr> children = root.childs["system"]; + if (!children || children->attribs.empty() || !children->attribs.contains("InGameOverlayScreenshotHotKey")) { + spdlog::warn("Couldn't detect overlay hotkey, using default: F12"); + return { "KEY_F12" }; // default + } + auto hotkeys = children->attribs.at("InGameOverlayScreenshotHotKey"); + + // has anyone more than 4 keys to screenshot?! + std::smatch m; + if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) { + spdlog::warn("Couldn't detect overlay hotkey, using default: F12"); + return { "KEY_F12" }; // default + } + + std::vector res; + for (auto i = 1; i < m.size(); i++) { + const auto s = std::string(m[i]); + if (!s.empty()) { + res.push_back(s); + } + } + if (res.empty()) { + spdlog::warn("Couldn't detect overlay hotkey, using default: F12"); + return { "KEY_F12" }; // default + } + spdlog::info("Detected screenshot hotkey(s): {}", std::accumulate( + res.begin() + 1, res.end(), res[0], + [](auto acc, const auto curr) { return acc += "+" + curr; })); + return res; + } + + inline bool getXBCRebindingEnabled(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamPath()) + { + const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name); + if (!std::filesystem::exists(config_path)) { + spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); + return false; + } + std::ifstream config_file(config_path); + auto root = tyti::vdf::read(config_file); + + if (root.attribs.empty() || !root.attribs.contains("SteamController_XBoxSupport")) { + spdlog::warn("\"Xbox Configuration Support\" is disabled in Steam. This may cause doubled Inputs!"); + return false; + } + auto xbsup = root.attribs.at("SteamController_XBoxSupport"); + if (xbsup != "1") { + spdlog::warn("\"Xbox Configuration Support\" is disabled in Steam. This may cause doubled Inputs!"); + } + return xbsup == "1"; + } + + inline nlohmann::json getSteamConfig(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamUserId()) + { + const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name); + if (!std::filesystem::exists(config_path)) { + spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path); + return nlohmann::json(); + } + std::ifstream config_file(config_path); + auto root = tyti::vdf::read(config_file); + if (root.attribs.empty()) + { + return {}; + } + auto res = nlohmann::json::object(); + res[root.name] = nlohmann::json::object(); + for (auto& [key, value] : root.attribs) + { + res[root.name][key] = value; + } + auto parse_child = [](nlohmann::json& j, std::shared_ptr> child, auto&& recurse) -> void + { + for (auto& [key, value] : child->attribs) + { + j[key] = value; + for (auto& [childkey, childval] : child->childs) + { + j[childkey] = {}; + recurse(j[childkey], childval, recurse); + } + } + }; + for (auto& [key, value] : root.childs) + { + res[root.name][key] = {}; + parse_child(res[root.name][key], value, parse_child); + } + return res; + } + } } \ No newline at end of file From 816b89cce663ba8219e8d4485fbd82ba07e47a8b Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 21:58:56 +0100 Subject: [PATCH 20/65] SteamTarget: Support disabling standalone mode via cli-switch --- GlosSITarget/SteamTarget.cpp | 80 ++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 39 deletions(-) diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index df5878a..70925ed 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -61,48 +61,50 @@ int SteamTarget::run() auto closeBPM = false; auto closeBPMTimer = sf::Clock{}; if (!SteamOverlayDetector::IsSteamInjected()) { - spdlog::warn("GlosSI not launched via Steam.\nEnabling EXPERIMENTAL global controller and overlay..."); - if (Settings::common.standaloneModeGameId == L"") { - spdlog::error("No game id set for standalone mode. Controller will use desktop-config!"); - } + if (Settings::common.allowStandAlone) { + spdlog::warn("GlosSI not launched via Steam.\nEnabling EXPERIMENTAL global controller and overlay..."); + if (Settings::common.standaloneModeGameId == L"") { + spdlog::error("No game id set for standalone mode. Controller will use desktop-config!"); + } - SetEnvironmentVariable(L"SteamAppId", L"0"); - SetEnvironmentVariable(L"SteamClientLaunch", L"0"); - SetEnvironmentVariable(L"SteamEnv", L"1"); - SetEnvironmentVariable(L"SteamPath", steam_path_.wstring().c_str()); - SetEnvironmentVariable(L"SteamTenfoot", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); - // SetEnvironmentVariable(L"SteamTenfootHybrid", L"1"); - SetEnvironmentVariable(L"SteamGamepadUI", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); - SetEnvironmentVariable(L"SteamGameId", Settings::common.standaloneModeGameId.c_str()); - SetEnvironmentVariable(L"SteamOverlayGameId", Settings::common.standaloneModeGameId.c_str()); - SetEnvironmentVariable(L"EnableConfiguratorSupport", L"15"); - SetEnvironmentVariable(L"SteamStreamingForceWindowedD3D9", L"1"); - - if (Settings::common.standaloneUseGamepadUI) { - system("start steam://open/bigpicture"); - auto steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); - auto timer = sf::Clock{}; - while (!steamwindow && timer.getElapsedTime().asSeconds() < 2) { - steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); - Sleep(50); + SetEnvironmentVariable(L"SteamAppId", L"0"); + SetEnvironmentVariable(L"SteamClientLaunch", L"0"); + SetEnvironmentVariable(L"SteamEnv", L"1"); + SetEnvironmentVariable(L"SteamPath", steam_path_.wstring().c_str()); + SetEnvironmentVariable(L"SteamTenfoot", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); + // SetEnvironmentVariable(L"SteamTenfootHybrid", L"1"); + SetEnvironmentVariable(L"SteamGamepadUI", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); + SetEnvironmentVariable(L"SteamGameId", Settings::common.standaloneModeGameId.c_str()); + SetEnvironmentVariable(L"SteamOverlayGameId", Settings::common.standaloneModeGameId.c_str()); + SetEnvironmentVariable(L"EnableConfiguratorSupport", L"15"); + SetEnvironmentVariable(L"SteamStreamingForceWindowedD3D9", L"1"); + + if (Settings::common.standaloneUseGamepadUI) { + system("start steam://open/bigpicture"); + auto steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); + auto timer = sf::Clock{}; + while (!steamwindow && timer.getElapsedTime().asSeconds() < 2) { + steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); + Sleep(50); + } + Sleep(6000); // DIRTY HACK to wait until BPM (GamepadUI) is initialized + // TODO: find way to force BPM even if BPM is not active + LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str()); + + // Overlay switches back to desktop one, once BPM is closed... Disable closing BPM for now. + // TODO: find way to force BPM even if BPM is not active + // closeBPM = true; + closeBPMTimer.restart(); + } + else { + LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str()); } - Sleep(6000); // DIRTY HACK to wait until BPM (GamepadUI) is initialized - // TODO: find way to force BPM even if BPM is not active - LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str()); - - // Overlay switches back to desktop one, once BPM is closed... Disable closing BPM for now. - // TODO: find way to force BPM even if BPM is not active - // closeBPM = true; - closeBPMTimer.restart(); - } - else { - LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str()); - } - window_.setClickThrough(true); - if (!overlay_.expired()) - overlay_.lock()->setEnabled(false); - steam_overlay_present_ = true; + window_.setClickThrough(true); + if (!overlay_.expired()) + overlay_.lock()->setEnabled(false); + steam_overlay_present_ = true; + } } else { spdlog::info("Steam-overlay detected."); From 766cac39699d000a12f3f41b0900c14fa8a194fe Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 22:54:50 +0100 Subject: [PATCH 21/65] CEFInjectLib: make wrapper function async --- CEFInjectLib/CEFInject.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp index af74b00..b76dfe9 100644 --- a/CEFInjectLib/CEFInject.cpp +++ b/CEFInjectLib/CEFInject.cpp @@ -69,7 +69,7 @@ namespace CEFInject nlohmann::json InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port) { - return InjectJs_Unscoped(tabname, L"(function(){\n" + js + L"\n})()", port); + return InjectJs_Unscoped(tabname, L"(async function(){\n" + js + L"\n})();", port); } nlohmann::json InjectJs_Unscoped(const std::wstring &tabname, const std::wstring &js, uint16_t port) From d60def51d5104e5ff839cfebb7497446ea592b32 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sun, 29 Jan 2023 22:55:48 +0100 Subject: [PATCH 22/65] Add/Start SteamTweaks TS/JS package --- SteamTweaks/.eslintrc.js | 187 ++ SteamTweaks/.nvmrc | 1 + SteamTweaks/.vscode/launch.json | 10 + SteamTweaks/package-lock.json | 2647 +++++++++++++++++ SteamTweaks/package.json | 19 + SteamTweaks/rollup.config.js | 11 + SteamTweaks/src/@types/SteamClient.d.ts | 14 + .../src/Tweaks/Overlay/HideFPSCounter.ts | 22 + SteamTweaks/src/common/tweakApi.ts | 62 + SteamTweaks/src/common/util/types.d.ts | 3 + SteamTweaks/tsconfig.json | 25 + glossi.code-workspace | 3 + 12 files changed, 3004 insertions(+) create mode 100644 SteamTweaks/.eslintrc.js create mode 100644 SteamTweaks/.nvmrc create mode 100644 SteamTweaks/.vscode/launch.json create mode 100644 SteamTweaks/package-lock.json create mode 100644 SteamTweaks/package.json create mode 100644 SteamTweaks/rollup.config.js create mode 100644 SteamTweaks/src/@types/SteamClient.d.ts create mode 100644 SteamTweaks/src/Tweaks/Overlay/HideFPSCounter.ts create mode 100644 SteamTweaks/src/common/tweakApi.ts create mode 100644 SteamTweaks/src/common/util/types.d.ts create mode 100644 SteamTweaks/tsconfig.json diff --git a/SteamTweaks/.eslintrc.js b/SteamTweaks/.eslintrc.js new file mode 100644 index 0000000..d87fc32 --- /dev/null +++ b/SteamTweaks/.eslintrc.js @@ -0,0 +1,187 @@ +module.exports = { + extends: [ + 'eslint:recommended', + 'plugin:@typescript-eslint/eslint-recommended', + 'plugin:@typescript-eslint/recommended', + 'plugin:@typescript-eslint/recommended-requiring-type-checking', + ], + env: { + browser: true, + node: false + }, + plugins: [ + '@typescript-eslint', + 'no-null', + 'prefer-arrow', + 'import', + ], + parser: '@typescript-eslint/parser', // Specifies the ESLint parser + parserOptions: { + ecmaVersion: 2022, // Allows for the parsing of modern ECMAScript features + sourceType: 'module', + ecmaFeatures: { + jsx: false, + }, + project: ['./tsconfig.json'], + tsconfigRootDir: __dirname + }, + rules: { + '@typescript-eslint/no-namespace': 'off', + '@typescript-eslint/ban-types': 'error', + '@typescript-eslint/adjacent-overload-signatures': 'error', + '@typescript-eslint/array-type': 'error', + '@typescript-eslint/consistent-type-definitions': ['error', 'interface'], + '@typescript-eslint/no-inferrable-types': 'error', + '@typescript-eslint/no-misused-new': 'error', + '@typescript-eslint/no-this-alias': 'error', + '@typescript-eslint/prefer-for-of': 'error', + '@typescript-eslint/prefer-function-type': 'error', + '@typescript-eslint/prefer-namespace-keyword': 'error', + 'no-inner-declarations': 'off', // we have es6blocked scoped functions. + '@typescript-eslint/triple-slash-reference': 'error', + '@typescript-eslint/type-annotation-spacing': 'error', + '@typescript-eslint/unified-signatures': 'error', + '@typescript-eslint/no-explicit-any': 'error', + '@typescript-eslint/no-unused-vars': 'error', + '@typescript-eslint/unbound-method': 'warn', + '@typescript-eslint/semi': [ + 'error', + 'always' + ], + '@typescript-eslint/quotes': [ + 'warn', + 'single' + ], + '@typescript-eslint/member-delimiter-style': [ + 'error', + { + 'multiline': { + 'delimiter': 'semi', + 'requireLast': true + }, + 'singleline': { + 'delimiter': 'semi', + 'requireLast': false + } + } + ], + '@typescript-eslint/indent': [ + 'warn', + 4, + { + 'FunctionDeclaration': { + 'parameters': 'first' + }, + 'FunctionExpression': { + 'parameters': 'first' + }, + 'SwitchCase': 1 + } + ], + + '@typescript-eslint/explicit-member-accessibility': [ + 'error', + { + 'accessibility': 'explicit' + } + ], + '@typescript-eslint/no-use-before-define': ['error', { 'functions': false }], + "@typescript-eslint/naming-convention": [ + "error", + { + "selector": "default", + "format": ["camelCase", "PascalCase"] + }, + { + "selector": "variable", + "format": ["camelCase", "UPPER_CASE"] + }, + { + "selector": "parameter", + "format": ["camelCase"], + "leadingUnderscore": "allow" + }, + { + "selector": "memberLike", + "modifiers": ["private"], + "format": ["camelCase"], + "leadingUnderscore": "require" + }, + { + "selector": "typeLike", + "format": ["PascalCase"] + } + ], + 'no-console': 'off', + 'no-return-await': 'error', + 'arrow-body-style': 'error', + 'arrow-parens': [ + 'error', + 'always' + ], + 'camelcase': ['warn', { "ignoreImports": true }], + 'comma-dangle': [ + 'error', + { + 'objects': 'never', + 'arrays': 'never', + 'functions': 'never' + } + ], + 'prefer-arrow/prefer-arrow-functions': 'error', + 'prefer-arrow-callback': 'error', + 'prefer-const': 'error', + 'quote-props': [ + 'error', + 'consistent-as-needed' + ], + 'no-var': 'error', + 'new-parens': 'error', + 'no-caller': 'error', + 'no-cond-assign': 'error', + 'no-debugger': 'error', + 'no-empty': 'error', + 'no-eval': 'error', + 'no-multiple-empty-lines': 'warn', + 'no-new-wrappers': 'error', + 'no-redeclare': 'error', + 'no-shadow': [ + 'error', + { + 'hoist': 'all' + } + ], + 'no-null/no-null': 'error', + 'no-throw-literal': 'error', + 'no-trailing-spaces': 'error', + 'no-undef-init': 'error', + 'no-underscore-dangle': 'error', + 'no-unsafe-finally': 'error', + 'no-unused-labels': 'error', + 'spaced-comment': 'error', + 'use-isnan': 'error', + 'max-lines': [ + 'error', + { + 'max': 300, + 'skipBlankLines': true, + 'skipComments': true + } + ], + 'max-len': [ + 'warn', + { + 'code': 140 + } + ], + 'dot-notation': 'error', + 'eqeqeq': 'error', + 'eol-last': 'error', + 'linebreak-style': ['error', 'windows'], + 'block-spacing': ['error', 'always'], + 'object-curly-spacing': ["error", "always"], + 'import/no-deprecated': 'warn', // eslint deprecation rule sucks. just wrns on deprecated IMPORTs + }, + settings: { + }, +}; \ No newline at end of file diff --git a/SteamTweaks/.nvmrc b/SteamTweaks/.nvmrc new file mode 100644 index 0000000..dec2bf5 --- /dev/null +++ b/SteamTweaks/.nvmrc @@ -0,0 +1 @@ +19 \ No newline at end of file diff --git a/SteamTweaks/.vscode/launch.json b/SteamTweaks/.vscode/launch.json new file mode 100644 index 0000000..64a2bd7 --- /dev/null +++ b/SteamTweaks/.vscode/launch.json @@ -0,0 +1,10 @@ +{ + "configurations": [ + { + "name": "Attach to Steam CEF", + "port": 8080, + "request": "attach", + "type": "chrome", + } + ] +} \ No newline at end of file diff --git a/SteamTweaks/package-lock.json b/SteamTweaks/package-lock.json new file mode 100644 index 0000000..e8233e5 --- /dev/null +++ b/SteamTweaks/package-lock.json @@ -0,0 +1,2647 @@ +{ + "name": "glossi_steamtweaks", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "glossi_steamtweaks", + "version": "0.0.0", + "license": "Apache-2.0", + "devDependencies": { + "@rollup/plugin-typescript": "^11.0.0", + "@typescript-eslint/eslint-plugin": "^5.49.0", + "@typescript-eslint/parser": "^5.49.0", + "eslint": "^8.33.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-no-null": "^1.0.2", + "eslint-plugin-prefer-arrow": "^1.2.3", + "rollup": "^3.12.0", + "typescript": "^4.9.4" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-1.4.1.tgz", + "integrity": "sha512-XXrH9Uarn0stsyldqDYq8r++mROmWRI1xKMXa640Bb//SY1+ECYX6VzT6Lcx5frD0V30XieqJ0oX9I2Xj5aoMA==", + "dev": true, + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^9.4.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.11.8", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.8.tgz", + "integrity": "sha512-UybHIJzJnR5Qc/MsD9Kr+RpO2h+/P1GhOwdiLPXK5TWk5sgTdu88bTD9UP+CKbPPh5Rni1u0GjAdYQLemG8g+g==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@rollup/plugin-typescript": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@rollup/plugin-typescript/-/plugin-typescript-11.0.0.tgz", + "integrity": "sha512-goPyCWBiimk1iJgSTgsehFD5OOFHiAknrRJjqFCudcW8JtWiBlK284Xnn4flqMqg6YAjVG/EE+3aVzrL5qNSzQ==", + "dev": true, + "dependencies": { + "@rollup/pluginutils": "^5.0.1", + "resolve": "^1.22.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.14.0||^3.0.0", + "tslib": "*", + "typescript": ">=3.7.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + }, + "tslib": { + "optional": true + } + } + }, + "node_modules/@rollup/pluginutils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-5.0.2.tgz", + "integrity": "sha512-pTd9rIsP92h+B6wWwFbW8RkZv4hiR/xKsqre4SIuAOaOEQRxi0lqLke9k2/7WegC85GgUs9pjmOjCUi3In4vwA==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.0", + "estree-walker": "^2.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^1.20.0||^2.0.0||^3.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, + "node_modules/@types/estree": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.0.tgz", + "integrity": "sha512-WulqXMDUTYAXCjZnk6JtIHPigp55cVtDgDrO2gHRwhyJto21+1zbVCtOYB2L1F9w4qCQ0rOGWBnBe0FNTiEJIQ==", + "dev": true + }, + "node_modules/@types/json-schema": { + "version": "7.0.11", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", + "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "dev": true + }, + "node_modules/@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true + }, + "node_modules/@types/semver": { + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", + "dev": true + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.49.0.tgz", + "integrity": "sha512-IhxabIpcf++TBaBa1h7jtOWyon80SXPRLDq0dVz5SLFC/eW6tofkw/O7Ar3lkx5z5U6wzbKDrl2larprp5kk5Q==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.49.0", + "@typescript-eslint/type-utils": "5.49.0", + "@typescript-eslint/utils": "5.49.0", + "debug": "^4.3.4", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "regexpp": "^3.2.0", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.49.0.tgz", + "integrity": "sha512-veDlZN9mUhGqU31Qiv2qEp+XrJj5fgZpJ8PW30sHU+j/8/e5ruAhLaVDAeznS7A7i4ucb/s8IozpDtt9NqCkZg==", + "dev": true, + "dependencies": { + "@typescript-eslint/scope-manager": "5.49.0", + "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/typescript-estree": "5.49.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.49.0.tgz", + "integrity": "sha512-clpROBOiMIzpbWNxCe1xDK14uPZh35u4QaZO1GddilEzoCLAEz4szb51rBpdgurs5k2YzPtJeTEN3qVbG+LRUQ==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/visitor-keys": "5.49.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.49.0.tgz", + "integrity": "sha512-eUgLTYq0tR0FGU5g1YHm4rt5H/+V2IPVkP0cBmbhRyEmyGe4XvJ2YJ6sYTmONfjmdMqyMLad7SB8GvblbeESZA==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "5.49.0", + "@typescript-eslint/utils": "5.49.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/types": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.49.0.tgz", + "integrity": "sha512-7If46kusG+sSnEpu0yOz2xFv5nRz158nzEXnJFCGVEHWnuzolXKwrH5Bsf9zsNlOQkyZuk0BZKKoJQI+1JPBBg==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.49.0.tgz", + "integrity": "sha512-PBdx+V7deZT/3GjNYPVQv1Nc0U46dAHbIuOG8AZ3on3vuEKiPDwFE/lG1snN2eUB9IhF7EyF7K1hmTcLztNIsA==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/visitor-keys": "5.49.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.49.0.tgz", + "integrity": "sha512-cPJue/4Si25FViIb74sHCLtM4nTSBXtLx1d3/QT6mirQ/c65bV8arBEebBJJizfq8W2YyMoPI/WWPFWitmNqnQ==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.49.0", + "@typescript-eslint/types": "5.49.0", + "@typescript-eslint/typescript-estree": "5.49.0", + "eslint-scope": "^5.1.1", + "eslint-utils": "^3.0.0", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-scope": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", + "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/estraverse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", + "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.49.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.49.0.tgz", + "integrity": "sha512-v9jBMjpNWyn8B6k/Mjt6VbUS4J1GvUlR4x3Y+ibnP1z7y7V4n0WRz+50DY6+Myj0UaXVSuUlHohO+eZ8IJEnkg==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "5.49.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/acorn": { + "version": "8.8.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", + "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/array-includes": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.6.tgz", + "integrity": "sha512-sgTbLvL6cNnw24FnbaDyjmvddQ2ML8arZsgaJhoABMoplz/4QRhtrYS+alr1BUM1Bwp6dhx8vVCBSLG+StwOFw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "get-intrinsic": "^1.1.3", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.1.tgz", + "integrity": "sha512-roTU0KWIOmJ4DRLmwKd19Otg0/mT3qPNt0Qb3GWW8iObuZXxrjB/pzn0R3hqpRSWg4HCwqx+0vwOnWnvlOyeIA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.1.tgz", + "integrity": "sha512-8UGn9O1FDVvMNB0UlLv4voxRMze7+FpHyF5mSMRjWHUMlpoDViniy05870VlxhfgTnLbpuwTzvD76MTtWxB/mQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4", + "es-shim-unscopables": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dev": true, + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", + "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "get-intrinsic": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true + }, + "node_modules/cross-spawn": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", + "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true + }, + "node_modules/define-properties": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.4.tgz", + "integrity": "sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==", + "dev": true, + "dependencies": { + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/es-abstract": { + "version": "1.21.1", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", + "integrity": "sha512-QudMsPOz86xYz/1dG1OuGBKOELjCh99IIWHLzy5znUB6j8xG2yMA7bfTV86VSqKF+Y/H08vQPR+9jyXpuC6hfg==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "es-set-tostringtag": "^2.0.1", + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "function.prototype.name": "^1.1.5", + "get-intrinsic": "^1.1.3", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has": "^1.0.3", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "internal-slot": "^1.0.4", + "is-array-buffer": "^3.0.1", + "is-callable": "^1.2.7", + "is-negative-zero": "^2.0.2", + "is-regex": "^1.1.4", + "is-shared-array-buffer": "^1.0.2", + "is-string": "^1.0.7", + "is-typed-array": "^1.1.10", + "is-weakref": "^1.0.2", + "object-inspect": "^1.12.2", + "object-keys": "^1.1.1", + "object.assign": "^4.1.4", + "regexp.prototype.flags": "^1.4.3", + "safe-regex-test": "^1.0.0", + "string.prototype.trimend": "^1.0.6", + "string.prototype.trimstart": "^1.0.6", + "typed-array-length": "^1.0.4", + "unbox-primitive": "^1.0.2", + "which-typed-array": "^1.1.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz", + "integrity": "sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz", + "integrity": "sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "8.33.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.33.0.tgz", + "integrity": "sha512-WjOpFQgKK8VrCnAtl8We0SUOy/oVZ5NHykyMiagV1M9r8IFpIJX7DduK6n1mpfhlG7T1NLWm2SuD8QB7KFySaA==", + "dev": true, + "dependencies": { + "@eslint/eslintrc": "^1.4.1", + "@humanwhocodes/config-array": "^0.11.8", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "ajv": "^6.10.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.2", + "debug": "^4.3.2", + "doctrine": "^3.0.0", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^7.1.1", + "eslint-utils": "^3.0.0", + "eslint-visitor-keys": "^3.3.0", + "espree": "^9.4.0", + "esquery": "^1.4.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^6.0.1", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "grapheme-splitter": "^1.0.4", + "ignore": "^5.2.0", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", + "js-sdsl": "^4.1.4", + "js-yaml": "^4.1.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.4.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.1", + "regexpp": "^3.2.0", + "strip-ansi": "^6.0.1", + "strip-json-comments": "^3.1.0", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.7.tgz", + "integrity": "sha512-gozW2blMLJCeFpBwugLTGyvVjNoeo1knonXAcatC6bjPBZitotxdWf7Gimr25N4c0AAOo4eOUfaG82IJPDpqCA==", + "dev": true, + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.11.0", + "resolve": "^1.22.1" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.7.4", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.7.4.tgz", + "integrity": "sha512-j4GT+rqzCoRKHwURX7pddtIPGySnX9Si/cgMI5ztrcqOPtk5dDEeZ34CQVPphnqkJytlc97Vuk05Um2mJ3gEQA==", + "dev": true, + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.27.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.27.5.tgz", + "integrity": "sha512-LmEt3GVofgiGuiE+ORpnvP+kAm3h6MLZJ4Q5HCyHADofsb4VzXFsRiWj3c0OFiV+3DWFh0qg3v9gcPlfc3zRow==", + "dev": true, + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "array.prototype.flatmap": "^1.3.1", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.7", + "eslint-module-utils": "^2.7.4", + "has": "^1.0.3", + "is-core-module": "^2.11.0", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.values": "^1.1.6", + "resolve": "^1.22.1", + "semver": "^6.3.0", + "tsconfig-paths": "^3.14.1" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eslint-plugin-import/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/eslint-plugin-no-null": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-no-null/-/eslint-plugin-no-null-1.0.2.tgz", + "integrity": "sha512-uRDiz88zCO/2rzGfgG15DBjNsgwWtWiSo4Ezy7zzajUgpnFIqd1TjepKeRmJZHEfBGu58o2a8S0D7vglvvhkVA==", + "dev": true, + "engines": { + "node": ">=5.0.0" + }, + "peerDependencies": { + "eslint": ">=3.0.0" + } + }, + "node_modules/eslint-plugin-prefer-arrow": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-prefer-arrow/-/eslint-plugin-prefer-arrow-1.2.3.tgz", + "integrity": "sha512-J9I5PKCOJretVuiZRGvPQxCbllxGAV/viI20JO3LYblAodofBxyMnZAJ+WGeClHgANnSJberTNoFWWjrWKBuXQ==", + "dev": true, + "peerDependencies": { + "eslint": ">=2.0.0" + } + }, + "node_modules/eslint-scope": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", + "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", + "dev": true, + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/eslint-utils": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", + "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^2.0.0" + }, + "engines": { + "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + }, + "peerDependencies": { + "eslint": ">=5" + } + }, + "node_modules/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", + "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "dev": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", + "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/espree": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", + "dev": true, + "dependencies": { + "acorn": "^8.8.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", + "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "dev": true, + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.2.12", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", + "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true + }, + "node_modules/fastq": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", + "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", + "dev": true, + "dependencies": { + "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", + "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "dev": true, + "dependencies": { + "flatted": "^3.1.0", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" + } + }, + "node_modules/flatted": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", + "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "dev": true + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true + }, + "node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==", + "dev": true + }, + "node_modules/function.prototype.name": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.5.tgz", + "integrity": "sha512-uN7m/BzVKQnCUF/iW8jYea67v++2u7m5UgENbHRtdDVclOUP+FMPlCNdmk0h/ysGyo2tavMJEDqJAkJdRa1vMA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "es-abstract": "^1.19.0", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", + "integrity": "sha512-L049y6nFOuom5wGyRc3/gdTLO94dySVKRACj1RmJZBQXlbTMhtNIgkWkUHq+jYmZvKf14EW1EoJnnjbmoHij0Q==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "13.20.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.20.0.tgz", + "integrity": "sha512-Qg5QtVkCy/kv3FUSlu4ukeZDVf9ee0iXLAUYX13gbR17bnejFTzr4iS9bY7kwCf1NztRNm1t91fjOiyx4CSwPQ==", + "dev": true, + "dependencies": { + "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "dev": true, + "dependencies": { + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/grapheme-splitter": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz", + "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", + "dev": true + }, + "node_modules/has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.1" + }, + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz", + "integrity": "sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ignore": { + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/import-fresh": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", + "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "dev": true, + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dev": true, + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/internal-slot": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.4.tgz", + "integrity": "sha512-tA8URYccNzMo94s5MQZgH8NB/XTa6HsOo0MLfXTKKEnHVVdegzaQoFZ7Jp44bdvLvY2waT5dc+j5ICEswhi7UQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3", + "has": "^1.0.3", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.1.tgz", + "integrity": "sha512-ASfLknmY8Xa2XtB4wmbz13Wu202baeA18cJBCeCy0wXUHZF0IPyVEXqKEcd+t2fNSLLL1vC6k7lxZEojNbISXQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", + "integrity": "sha512-RRjxlvLDkD1YJwDbroBHMb+cukurkDWNyHx7D3oNB5x9rb5ogcksMC5wHCadcXoo67gVr/+3GFySh3134zi6rw==", + "dev": true, + "dependencies": { + "has": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.10", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.10.tgz", + "integrity": "sha512-PJqgEHiWZvMpaFZ3uTc8kHPM4+4ADTlDniuQL7cU/UDA0Ql7F70yGfHph3cLNe+c9toaigv+DFzTJKhc2CtO6A==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/js-sdsl": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.3.0.tgz", + "integrity": "sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true + }, + "node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.7.tgz", + "integrity": "sha512-bzfL1YUZsP41gmu/qjrEk0Q6i2ix/cVeAhbCbqH9u3zYutS1cLg00qhrD0M2MVdCcx4Sc0UpP2eBWo9rotpq6g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true + }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, + "node_modules/object-inspect": { + "version": "1.12.3", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", + "integrity": "sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.4.tgz", + "integrity": "sha512-1mxKf0e58bvyjSCtKYY4sRe9itRk3PJpquJOjeIkz885CczcI4IvJJDLPS72oowuSh+pBxUFROpX+TU++hxhZQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.values": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.6.tgz", + "integrity": "sha512-FVVTkD1vENCsAcwNs9k6jea2uHC/X0+JcjG8YA60FN5CMaJmG95wT9jek/xX9nornqGRrBkKtzuAu2wuHpKqvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", + "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "dev": true, + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", + "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/regexp.prototype.flags": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", + "integrity": "sha512-fjggEOO3slI6Wvgjwflkc4NFRCTZAu5CnNfBd5qOMYhWdn67nJBBu34/TkD++eeFmd8C9r9jfXJ27+nSiRkSUA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.3", + "functions-have-names": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexpp": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", + "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/mysticatea" + } + }, + "node_modules/resolve": { + "version": "1.22.1", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", + "integrity": "sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==", + "dev": true, + "dependencies": { + "is-core-module": "^2.9.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rollup": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-3.12.0.tgz", + "integrity": "sha512-4MZ8kA2HNYahIjz63rzrMMRvDqQDeS9LoriJvMuV0V6zIGysP36e9t4yObUfwdT9h/szXoHQideICftcdZklWg==", + "dev": true, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=14.18.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/semver": { + "version": "7.3.8", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", + "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", + "integrity": "sha512-JySq+4mrPf9EsDBEDYMOb/lM7XQLulwg5R/m1r0PXEFqrV0qHvl58sdTilSXtKOflCsK2E8jxf+GKC0T07RWwQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz", + "integrity": "sha512-omqjMDaY92pbn5HOX7f9IccLA+U1tA9GvtU4JrodiXFfYB7jPzzHpRzpglLAjtUV6bB557zwClJezTqnAiYnQA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.1.4", + "es-abstract": "^1.20.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.14.1.tgz", + "integrity": "sha512-fxDhWnFSLt3VuTwtvJt5fpwxBHg5AdKWMsgcPOOIilyjymcYVZoCQF8fvFRezCNfblEXmi+PcM1eYHeOAgXCOQ==", + "dev": true, + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.1", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/tsutils": { + "version": "3.21.0", + "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", + "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", + "dev": true, + "dependencies": { + "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-fest": { + "version": "0.20.2", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", + "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.9.tgz", + "integrity": "sha512-w9c4xkx6mPidwp7180ckYWfMmvxpjlZuIudNtDf4N/tTAUB8VJbX25qZoAsrtGuYNnGw3pa0AXgbGKRB8/EceA==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", + "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/SteamTweaks/package.json b/SteamTweaks/package.json new file mode 100644 index 0000000..2515d70 --- /dev/null +++ b/SteamTweaks/package.json @@ -0,0 +1,19 @@ +{ + "name": "glossi_steamtweaks", + "version": "0.0.0", + "type": "module", + "scripts": {}, + "author": "Peter Repukat - FlatspotSoftware", + "license": "Apache-2.0", + "devDependencies": { + "@rollup/plugin-typescript": "^11.0.0", + "@typescript-eslint/eslint-plugin": "^5.49.0", + "@typescript-eslint/parser": "^5.49.0", + "eslint": "^8.33.0", + "eslint-plugin-import": "^2.27.5", + "eslint-plugin-no-null": "^1.0.2", + "eslint-plugin-prefer-arrow": "^1.2.3", + "rollup": "^3.12.0", + "typescript": "^4.9.4" + } +} diff --git a/SteamTweaks/rollup.config.js b/SteamTweaks/rollup.config.js new file mode 100644 index 0000000..c77730b --- /dev/null +++ b/SteamTweaks/rollup.config.js @@ -0,0 +1,11 @@ +import typescript from '@rollup/plugin-typescript'; + +export default { + input: 'src/Tweaks/Overlay/HideFPSCounter.ts', + output: { + file: 'dist/glossiTweaks.js', + sourcemap: "inline", + format: 'es', + }, + plugins: [typescript()] +}; \ No newline at end of file diff --git a/SteamTweaks/src/@types/SteamClient.d.ts b/SteamTweaks/src/@types/SteamClient.d.ts new file mode 100644 index 0000000..c2072e5 --- /dev/null +++ b/SteamTweaks/src/@types/SteamClient.d.ts @@ -0,0 +1,14 @@ +export interface SteamClient { + Settings: { + SetInGameOverlayShowFPSCorner: (value: 0|1|2|3|4) => void; + SetInGameOverlayShowFPSContrast: (value: boolean) => void; + }; +} + +declare global { + interface Window { + SteamClient: SteamClient; + } + // eslint-disable-next-line + declare const SteamClient: SteamClient; +} diff --git a/SteamTweaks/src/Tweaks/Overlay/HideFPSCounter.ts b/SteamTweaks/src/Tweaks/Overlay/HideFPSCounter.ts new file mode 100644 index 0000000..f7865cc --- /dev/null +++ b/SteamTweaks/src/Tweaks/Overlay/HideFPSCounter.ts @@ -0,0 +1,22 @@ + +import type { SteamConfig } from "../../common/util/types"; +import { initTweak } from "../../common/tweakApi"; + +// variables here are scoped to the tweak +// and are not accessible from other tweaks or even the main script +// there is no risk of conflicts + + +const originalFpsCorner = Number( + ((await GlosSI.getSettings()).system as SteamConfig) + .InGameOverlayShowFPSCorner +) as 0 | 1 | 2 | 3 | 4; + +initTweak('HideFPSCounter', { + install: () => { + SteamClient.Settings.SetInGameOverlayShowFPSCorner(0); + }, + uninstall: () => { + SteamClient.Settings.SetInGameOverlayShowFPSCorner(originalFpsCorner); + } +}); diff --git a/SteamTweaks/src/common/tweakApi.ts b/SteamTweaks/src/common/tweakApi.ts new file mode 100644 index 0000000..df2544e --- /dev/null +++ b/SteamTweaks/src/common/tweakApi.ts @@ -0,0 +1,62 @@ +import type { SteamConfig } from './util/types'; +class GlosSIApi { + public getSettings(): Promise { + return fetch('http://localhost:8756/steam_settings') + .then( + (res) => res.json().then( + (json) => (json as SteamConfig).UserLocalConfigStore as SteamConfig + ) + ); + } +} + +declare global { + interface Window { + GlosSITweaks: Record unknown; uninstall?: () => void}>; + GlosSI: InstanceType; + } + // eslint-disable-next-line + const GlosSI: InstanceType; +} + + +const installGlosSIApi = () => { + window.GlosSITweaks = { + GlosSI: { + install: () => { + const api = new GlosSIApi(); + Object.assign(window, { GlosSI: api }); + }, + uninstall: () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + delete window.GlosSI; + } + } + }; + window.GlosSITweaks.GlosSI.install(); +}; + +if (!window.GlosSITweaks || !window.GlosSI) { + installGlosSIApi(); +} + +export const initTweak = (name: string, tweakMain: (() => T)|{ + install: () => T; + uninstall: () => void; +}, force = false): T|Error => { + if (!force && window.GlosSITweaks[name]) { + return new Error(`Tweak ${name} is already installed!`); + } + + if (typeof tweakMain === 'object') { + window.GlosSITweaks[name] = { install: tweakMain.install, uninstall: () => { + tweakMain.uninstall(); + delete window.GlosSITweaks[name]; + } }; + } else { + window.GlosSITweaks[name] = { install: tweakMain }; + } + return window.GlosSITweaks[name].install() as T; + +}; diff --git a/SteamTweaks/src/common/util/types.d.ts b/SteamTweaks/src/common/util/types.d.ts new file mode 100644 index 0000000..b179cd5 --- /dev/null +++ b/SteamTweaks/src/common/util/types.d.ts @@ -0,0 +1,3 @@ +export interface SteamConfig { + [key: string]: string|SteamConfig; +} diff --git a/SteamTweaks/tsconfig.json b/SteamTweaks/tsconfig.json new file mode 100644 index 0000000..1f74fb2 --- /dev/null +++ b/SteamTweaks/tsconfig.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "target": "ES2020", + "module": "ES2022", + "noImplicitAny": true, + "rootDir": "./src", + "outDir": "./dist", + "inlineSourceMap": true, + "esModuleInterop": true, + "strict": true, + "skipLibCheck": true, + "useDefineForClassFields": true, + "forceConsistentCasingInFileNames": true, + "lib": [ + "esnext", + "DOM" + ], + }, + "include": [ + "src/**/*" + ], + "exclude": [ + "node_modules" + ] +} \ No newline at end of file diff --git a/glossi.code-workspace b/glossi.code-workspace index 8a21769..83d064a 100644 --- a/glossi.code-workspace +++ b/glossi.code-workspace @@ -21,6 +21,9 @@ { "path": "Installer" }, + { + "path": "SteamTweaks" + }, { "name": "root", "path": "." From a07faa926c090968714720149699c59e9ab0ad6c Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 12:05:11 +0100 Subject: [PATCH 23/65] CEFInject: Kill manual scoping of injection and let rollup handle that crap --- CEFInjectLib/CEFInject.cpp | 8 +------- CEFInjectLib/CEFInject.h | 1 - 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp index b76dfe9..297e484 100644 --- a/CEFInjectLib/CEFInject.cpp +++ b/CEFInjectLib/CEFInject.cpp @@ -66,13 +66,7 @@ namespace CEFInject return tabs; } - - nlohmann::json InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port) - { - return InjectJs_Unscoped(tabname, L"(async function(){\n" + js + L"\n})();", port); - } - - nlohmann::json InjectJs_Unscoped(const std::wstring &tabname, const std::wstring &js, uint16_t port) + nlohmann::json InjectJs(const std::wstring &tabname, const std::wstring &js, uint16_t port) { auto cli = internal::GetHttpClient(port); if (auto res = cli.Get("/json")) { diff --git a/CEFInjectLib/CEFInject.h b/CEFInjectLib/CEFInject.h index 8c85118..fc077f6 100644 --- a/CEFInjectLib/CEFInject.h +++ b/CEFInjectLib/CEFInject.h @@ -32,6 +32,5 @@ namespace CEFInject bool CEFDebugAvailable(uint16_t port = internal::port_); std::vector AvailableTabs(uint16_t port = internal::port_); nlohmann::json InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port = internal::port_); - nlohmann::json InjectJs_Unscoped(const std::wstring& tabname, const std::wstring& js, uint16_t port = internal::port_); } \ No newline at end of file From b26fd9faf538801f65450647302b17413294a391 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 12:05:33 +0100 Subject: [PATCH 24/65] Update SteamTweaks --- SteamTweaks/rollup.config.js | 51 ++++++++++++++---- SteamTweaks/src/GlosSITweaks.ts | 54 +++++++++++++++++++ .../src/Tweaks/Overlay/HideFPSCounter.ts | 20 +++---- SteamTweaks/src/common/tweakApi.ts | 42 --------------- SteamTweaks/tsconfig.json | 3 +- 5 files changed, 106 insertions(+), 64 deletions(-) create mode 100644 SteamTweaks/src/GlosSITweaks.ts diff --git a/SteamTweaks/rollup.config.js b/SteamTweaks/rollup.config.js index c77730b..3eec110 100644 --- a/SteamTweaks/rollup.config.js +++ b/SteamTweaks/rollup.config.js @@ -1,11 +1,44 @@ import typescript from '@rollup/plugin-typescript'; +import { readdirSync, lstatSync } from 'fs'; +import path from 'path'; -export default { - input: 'src/Tweaks/Overlay/HideFPSCounter.ts', - output: { - file: 'dist/glossiTweaks.js', - sourcemap: "inline", - format: 'es', - }, - plugins: [typescript()] -}; \ No newline at end of file +const getFileListForDir = (dir) => { + return readdirSync(dir).map((file) => { + const absolute = path.resolve(dir, file); + if (file.endsWith('.ts')) { + return absolute; + } + if (lstatSync(absolute).isDirectory()) { + return getFileListForDir(absolute) + } + }).flat(999); + +} + + +const tsPluginConf = typescript({ + cacheDir: '.rollup.tscache' +}); + +export default [ + { + input: 'src/GlosSITweaks.ts', + output: { + dir: 'dist', + sourcemap: "inline", + format: 'iife', + }, + plugins: [tsPluginConf], + }, + ...getFileListForDir('src/Tweaks').map((file) => { + return { + input: file, + output: { + file: file.replace('src', 'dist').replace(/\.ts$/, '.js'), + sourcemap: "inline", + format: 'iife', + }, + plugins: [tsPluginConf], + } + }) +]; \ No newline at end of file diff --git a/SteamTweaks/src/GlosSITweaks.ts b/SteamTweaks/src/GlosSITweaks.ts new file mode 100644 index 0000000..dd5140b --- /dev/null +++ b/SteamTweaks/src/GlosSITweaks.ts @@ -0,0 +1,54 @@ +import type { SteamConfig } from './common/util/types'; +class SteamTargetApi { + public getSteamSettings(): Promise { + return fetch('http://localhost:8756/steam_settings') + .then( + (res) => res.json().then( + (json) => (json as SteamConfig).UserLocalConfigStore as SteamConfig + ) + ); + } +} + +class GlosSIApiCtor { + public readonly SteamTarget: SteamTargetApi = new SteamTargetApi(); +} + +interface GlosSITweaks { + [tweakName: string]: { readonly install: () => unknown; readonly uninstall?: () => void } +} + +declare global { + interface Window { + GlosSITweaks: GlosSITweaks + GlosSIApi: InstanceType; + } + + // eslint-disable-next-line + const GlosSIApi: InstanceType; + const GlosSITweaks: GlosSITweaks +} + + +const installGlosSIApi = () => { + window.GlosSITweaks = { + GlosSI: { + install: () => { + const api = new GlosSIApiCtor(); + Object.assign(window, { GlosSIApi: api }); + }, + uninstall: () => { + Object.values(window.GlosSITweaks) + .forEach((obj) => obj.uninstall?.()); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + delete window.GlosSIApi; + } + } + }; + window.GlosSITweaks.GlosSI.install(); +}; + +if (!window.GlosSITweaks || !window.GlosSIApi) { + installGlosSIApi(); +} diff --git a/SteamTweaks/src/Tweaks/Overlay/HideFPSCounter.ts b/SteamTweaks/src/Tweaks/Overlay/HideFPSCounter.ts index f7865cc..c8a37a8 100644 --- a/SteamTweaks/src/Tweaks/Overlay/HideFPSCounter.ts +++ b/SteamTweaks/src/Tweaks/Overlay/HideFPSCounter.ts @@ -2,21 +2,17 @@ import type { SteamConfig } from "../../common/util/types"; import { initTweak } from "../../common/tweakApi"; -// variables here are scoped to the tweak -// and are not accessible from other tweaks or even the main script -// there is no risk of conflicts - -const originalFpsCorner = Number( - ((await GlosSI.getSettings()).system as SteamConfig) - .InGameOverlayShowFPSCorner -) as 0 | 1 | 2 | 3 | 4; - -initTweak('HideFPSCounter', { - install: () => { +const backup: { originalFpsCorner?: number } = {}; +initTweak('AnotherTweak', { + install: async () => { + backup.originalFpsCorner = Number( + ((await GlosSIApi.SteamTarget.getSteamSettings()).system as SteamConfig) + .InGameOverlayShowFPSCorner + ) as 0 | 1 | 2 | 3 | 4; SteamClient.Settings.SetInGameOverlayShowFPSCorner(0); }, uninstall: () => { - SteamClient.Settings.SetInGameOverlayShowFPSCorner(originalFpsCorner); + SteamClient.Settings.SetInGameOverlayShowFPSCorner((backup.originalFpsCorner ?? 0) as 0 | 1 | 2 | 3 | 4); } }); diff --git a/SteamTweaks/src/common/tweakApi.ts b/SteamTweaks/src/common/tweakApi.ts index df2544e..7b3ad99 100644 --- a/SteamTweaks/src/common/tweakApi.ts +++ b/SteamTweaks/src/common/tweakApi.ts @@ -1,45 +1,3 @@ -import type { SteamConfig } from './util/types'; -class GlosSIApi { - public getSettings(): Promise { - return fetch('http://localhost:8756/steam_settings') - .then( - (res) => res.json().then( - (json) => (json as SteamConfig).UserLocalConfigStore as SteamConfig - ) - ); - } -} - -declare global { - interface Window { - GlosSITweaks: Record unknown; uninstall?: () => void}>; - GlosSI: InstanceType; - } - // eslint-disable-next-line - const GlosSI: InstanceType; -} - - -const installGlosSIApi = () => { - window.GlosSITweaks = { - GlosSI: { - install: () => { - const api = new GlosSIApi(); - Object.assign(window, { GlosSI: api }); - }, - uninstall: () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - delete window.GlosSI; - } - } - }; - window.GlosSITweaks.GlosSI.install(); -}; - -if (!window.GlosSITweaks || !window.GlosSI) { - installGlosSIApi(); -} export const initTweak = (name: string, tweakMain: (() => T)|{ install: () => T; diff --git a/SteamTweaks/tsconfig.json b/SteamTweaks/tsconfig.json index 1f74fb2..44bc984 100644 --- a/SteamTweaks/tsconfig.json +++ b/SteamTweaks/tsconfig.json @@ -11,6 +11,7 @@ "skipLibCheck": true, "useDefineForClassFields": true, "forceConsistentCasingInFileNames": true, + "incremental": true, "lib": [ "esnext", "DOM" @@ -20,6 +21,6 @@ "src/**/*" ], "exclude": [ - "node_modules" + "node_modules", ] } \ No newline at end of file From 18241c1fc485c35ccba33bbae19a22e5db5d7a59 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 12:47:58 +0100 Subject: [PATCH 25/65] Update ViGEmCLient: DS4 dispose deadlock fix --- GlosSITarget/InputRedirector.cpp | 4 ++++ ViGEm_BuildConfig.patch | 40 ++++++++++++++++++++++++++++---- deps/ViGEmClient | 2 +- 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/GlosSITarget/InputRedirector.cpp b/GlosSITarget/InputRedirector.cpp index 7a8dc3d..1854861 100644 --- a/GlosSITarget/InputRedirector.cpp +++ b/GlosSITarget/InputRedirector.cpp @@ -216,6 +216,10 @@ void InputRedirector::runLoop() vigem_target_get_pid(vt_pad_[i])); if (Settings::controller.emulateDS4) { + // TODO: make sense of DS4_OUTPUT_BUFFER + // there is no doc? Ask @Nef about this... + // ReSharper disable once CppDeprecatedEntity +#pragma warning(disable : 4996) const auto callback_register_res = vigem_target_ds4_register_notification( driver_, vt_pad_[i], diff --git a/ViGEm_BuildConfig.patch b/ViGEm_BuildConfig.patch index eba667b..261693a 100644 --- a/ViGEm_BuildConfig.patch +++ b/ViGEm_BuildConfig.patch @@ -1,8 +1,38 @@ -diff --git a/src/ViGEmClient.vcxproj b/src/ViGEmClient.vcxproj -index 7c186414e62a6334fbcd518d506f55db57491dfe..6b71920a9b848e4a5a3ffb314a2e009a7a22502f 100644 ---- a/src/ViGEmClient.vcxproj -+++ b/src/ViGEmClient.vcxproj -@@ -75,26 +75,26 @@ +diff --git forkSrcPrefix/src/ViGEmClient.vcxproj forkDstPrefix/src/ViGEmClient.vcxproj +index 7c186414e62a6334fbcd518d506f55db57491dfe..2955df4d391b37bf0b71e17df8bd161c1014a0c4 100644 +--- forkSrcPrefix/src/ViGEmClient.vcxproj ++++ forkDstPrefix/src/ViGEmClient.vcxproj +@@ -49,52 +49,52 @@ + + StaticLibrary + true +- v142 ++ v143 + Unicode + + + DynamicLibrary + true +- v142 ++ v143 + Unicode + + + StaticLibrary + false +- v142 ++ v143 + true + Unicode + + + DynamicLibrary + false +- v142 ++ v143 + true + Unicode + StaticLibrary true diff --git a/deps/ViGEmClient b/deps/ViGEmClient index 3bc2cee..20fb9cb 160000 --- a/deps/ViGEmClient +++ b/deps/ViGEmClient @@ -1 +1 @@ -Subproject commit 3bc2cee48ab0b10b5dd31323a621677175cfb00d +Subproject commit 20fb9cb05275acad6ab5c786160656d8f83e0c6f From 226b8368b15bc688e981009abd5c9b38968b9f3e Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 12:52:32 +0100 Subject: [PATCH 26/65] Stop trying to build "common"-project files on their own --- GlosSI.sln | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/GlosSI.sln b/GlosSI.sln index 736ba44..05714a1 100644 --- a/GlosSI.sln +++ b/GlosSI.sln @@ -65,11 +65,9 @@ Global {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x86.ActiveCfg = Release|Win32 {74FBA967-AB7E-43EA-B561-3F4821954B3B}.Release|x86.Build.0 = Release|Win32 {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Debug|x64.ActiveCfg = Debug|x64 - {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Debug|x64.Build.0 = Debug|x64 {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Debug|x86.ActiveCfg = Debug|Win32 {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Debug|x86.Build.0 = Debug|Win32 {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Release|x64.ActiveCfg = Release|x64 - {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Release|x64.Build.0 = Release|x64 {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Release|x86.ActiveCfg = Release|Win32 {DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Release|x86.Build.0 = Release|Win32 EndGlobalSection @@ -77,7 +75,7 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - Qt5Version = 6.2.2_msvc2019_64 SolutionGuid = {789386B6-7D1E-4F9C-BF2E-9B5EDC3BB7C8} + Qt5Version = 6.2.2_msvc2019_64 EndGlobalSection EndGlobal From b35f422a54364c367b74119045c3a7ba50405840 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 13:12:46 +0100 Subject: [PATCH 27/65] SteamTweaks: update .gitignore --- SteamTweaks/.gitignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 SteamTweaks/.gitignore diff --git a/SteamTweaks/.gitignore b/SteamTweaks/.gitignore new file mode 100644 index 0000000..7cfc1a2 --- /dev/null +++ b/SteamTweaks/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +tsconfig.tsbuildinfo +.rollup.tscache/ \ No newline at end of file From b0b67f8c9dfe1cd990f644110b96eb938deb2029 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 17:17:10 +0100 Subject: [PATCH 28/65] SteamTweaks: Move Overlay-Tweaks that run SteamSharedsContext to subfolder --- .../src/Tweaks/Overlay/{ => SharedContext}/HideFPSCounter.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) rename SteamTweaks/src/Tweaks/Overlay/{ => SharedContext}/HideFPSCounter.ts (81%) diff --git a/SteamTweaks/src/Tweaks/Overlay/HideFPSCounter.ts b/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts similarity index 81% rename from SteamTweaks/src/Tweaks/Overlay/HideFPSCounter.ts rename to SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts index c8a37a8..bfb99d0 100644 --- a/SteamTweaks/src/Tweaks/Overlay/HideFPSCounter.ts +++ b/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts @@ -1,6 +1,6 @@ -import type { SteamConfig } from "../../common/util/types"; -import { initTweak } from "../../common/tweakApi"; +import type { SteamConfig } from "../../../common/util/types"; +import { initTweak } from "../../../common/tweakApi"; const backup: { originalFpsCorner?: number } = {}; From b86d548fc28a28b5baada45ab54e8952171e6fd6 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 18:19:36 +0100 Subject: [PATCH 29/65] Support delayed target initialization and delay init if XBox controller rebinding is disabled in Steam --- GlosSITarget/Overlay.cpp | 31 +++++- GlosSITarget/Overlay.h | 4 +- GlosSITarget/Resource.rc | 8 +- GlosSITarget/SteamTarget.cpp | 208 +++++++++++++++++++++-------------- GlosSITarget/SteamTarget.h | 3 + 5 files changed, 160 insertions(+), 94 deletions(-) diff --git a/GlosSITarget/Overlay.cpp b/GlosSITarget/Overlay.cpp index 44e0581..7455fea 100644 --- a/GlosSITarget/Overlay.cpp +++ b/GlosSITarget/Overlay.cpp @@ -163,12 +163,16 @@ void Overlay::update() const auto remain_millis = SPLASH_DURATION_S_ * 1000 - millis; if (remain_millis <= fade_millis) { showSplash(static_cast(remain_millis) / static_cast(fade_millis) * 128.f); - } else { + } + else { showSplash(128); } } if (Settings::window.disableGlosSIOverlay) { + std::ranges::for_each(FORCED_OVERLAY_ELEMS_, [this](const auto& elem) { + elem.second(window_.hasFocus(), 0); + }); ImGui::SFML::Render(window_); return; } @@ -211,6 +215,17 @@ void Overlay::update() closeOverlayButton(); } + std::ranges::for_each(FORCED_OVERLAY_ELEMS_, [this](const auto& elem) { + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.05f, 0.07f, 0.07f, 0.95f)); + ImGui::SetNextWindowPos({ + ImGui::GetMainViewport()->Size.x * 0.5f, + ImGui::GetMainViewport()->Size.y * 0.5f + }, ImGuiCond_Always, + {0.5f, 0.5f}); + elem.second(window_.hasFocus(), 0); + ImGui::PopStyleColor(); + }); + ImGui::SFML::Render(window_); } @@ -229,9 +244,14 @@ void Overlay::AddLog(const spdlog::details::log_msg& msg) LOG_MSGS_.push_back({.time = msg.time, .level = msg.level, .payload = msg.payload.data()}); } -int Overlay::AddOverlayElem(const std::function& elem_fn) +int Overlay::AddOverlayElem(const std::function& elem_fn, bool force_show) { - OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn}); + if (force_show) { + FORCED_OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn}); + } + else { + OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn}); + } // keep this non confusing, but longer... const auto res = overlay_element_id_; overlay_element_id_++; @@ -240,7 +260,10 @@ int Overlay::AddOverlayElem(const std::function& elem_fn); + static int AddOverlayElem(const std::function& elem_fn, bool force_show_ = false); static void RemoveOverlayElem(int id); private: @@ -71,6 +71,8 @@ class Overlay { static inline int overlay_element_id_ = 0; static inline std::map> OVERLAY_ELEMS_; + static inline std::map> FORCED_OVERLAY_ELEMS_; + #ifdef _WIN32 std::string config_file_name_; #endif diff --git a/GlosSITarget/Resource.rc b/GlosSITarget/Resource.rc index 8346603..ba91c46 100644 --- a/GlosSITarget/Resource.rc +++ b/GlosSITarget/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,2,0018002705604 - PRODUCTVERSION 0,1,2,0018002705604 + FILEVERSION 0,1,2,0031000204300 + PRODUCTVERSION 0,1,2,0031000204300 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - SteamTarget" - VALUE "FileVersion", "0.1.2.0-18-g27056b4" + VALUE "FileVersion", "0.1.2.0-31-gd2b43ff" VALUE "InternalName", "GlosSITarget" VALUE "LegalCopyright", "Copyright (C) 2021-2022 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSITarget.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.2.0-18-g27056b4" + VALUE "ProductVersion", "0.1.2.0-31-gd2b43ff" END END BLOCK "VarFileInfo" diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 70925ed..c52ec56 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -46,14 +46,6 @@ SteamTarget::SteamTarget() server_(launcher_, [this] { run_ = false; }) { target_window_handle_ = window_.getSystemHandle(); -#ifdef _WIN32 - if (Settings::common.no_uwp_overlay) { - UWPOverlayEnabler::AddUwpOverlayOvWidget(); - } - else { - UWPOverlayEnabler::EnableUwpOverlay(); - } -#endif } int SteamTarget::run() @@ -67,93 +59,43 @@ int SteamTarget::run() spdlog::error("No game id set for standalone mode. Controller will use desktop-config!"); } - SetEnvironmentVariable(L"SteamAppId", L"0"); - SetEnvironmentVariable(L"SteamClientLaunch", L"0"); - SetEnvironmentVariable(L"SteamEnv", L"1"); - SetEnvironmentVariable(L"SteamPath", steam_path_.wstring().c_str()); - SetEnvironmentVariable(L"SteamTenfoot", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); - // SetEnvironmentVariable(L"SteamTenfootHybrid", L"1"); - SetEnvironmentVariable(L"SteamGamepadUI", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); - SetEnvironmentVariable(L"SteamGameId", Settings::common.standaloneModeGameId.c_str()); - SetEnvironmentVariable(L"SteamOverlayGameId", Settings::common.standaloneModeGameId.c_str()); - SetEnvironmentVariable(L"EnableConfiguratorSupport", L"15"); - SetEnvironmentVariable(L"SteamStreamingForceWindowedD3D9", L"1"); - - if (Settings::common.standaloneUseGamepadUI) { - system("start steam://open/bigpicture"); - auto steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); - auto timer = sf::Clock{}; - while (!steamwindow && timer.getElapsedTime().asSeconds() < 2) { - steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); - Sleep(50); + std::vector> end_frame_callbacks; + + if (!util::steam::getXBCRebindingEnabled(steam_path_, steam_user_id_)) { + auto overlay_id = std::make_shared(-1); + *overlay_id = Overlay::AddOverlayElem( + [this, overlay_id, &end_frame_callbacks](bool window_has_focus, ImGuiID dockspace_id) { + can_fully_initialize_ = false; + ImGui::Begin("XBox Controller configuration support Disabled", nullptr, ImGuiWindowFlags_NoSavedSettings); + ImGui::TextColored({1.f, 0.8f, 0.f, 1.f}, "XBox Controller configuration support is disabled in Steam. Please enable it in Steam Settings."); + if (ImGui::Button("OK")) { + can_fully_initialize_ = true; + if (*overlay_id != -1) { + end_frame_callbacks.emplace_back([this, overlay_id] { + Overlay::RemoveOverlayElem(*overlay_id); + }); + } } - Sleep(6000); // DIRTY HACK to wait until BPM (GamepadUI) is initialized - // TODO: find way to force BPM even if BPM is not active - LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str()); - - // Overlay switches back to desktop one, once BPM is closed... Disable closing BPM for now. - // TODO: find way to force BPM even if BPM is not active - // closeBPM = true; - closeBPMTimer.restart(); - } - else { - LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str()); - } - - window_.setClickThrough(true); - if (!overlay_.expired()) - overlay_.lock()->setEnabled(false); - steam_overlay_present_ = true; - } - } - else { - spdlog::info("Steam-overlay detected."); - spdlog::warn("Double press Steam- overlay key(s)/Controller button to show GlosSI-overlay"); // Just to color output and really get users attention - window_.setClickThrough(true); - if (!overlay_.expired()) - overlay_.lock()->setEnabled(false); - steam_overlay_present_ = true; - -#ifdef WIN32 - if (!Settings::common.disable_watchdog) { - wchar_t buff[MAX_PATH]; - GetModuleFileName(GetModuleHandle(NULL), buff, MAX_PATH); - std::wstring watchDogPath(buff); - watchDogPath = watchDogPath.substr(0, 1 + watchDogPath.find_last_of(L'\\')) + L"GlosSIWatchdog.dll"; - - DllInjector::injectDllInto(watchDogPath, L"explorer.exe"); - } -#endif + ImGui::End(); + }, + true); + can_fully_initialize_ = false; } - util::steam::getXBCRebindingEnabled(steam_path_, steam_user_id_); run_ = true; -#ifdef _WIN32 - hidhide_.hideDevices(steam_path_); - input_redirector_.run(); -#endif - - if (Settings::launch.launch) { - launcher_.launchApp(Settings::launch.launchPath, Settings::launch.launchAppArgs); - } - - keepControllerConfig(true); - const auto tray = createTrayMenu(); server_.run(); while (run_) { + if (!fully_initialized_ && can_fully_initialize_) { + init_FuckingRenameMe(); + } detector_.update(); overlayHotkeyWorkaround(); window_.update(); - if (closeBPM && closeBPMTimer.getElapsedTime().asSeconds() >= 3) { - system("start steam://close/bigpicture"); - closeBPM = false; - } - // Wait on shutdown; User might get confused if window closes to fast if anything with launchApp get's borked. if (delayed_shutdown_) { if (delay_shutdown_clock_.getElapsedTime().asSeconds() >= 3) { @@ -161,17 +103,25 @@ int SteamTarget::run() } } else { - launcher_.update(); + if (fully_initialized_) { + launcher_.update(); + } } + for (auto& efc : end_frame_callbacks) { + efc(); + } + end_frame_callbacks.clear(); } tray->exit(); server_.stop(); + if (fully_initialized_) { #ifdef _WIN32 - input_redirector_.stop(); - hidhide_.disableHidHide(); + input_redirector_.stop(); + hidhide_.disableHidHide(); #endif - launcher_.close(); + launcher_.close(); + } return 0; } @@ -262,6 +212,93 @@ void SteamTarget::focusWindow(WindowHandle hndl) #endif } +void SteamTarget::init_FuckingRenameMe() +{ + if (!SteamOverlayDetector::IsSteamInjected()) { + if (Settings::common.allowStandAlone) { + spdlog::warn("GlosSI not launched via Steam.\nEnabling EXPERIMENTAL global controller and overlay..."); + if (Settings::common.standaloneModeGameId == L"") { + spdlog::error("No game id set for standalone mode. Controller will use desktop-config!"); + } + + SetEnvironmentVariable(L"SteamAppId", L"0"); + SetEnvironmentVariable(L"SteamClientLaunch", L"0"); + SetEnvironmentVariable(L"SteamEnv", L"1"); + SetEnvironmentVariable(L"SteamPath", steam_path_.wstring().c_str()); + SetEnvironmentVariable(L"SteamTenfoot", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); + // SetEnvironmentVariable(L"SteamTenfootHybrid", L"1"); + SetEnvironmentVariable(L"SteamGamepadUI", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); + SetEnvironmentVariable(L"SteamGameId", Settings::common.standaloneModeGameId.c_str()); + SetEnvironmentVariable(L"SteamOverlayGameId", Settings::common.standaloneModeGameId.c_str()); + SetEnvironmentVariable(L"EnableConfiguratorSupport", L"15"); + SetEnvironmentVariable(L"SteamStreamingForceWindowedD3D9", L"1"); + + if (Settings::common.standaloneUseGamepadUI) { + system("start steam://open/bigpicture"); + auto steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); + auto timer = sf::Clock{}; + while (!steamwindow && timer.getElapsedTime().asSeconds() < 2) { + steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); + Sleep(50); + } + Sleep(6000); // DIRTY HACK to wait until BPM (GamepadUI) is initialized + // TODO: find way to force BPM even if BPM is not active + LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str()); + + // Overlay switches back to desktop one, once BPM is closed... Disable closing BPM for now. + // TODO: find way to force BPM even if BPM is not active + // closeBPM = true; + // closeBPMTimer.restart(); + } + else { + LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str()); + } + + window_.setClickThrough(true); + steam_overlay_present_ = true; + } + else { + spdlog::warn("Steam-overlay not detected and global mode disabled. Showing GlosSI-overlay!\n\ +Application will not function!"); + window_.setClickThrough(false); + if (!overlay_.expired()) + overlay_.lock()->setEnabled(true); + steam_overlay_present_ = false; + } + } + else { + spdlog::info("Steam-overlay detected."); + spdlog::warn("Double press Steam- overlay key(s)/Controller button to show GlosSI-overlay"); // Just to color output and really get users attention + window_.setClickThrough(true); + steam_overlay_present_ = true; + } + +#ifdef WIN32 + if (!Settings::common.disable_watchdog) { + wchar_t buff[MAX_PATH]; + GetModuleFileName(GetModuleHandle(NULL), buff, MAX_PATH); + std::wstring watchDogPath(buff); + watchDogPath = watchDogPath.substr(0, 1 + watchDogPath.find_last_of(L'\\')) + L"GlosSIWatchdog.dll"; + + DllInjector::injectDllInto(watchDogPath, L"explorer.exe"); + } + + if (Settings::common.no_uwp_overlay) { + UWPOverlayEnabler::AddUwpOverlayOvWidget(); + } + else { + UWPOverlayEnabler::EnableUwpOverlay(); + } + + hidhide_.hideDevices(steam_path_); + input_redirector_.run(); +#endif + if (Settings::launch.launch) { + launcher_.launchApp(Settings::launch.launchPath, Settings::launch.launchAppArgs); + } + keepControllerConfig(true); + fully_initialized_ = true; +} /* * The "magic" that keeps a controller-config forced (without hooking into Steam) @@ -287,6 +324,7 @@ void SteamTarget::keepControllerConfig(bool keep) spdlog::error("Couldn't un-install GetForegroundWindow hook!"); } } + #endif } diff --git a/GlosSITarget/SteamTarget.h b/GlosSITarget/SteamTarget.h index 2aea62f..70dc70b 100644 --- a/GlosSITarget/SteamTarget.h +++ b/GlosSITarget/SteamTarget.h @@ -51,6 +51,9 @@ class SteamTarget { std::wstring steam_user_id_ = util::steam::getSteamUserId(); bool steam_overlay_present_ = false; + bool fully_initialized_ = false; + bool can_fully_initialize_ = true; + void init_FuckingRenameMe(); // Keep controllerConfig even is window is switched. // On Windoze hooking "GetForeGroundWindow" is enough; From 54fb29a79d09d64a2130cc1005a7ae7ef5a1e43c Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 18:26:50 +0100 Subject: [PATCH 30/65] Rename "standalone mode" -> "global mode" --- GlosSIConfig/Resource.rc | 8 +- GlosSIConfig/UIModel.cpp | 26 +- GlosSIConfig/UIModel.h | 8 +- GlosSIConfig/qml/GlobalConf.qml | 46 +-- GlosSITarget/SteamTarget.cpp | 16 +- Installer/Installer.nsi | 2 +- common/Settings.h | 598 +++++++++++++++++--------------- 7 files changed, 375 insertions(+), 329 deletions(-) diff --git a/GlosSIConfig/Resource.rc b/GlosSIConfig/Resource.rc index cc7ca13..2d28795 100644 --- a/GlosSIConfig/Resource.rc +++ b/GlosSIConfig/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,2,0018002705604 - PRODUCTVERSION 0,1,2,0018002705604 + FILEVERSION 0,1,2,0030000050130 + PRODUCTVERSION 0,1,2,0030000050130 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - Config" - VALUE "FileVersion", "0.1.2.0-18-g27056b4" + VALUE "FileVersion", "0.1.2.0-30-geb5f13f" VALUE "InternalName", "GlosSIConfig" VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSIConfig.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.2.0-18-g27056b4" + VALUE "ProductVersion", "0.1.2.0-30-geb5f13f" END END BLOCK "VarFileInfo" diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index 62c86aa..7fd66ba 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -398,8 +398,8 @@ QVariantMap UIModel::getDefaultConf() const QJsonValue::fromVariant(QString::fromStdWString(getSteamPath(false).wstring()))}, {"steamUserId", QJsonValue::fromVariant(QString::fromStdWString(getSteamUserId(false)))}, - {"standaloneModeGameId", ""}, - {"standaloneUseGamepadUI", false}, + {"globalModeGameId", ""}, + {"globalModeUseGamepadUI", false}, {"controller", QJsonObject{{"maxControllers", 1}, {"emulateDS4", false}, {"allowDesktopConfig", false}}}, {"devices", QJsonObject{ @@ -474,36 +474,36 @@ void UIModel::saveDefaultConf(QVariantMap conf) const file.close(); } -Q_INVOKABLE QVariant UIModel::standaloneShortcutConf() { +Q_INVOKABLE QVariant UIModel::globalModeShortcutConf() { for (auto& target : targets_) { const auto map = target.toMap(); - if (map["name"] == "GlosSI Standalone/Desktop") { + if (map["name"] == "GlosSI GlobalMode/Desktop") { return target; } } return QVariant(); } -Q_INVOKABLE bool UIModel::standaloneModeShortcutExists() { - const auto map = standaloneShortcutConf().toMap(); - if (map["name"] == "GlosSI Standalone/Desktop") { +Q_INVOKABLE bool UIModel::globalModeShortcutExists() { + const auto map = globalModeShortcutConf().toMap(); + if (map["name"] == "GlosSI GlobalMode/Desktop") { return true; } return false; } -Q_INVOKABLE uint32_t UIModel::standaloneModeShortcutAppId() { - if (!standaloneModeShortcutExists()) { +Q_INVOKABLE uint32_t UIModel::globalModeShortcutAppId() { + if (!globalModeShortcutExists()) { return 0; } - return getAppId(standaloneShortcutConf()); + return getAppId(globalModeShortcutConf()); } -Q_INVOKABLE QString UIModel::standaloneModeShortcutGameId() { - if (!standaloneModeShortcutExists()) { +Q_INVOKABLE QString UIModel::globalModeShortcutGameId() { + if (!globalModeShortcutExists()) { return ""; } - return getGameId(standaloneShortcutConf()); + return getGameId(globalModeShortcutConf()); } #ifdef _WIN32 diff --git a/GlosSIConfig/UIModel.h b/GlosSIConfig/UIModel.h index 10cdb3a..05ddf35 100644 --- a/GlosSIConfig/UIModel.h +++ b/GlosSIConfig/UIModel.h @@ -64,10 +64,10 @@ class UIModel : public QObject { Q_INVOKABLE QVariantMap getDefaultConf() const; Q_INVOKABLE void saveDefaultConf(QVariantMap conf) const; - Q_INVOKABLE QVariant standaloneShortcutConf(); - Q_INVOKABLE bool standaloneModeShortcutExists(); - Q_INVOKABLE uint32_t standaloneModeShortcutAppId(); - Q_INVOKABLE QString standaloneModeShortcutGameId(); + Q_INVOKABLE QVariant globalModeShortcutConf(); + Q_INVOKABLE bool globalModeShortcutExists(); + Q_INVOKABLE uint32_t globalModeShortcutAppId(); + Q_INVOKABLE QString globalModeShortcutGameId(); #ifdef _WIN32 Q_INVOKABLE QVariantList uwpApps(); diff --git a/GlosSIConfig/qml/GlobalConf.qml b/GlosSIConfig/qml/GlobalConf.qml index 4cda0e7..a2c39b1 100644 --- a/GlosSIConfig/qml/GlobalConf.qml +++ b/GlosSIConfig/qml/GlobalConf.qml @@ -140,10 +140,10 @@ Item { Row { Row { CheckBox { - id: standaloneUseGamepadUI - text: qsTr("Use BPM for standalone-/desktop-mode") - checked: config.standaloneUseGamepadUI - onCheckedChanged: config.standaloneUseGamepadUI = checked + id: globalModeUseGamepadUI + text: qsTr("Use BPM for global-/desktop-mode") + checked: config.globalModeUseGamepadUI + onCheckedChanged: config.globalModeUseGamepadUI = checked } } } @@ -153,44 +153,44 @@ Item { spacing: 16 Label { topPadding: 8 - id: standAloneGameIdLabel - text: qsTr("StandaloneGameId") + id: GlobalModeGameIdLabel + text: qsTr("GlobalMode GameId") } FluentTextInput { width: 128 - id: standAloneGameId + id: GlobalModeGameId enabled: false - text: config.standaloneModeGameId - onTextChanged: config.standaloneModeGameId = text + text: config.globalModeGameId + onTextChanged: config.globalModeGameId = text } Button { - id: standAloneGameIdButton - text: qsTr("Create standalone-/desktop-mode shortcut") + id: GlobalModeGameIdButton + text: qsTr("Create global-/desktop-mode shortcut") onClicked: { - const standaloneConf = uiModel.getDefaultConf(); - standaloneConf.name = "GlosSI Standalone/Desktop"; - standaloneConf.launch.launch = false; - uiModel.addTarget(standaloneConf); - if (uiModel.addToSteam(standaloneConf, "")) { + const globalModeConf = uiModel.getDefaultConf(); + globalModeConf.name = "GlosSI GlobalMode/Desktop"; + globalModeConf.launch.launch = false; + uiModel.addTarget(globalModeConf); + if (uiModel.addToSteam(globalModeConf, "")) { steamChangedDialog.open(); } - const standaloneGID = uiModel.standaloneModeShortcutGameId(); - standAloneGameId.text = standaloneGID; + const globalModeGID = uiModel.globalModeShortcutGameId(); + GlobalModeGameId.text = globalModeGID; setTimeout(() => { uiModel.saveDefaultConf(config); done(); }, 10); } highlighted: true - visible: !uiModel.standaloneModeShortcutExists() + visible: !uiModel.globalModeShortcutExists() } Button { - id: standAloneGameIdConfigButton - text: qsTr("Open standalone-/desktop-mode controller config") + id: GlobalModeGameIdConfigButton + text: qsTr("Open global-/desktop-mode controller config") onClicked: { - Qt.openUrlExternally("steam://currentcontrollerconfig/" + uiModel.standaloneModeShortcutAppId() + "/"); + Qt.openUrlExternally("steam://currentcontrollerconfig/" + uiModel.globalModeShortcutAppId() + "/"); } - visible: uiModel.standaloneModeShortcutExists() + visible: uiModel.globalModeShortcutExists() } } } diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index c52ec56..a008366 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -215,25 +215,25 @@ void SteamTarget::focusWindow(WindowHandle hndl) void SteamTarget::init_FuckingRenameMe() { if (!SteamOverlayDetector::IsSteamInjected()) { - if (Settings::common.allowStandAlone) { + if (Settings::common.allowGlobalMode) { spdlog::warn("GlosSI not launched via Steam.\nEnabling EXPERIMENTAL global controller and overlay..."); - if (Settings::common.standaloneModeGameId == L"") { - spdlog::error("No game id set for standalone mode. Controller will use desktop-config!"); + if (Settings::common.globalModeGameId == L"") { + spdlog::error("No game id set for global mode. Controller will use desktop-config!"); } SetEnvironmentVariable(L"SteamAppId", L"0"); SetEnvironmentVariable(L"SteamClientLaunch", L"0"); SetEnvironmentVariable(L"SteamEnv", L"1"); SetEnvironmentVariable(L"SteamPath", steam_path_.wstring().c_str()); - SetEnvironmentVariable(L"SteamTenfoot", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); + SetEnvironmentVariable(L"SteamTenfoot", Settings::common.globalModeUseGamepadUI ? L"1" : L"0"); // SetEnvironmentVariable(L"SteamTenfootHybrid", L"1"); - SetEnvironmentVariable(L"SteamGamepadUI", Settings::common.standaloneUseGamepadUI ? L"1" : L"0"); - SetEnvironmentVariable(L"SteamGameId", Settings::common.standaloneModeGameId.c_str()); - SetEnvironmentVariable(L"SteamOverlayGameId", Settings::common.standaloneModeGameId.c_str()); + SetEnvironmentVariable(L"SteamGamepadUI", Settings::common.globalModeUseGamepadUI ? L"1" : L"0"); + SetEnvironmentVariable(L"SteamGameId", Settings::common.globalModeGameId.c_str()); + SetEnvironmentVariable(L"SteamOverlayGameId", Settings::common.globalModeGameId.c_str()); SetEnvironmentVariable(L"EnableConfiguratorSupport", L"15"); SetEnvironmentVariable(L"SteamStreamingForceWindowedD3D9", L"1"); - if (Settings::common.standaloneUseGamepadUI) { + if (Settings::common.globalModeUseGamepadUI) { system("start steam://open/bigpicture"); auto steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); auto timer = sf::Clock{}; diff --git a/Installer/Installer.nsi b/Installer/Installer.nsi index 0d9a1dc..a27ec7f 100644 --- a/Installer/Installer.nsi +++ b/Installer/Installer.nsi @@ -3,7 +3,7 @@ !define APP_NAME "GlosSI" !define COMP_NAME "Peter Repukat - Flatspotsoftware" !define WEB_SITE "https://glossi.flatspot.pictures/" -!define VERSION "0.1.2.0-18-g27056b4" +!define VERSION "0.1.2.0-31-gd2b43ff" !define COPYRIGHT "Peter Repukat - FlatspotSoftware © 2017-2022" !define DESCRIPTION "SteamInput compatibility tool" !define INSTALLER_NAME "GlosSI-Installer.exe" diff --git a/common/Settings.h b/common/Settings.h index 83dcc1d..ece94ac 100644 --- a/common/Settings.h +++ b/common/Settings.h @@ -31,285 +31,331 @@ limitations under the License. #include "../common/nlohmann_json_wstring.h" #include "../common/util.h" - -namespace Settings { - - inline struct Launch { - bool launch = false; - std::wstring launchPath; - std::wstring launchAppArgs; - bool closeOnExit = true; - bool waitForChildProcs = true; - bool isUWP = false; - bool ignoreLauncher = true; - bool killLauncher = false; - std::vector launcherProcesses{}; - } launch; - - inline struct Devices { - bool hideDevices = true; - bool realDeviceIds = false; - } devices; - - inline struct Window { - bool windowMode = false; - int maxFps = 0; - float scale = 0.f; - bool disableOverlay = false; - bool hideAltTab = true; - bool disableGlosSIOverlay = false; - } window; - - inline struct Controller { - int maxControllers = 1; - bool allowDesktopConfig = false; - bool emulateDS4 = false; - } controller; - - inline struct Common { - bool no_uwp_overlay = false; - bool disable_watchdog = false; - bool extendedLogging = false; - std::wstring name; - std::wstring icon; - int version; - std::wstring steamPath; - std::wstring steamUserId; - std::wstring standaloneModeGameId; /* = L"12605636929694728192"; */ - bool standaloneUseGamepadUI = false; - bool allowStandAlone = true; - } common; - - inline const std::map> cmd_args = { - {L"-disableuwpoverlay", [&]() { common.no_uwp_overlay = true; }}, - {L"-disablewatchdog", [&]() { common.disable_watchdog = true; }}, - {L"-ignorelauncher", [&]() { launch.ignoreLauncher = true; }}, - {L"-window", [&]() { window.windowMode = true; }}, - {L"-extendedLogging", [&]() { common.extendedLogging = true; }}, - {L"-standaloneUseGamepadUI", [&]() { common.standaloneUseGamepadUI = true; }}, - {L"-disallowStandAlone", [&]() { common.allowStandAlone = false; }}, - }; - - inline std::filesystem::path settings_path_ = ""; - - inline bool checkIsUwp(const std::wstring& launch_path) - { - if (launch_path.find(L"://") != std::wstring::npos) { - return false; - } - std::wsmatch m; - if (!std::regex_search(launch_path, m, std::wregex(L"^.{1,5}:"))) { - return true; - } - return false; - } +namespace Settings +{ + + inline struct Launch + { + bool launch = false; + std::wstring launchPath; + std::wstring launchAppArgs; + bool closeOnExit = true; + bool waitForChildProcs = true; + bool isUWP = false; + bool ignoreLauncher = true; + bool killLauncher = false; + std::vector launcherProcesses{}; + } launch; + + inline struct Devices + { + bool hideDevices = true; + bool realDeviceIds = false; + } devices; + + inline struct Window + { + bool windowMode = false; + int maxFps = 0; + float scale = 0.f; + bool disableOverlay = false; + bool hideAltTab = true; + bool disableGlosSIOverlay = false; + } window; + + inline struct Controller + { + int maxControllers = 1; + bool allowDesktopConfig = false; + bool emulateDS4 = false; + } controller; + + inline struct Common + { + bool no_uwp_overlay = false; + bool disable_watchdog = false; + bool extendedLogging = false; + std::wstring name; + std::wstring icon; + int version; + std::wstring steamPath; + std::wstring steamUserId; + std::wstring globalModeGameId; /* = L"12605636929694728192"; */ + bool globalModeUseGamepadUI = false; + bool allowGlobalMode = true; + } common; + + inline const std::map> cmd_args = { + {L"-disableuwpoverlay", [&]() + { common.no_uwp_overlay = true; }}, + {L"-disablewatchdog", [&]() + { common.disable_watchdog = true; }}, + {L"-ignorelauncher", [&]() + { launch.ignoreLauncher = true; }}, + {L"-window", [&]() + { window.windowMode = true; }}, + {L"-extendedLogging", [&]() + { common.extendedLogging = true; }}, + {L"-globalModeUseGamepadUI", [&]() + { common.globalModeUseGamepadUI = true; }}, + {L"-disallowGlobalMode", [&]() + { common.allowGlobalMode = false; }}, + }; + + inline std::filesystem::path settings_path_ = ""; + + inline bool checkIsUwp(const std::wstring &launch_path) + { + if (launch_path.find(L"://") != std::wstring::npos) + { + return false; + } + std::wsmatch m; + if (!std::regex_search(launch_path, m, std::wregex(L"^.{1,5}:"))) + { + return true; + } + return false; + } #ifdef WIN32 - inline bool isWin10 = false; - - inline void checkWinVer() - { - auto VN = util::win::GetRealOSVersion(); - isWin10 = VN.dwBuildNumber < 22000; - - if (isWin10) { - spdlog::info("Running on Windows 10; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber); - } - else { - spdlog::info("Running on Windows 11; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber); - } - } + inline bool isWin10 = false; + + inline void checkWinVer() + { + auto VN = util::win::GetRealOSVersion(); + isWin10 = VN.dwBuildNumber < 22000; + + if (isWin10) + { + spdlog::info("Running on Windows 10; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber); + } + else + { + spdlog::info("Running on Windows 11; Winver: {}.{}.{}", VN.dwMajorVersion, VN.dwMinorVersion, VN.dwBuildNumber); + } + } #endif - inline void Parse(const nlohmann::basic_json<>& json) - { - constexpr auto safeParseValue = [](const auto & object, const auto & key, T & value) { - try { - if (object.is_null() || object.empty() || object.at(key).empty() || object.at(key).is_null()) { - return; - } - if constexpr (std::is_same_v) { - value = util::string::to_wstring(object[key].get()); - } - else { - value = object[key]; - } - } - catch (const nlohmann::json::exception& e) { - e.id == 403 - ? spdlog::trace("Err parsing \"{}\"; {}", key, e.what()) - : spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); - - } - catch (const std::exception& e) { - spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); - } - }; - - int version; - safeParseValue(json, "version", version); - if (version != 1) { // TODO: versioning stuff - spdlog::warn("Config version doesn't match application version."); - } - - // TODO: make this as much generic as fits in about the same amount of code if one would parse every value separately. - try { - if (const auto launchconf = json["launch"]; !launchconf.is_null() && !launchconf.empty() && launchconf.is_object()) { - safeParseValue(launchconf, "launch", launch.launch); - safeParseValue(launchconf, "launchPath", launch.launchPath); - safeParseValue(launchconf, "launchAppArgs", launch.launchAppArgs); - safeParseValue(launchconf, "closeOnExit", launch.closeOnExit); - safeParseValue(launchconf, "waitForChildProcs", launch.waitForChildProcs); - safeParseValue(launchconf, "killLauncher", launch.killLauncher); - safeParseValue(launchconf, "ignoreLauncher", launch.ignoreLauncher); - - if (launchconf.contains("launcherProcesses") && launchconf["launcherProcesses"].is_array()) { - if (const auto launcherProcs = launchconf["launcherProcesses"]; - !launcherProcs.is_null() && !launcherProcs.empty() && launcherProcs.is_array()) { - launch.launcherProcesses.clear(); - launch.launcherProcesses.reserve(launcherProcs.size()); - for (auto& proc : launcherProcs) { - launch.launcherProcesses.push_back(util::string::to_wstring(proc)); - } - } - } - } - - if (const auto devconf = json["devices"]; !devconf.is_null() && !devconf.empty() && devconf.is_object()) { - safeParseValue(devconf, "hideDevices", devices.hideDevices); - safeParseValue(devconf, "realDeviceIds", devices.realDeviceIds); - } - - if (const auto winconf = json["window"]; !winconf.is_null() && !winconf.empty() && winconf.is_object()) { - safeParseValue(winconf, "windowMode", window.windowMode); - safeParseValue(winconf, "maxFps", window.maxFps); - safeParseValue(winconf, "scale", window.scale); - safeParseValue(winconf, "disableOverlay", window.disableOverlay); - safeParseValue(winconf, "hideAltTab", window.hideAltTab); - safeParseValue(winconf, "disableGlosSIOverlay", window.disableGlosSIOverlay); - } - - if (const auto controllerConf = json["controller"]; !controllerConf.is_null() && !controllerConf.empty() && controllerConf.is_object()) { - safeParseValue(controllerConf, "maxControllers", controller.maxControllers); - safeParseValue(controllerConf, "allowDesktopConfig", controller.allowDesktopConfig); - safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4); - } - safeParseValue(json, "extendedLogging", common.extendedLogging); - safeParseValue(json, "name", common.name); - safeParseValue(json, "icon", common.icon); - safeParseValue(json, "version", common.version); - - safeParseValue(json, "steamPath", common.steamPath); - safeParseValue(json, "steamUserId", common.steamUserId); - - safeParseValue(json, "standaloneModeGameId", common.standaloneModeGameId); - safeParseValue(json, "standaloneUseGamepadUI", common.standaloneUseGamepadUI); - } - catch (const nlohmann::json::exception& e) { - spdlog::warn("Err parsing config: {}", e.what()); - } - catch (const std::exception& e) { - spdlog::warn("Err parsing config: {}", e.what()); - } - if (launch.launch) { - launch.isUWP = checkIsUwp(launch.launchPath); - } - } - - inline void Parse(const std::vector& args) - { - std::wstring configName; - std::vector> cli_overrides; - for (const auto& arg : args) { - if (arg.empty()) { - continue; - } - if (cmd_args.contains(arg)) - { - cli_overrides.push_back(cmd_args.at(arg)); - } - else { - configName += L" " + std::wstring(arg.begin(), arg.end()); - } - } - if (!configName.empty()) { - if (configName[0] == L' ') { - configName.erase(configName.begin()); - } - if (!configName.ends_with(L".json")) { - configName += L".json"; - } - } - auto path = util::path::getDataDirPath(); - if (!configName.empty()) { - path /= "Targets"; - path /= configName; - } - else { - spdlog::info("No config file specified, using default"); - path /= "default.json"; - } - - std::ifstream json_file; - json_file.open(path); - if (!json_file.is_open()) { - spdlog::error(L"Couldn't open settings file {}", path.wstring()); - spdlog::debug(L"Using sane defaults..."); - for (const auto& ovr : cli_overrides) { - ovr(); - } - return; - } - settings_path_ = path; - const auto& json = nlohmann::json::parse(json_file); - Parse(json); - - for (const auto& ovr : cli_overrides) { - ovr(); - } - spdlog::debug("Read config file \"{}\"; config: {}", path.string(), json.dump()); - json_file.close(); - } - - inline nlohmann::json toJson() - { - nlohmann::json json; - json["version"] = 1; - json["launch"]["launch"] = launch.launch; - json["launch"]["launchPath"] = launch.launchPath; - json["launch"]["launchAppArgs"] = launch.launchAppArgs; - json["launch"]["closeOnExit"] = launch.closeOnExit; - json["launch"]["waitForChildProcs"] = launch.waitForChildProcs; - json["devices"]["hideDevices"] = devices.hideDevices; - json["devices"]["realDeviceIds"] = devices.realDeviceIds; - json["window"]["windowMode"] = window.windowMode; - json["window"]["maxFps"] = window.maxFps; - json["window"]["scale"] = window.scale; - json["window"]["disableOverlay"] = window.disableOverlay; - json["window"]["hideAltTab"] = window.hideAltTab; - json["controller"]["maxControllers"] = controller.maxControllers; - json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig; - json["controller"]["emulateDS4"] = controller.emulateDS4; - - json["extendedLogging"] = common.extendedLogging; - json["name"] = common.name; - json["icon"] = common.icon; - json["version"] = common.version; - return json; - } - - inline void StoreSettings() - { - const auto& json = toJson(); - - std::ofstream json_file; - json_file.open(settings_path_); - if (!json_file.is_open()) { - spdlog::error(L"Couldn't open settings file {}", settings_path_.wstring()); - return; - } - json_file << json.dump(4); - json_file.close(); - } + inline void Parse(const nlohmann::basic_json<> &json) + { + constexpr auto safeParseValue = [](const auto &object, const auto &key, T &value) + { + try + { + if (object.is_null() || object.empty() || object.at(key).empty() || object.at(key).is_null()) + { + return; + } + if constexpr (std::is_same_v) + { + value = util::string::to_wstring(object[key].get()); + } + else + { + value = object[key]; + } + } + catch (const nlohmann::json::exception &e) + { + e.id == 403 + ? spdlog::trace("Err parsing \"{}\"; {}", key, e.what()) + : spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); + } + catch (const std::exception &e) + { + spdlog::warn("Err parsing \"{}\"; {}", key, e.what()); + } + }; + + int version; + safeParseValue(json, "version", version); + if (version != 1) + { // TODO: versioning stuff + spdlog::warn("Config version doesn't match application version."); + } + + // TODO: make this as much generic as fits in about the same amount of code if one would parse every value separately. + try + { + if (const auto launchconf = json["launch"]; !launchconf.is_null() && !launchconf.empty() && launchconf.is_object()) + { + safeParseValue(launchconf, "launch", launch.launch); + safeParseValue(launchconf, "launchPath", launch.launchPath); + safeParseValue(launchconf, "launchAppArgs", launch.launchAppArgs); + safeParseValue(launchconf, "closeOnExit", launch.closeOnExit); + safeParseValue(launchconf, "waitForChildProcs", launch.waitForChildProcs); + safeParseValue(launchconf, "killLauncher", launch.killLauncher); + safeParseValue(launchconf, "ignoreLauncher", launch.ignoreLauncher); + + if (launchconf.contains("launcherProcesses") && launchconf["launcherProcesses"].is_array()) + { + if (const auto launcherProcs = launchconf["launcherProcesses"]; + !launcherProcs.is_null() && !launcherProcs.empty() && launcherProcs.is_array()) + { + launch.launcherProcesses.clear(); + launch.launcherProcesses.reserve(launcherProcs.size()); + for (auto &proc : launcherProcs) + { + launch.launcherProcesses.push_back(util::string::to_wstring(proc)); + } + } + } + } + + if (const auto devconf = json["devices"]; !devconf.is_null() && !devconf.empty() && devconf.is_object()) + { + safeParseValue(devconf, "hideDevices", devices.hideDevices); + safeParseValue(devconf, "realDeviceIds", devices.realDeviceIds); + } + + if (const auto winconf = json["window"]; !winconf.is_null() && !winconf.empty() && winconf.is_object()) + { + safeParseValue(winconf, "windowMode", window.windowMode); + safeParseValue(winconf, "maxFps", window.maxFps); + safeParseValue(winconf, "scale", window.scale); + safeParseValue(winconf, "disableOverlay", window.disableOverlay); + safeParseValue(winconf, "hideAltTab", window.hideAltTab); + safeParseValue(winconf, "disableGlosSIOverlay", window.disableGlosSIOverlay); + } + + if (const auto controllerConf = json["controller"]; !controllerConf.is_null() && !controllerConf.empty() && controllerConf.is_object()) + { + safeParseValue(controllerConf, "maxControllers", controller.maxControllers); + safeParseValue(controllerConf, "allowDesktopConfig", controller.allowDesktopConfig); + safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4); + } + safeParseValue(json, "extendedLogging", common.extendedLogging); + safeParseValue(json, "name", common.name); + safeParseValue(json, "icon", common.icon); + safeParseValue(json, "version", common.version); + + safeParseValue(json, "steamPath", common.steamPath); + safeParseValue(json, "steamUserId", common.steamUserId); + + safeParseValue(json, "globalModeGameId", common.globalModeGameId); + safeParseValue(json, "globalModeUseGamepadUI", common.globalModeUseGamepadUI); + } + catch (const nlohmann::json::exception &e) + { + spdlog::warn("Err parsing config: {}", e.what()); + } + catch (const std::exception &e) + { + spdlog::warn("Err parsing config: {}", e.what()); + } + if (launch.launch) + { + launch.isUWP = checkIsUwp(launch.launchPath); + } + } + + inline void Parse(const std::vector &args) + { + std::wstring configName; + std::vector> cli_overrides; + for (const auto &arg : args) + { + if (arg.empty()) + { + continue; + } + if (cmd_args.contains(arg)) + { + cli_overrides.push_back(cmd_args.at(arg)); + } + else + { + configName += L" " + std::wstring(arg.begin(), arg.end()); + } + } + if (!configName.empty()) + { + if (configName[0] == L' ') + { + configName.erase(configName.begin()); + } + if (!configName.ends_with(L".json")) + { + configName += L".json"; + } + } + auto path = util::path::getDataDirPath(); + if (!configName.empty()) + { + path /= "Targets"; + path /= configName; + } + else + { + spdlog::info("No config file specified, using default"); + path /= "default.json"; + } + + std::ifstream json_file; + json_file.open(path); + if (!json_file.is_open()) + { + spdlog::error(L"Couldn't open settings file {}", path.wstring()); + spdlog::debug(L"Using sane defaults..."); + for (const auto &ovr : cli_overrides) + { + ovr(); + } + return; + } + settings_path_ = path; + const auto &json = nlohmann::json::parse(json_file); + Parse(json); + + for (const auto &ovr : cli_overrides) + { + ovr(); + } + spdlog::debug("Read config file \"{}\"; config: {}", path.string(), json.dump()); + json_file.close(); + } + + inline nlohmann::json toJson() + { + nlohmann::json json; + json["version"] = 1; + json["launch"]["launch"] = launch.launch; + json["launch"]["launchPath"] = launch.launchPath; + json["launch"]["launchAppArgs"] = launch.launchAppArgs; + json["launch"]["closeOnExit"] = launch.closeOnExit; + json["launch"]["waitForChildProcs"] = launch.waitForChildProcs; + json["devices"]["hideDevices"] = devices.hideDevices; + json["devices"]["realDeviceIds"] = devices.realDeviceIds; + json["window"]["windowMode"] = window.windowMode; + json["window"]["maxFps"] = window.maxFps; + json["window"]["scale"] = window.scale; + json["window"]["disableOverlay"] = window.disableOverlay; + json["window"]["hideAltTab"] = window.hideAltTab; + json["controller"]["maxControllers"] = controller.maxControllers; + json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig; + json["controller"]["emulateDS4"] = controller.emulateDS4; + + json["extendedLogging"] = common.extendedLogging; + json["name"] = common.name; + json["icon"] = common.icon; + json["version"] = common.version; + return json; + } + + inline void StoreSettings() + { + const auto &json = toJson(); + + std::ofstream json_file; + json_file.open(settings_path_); + if (!json_file.is_open()) + { + spdlog::error(L"Couldn't open settings file {}", settings_path_.wstring()); + return; + } + json_file << json.dump(4); + json_file.close(); + } } // namespace Settings From aa3f513e135bfd19054c3221e1c672be62048402 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 18:41:46 +0100 Subject: [PATCH 31/65] Add warning dialog about not properly setup Global mode --- GlosSITarget/SteamTarget.cpp | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index a008366..54f97fa 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -61,6 +61,33 @@ int SteamTarget::run() std::vector> end_frame_callbacks; + if (!SteamOverlayDetector::IsSteamInjected() && Settings::common.allowGlobalMode && Settings::common.globalModeGameId == L"") { + auto overlay_id = std::make_shared(-1); + *overlay_id = Overlay::AddOverlayElem( + [this, overlay_id, &end_frame_callbacks](bool window_has_focus, ImGuiID dockspace_id) { + can_fully_initialize_ = false; + ImGui::Begin("Global mode", nullptr, ImGuiWindowFlags_NoSavedSettings); + ImGui::Text("You are running GlosSI in (experimental) global mode (=outside of Steam)"); + ImGui::Text("but global mode doesn't appear to be setup properly."); + ImGui::Text(""); + ImGui::Text("Please open GlosSI-Config first and setup global mode"); + ImGui::Text(""); + ImGui::Text("Application will exit on confirm"); + if (ImGui::Button("OK")) { + can_fully_initialize_ = true; + if (*overlay_id != -1) { + end_frame_callbacks.emplace_back([this, overlay_id] { + Overlay::RemoveOverlayElem(*overlay_id); + run_ = false; + }); + } + } + ImGui::End(); + }, + true); + can_fully_initialize_ = false; + } + if (!util::steam::getXBCRebindingEnabled(steam_path_, steam_user_id_)) { auto overlay_id = std::make_shared(-1); *overlay_id = Overlay::AddOverlayElem( @@ -88,10 +115,17 @@ int SteamTarget::run() server_.run(); + bool delayed_full_init_1_frame = false; while (run_) { - if (!fully_initialized_ && can_fully_initialize_) { + if (!fully_initialized_ && can_fully_initialize_ && delayed_full_init_1_frame) { init_FuckingRenameMe(); } + else if (!fully_initialized_ && can_fully_initialize_) { + delayed_full_init_1_frame = true; + } + else { + delayed_full_init_1_frame = false; + } detector_.update(); overlayHotkeyWorkaround(); window_.update(); From efe12dc333e6778f9c71e2bc97f2e1905a8224cd Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 18:48:16 +0100 Subject: [PATCH 32/65] SteamTweaks: GlosSITweaks: Fix uninstall --- SteamTweaks/src/GlosSITweaks.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/SteamTweaks/src/GlosSITweaks.ts b/SteamTweaks/src/GlosSITweaks.ts index dd5140b..4f710af 100644 --- a/SteamTweaks/src/GlosSITweaks.ts +++ b/SteamTweaks/src/GlosSITweaks.ts @@ -38,11 +38,15 @@ const installGlosSIApi = () => { Object.assign(window, { GlosSIApi: api }); }, uninstall: () => { - Object.values(window.GlosSITweaks) - .forEach((obj) => obj.uninstall?.()); + Object.entries(window.GlosSITweaks) + .filter(([tweakName, obj]) => (tweakName !== 'GlosSI')) + .forEach(([, obj]) => obj.uninstall?.()); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore delete window.GlosSIApi; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + delete window.GlosSITweaks; } } }; From 0e356801e7648788ca332d5b350421c40ec9e8cc Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 19:15:49 +0100 Subject: [PATCH 33/65] common: utils: add util to get executableDir --- common/util.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/common/util.h b/common/util.h index b12da09..148dfbe 100644 --- a/common/util.h +++ b/common/util.h @@ -73,6 +73,14 @@ namespace util { std::filesystem::create_directories(path); return path; } + + inline std::filesystem::path getGlosSIDir() + { + wchar_t result[MAX_PATH]; + std::filesystem::path res{ std::wstring{result, GetModuleFileNameW(NULL, result, MAX_PATH)} }; + return res.parent_path(); + } + } #ifdef _WIN32 From e5368744f1678e6aa4a71b0166de4eabcaef9f75 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 19:15:55 +0100 Subject: [PATCH 34/65] CEFInject: Add (WIP) GlosSITweaks injection system --- CEFInjectLib/CEFInject.cpp | 257 +++++++++++++++++++++++++++++-------- CEFInjectLib/CEFInject.h | 38 +++++- 2 files changed, 237 insertions(+), 58 deletions(-) diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp index 297e484..df86899 100644 --- a/CEFInjectLib/CEFInject.cpp +++ b/CEFInjectLib/CEFInject.cpp @@ -1,4 +1,4 @@ -/* +/* Copyright 2021-2023 Peter Repukat - FlatspotSoftware Licensed under the Apache License, Version 2.0 (the "License"); @@ -20,10 +20,13 @@ limitations under the License. #include #define _SSIZE_T_DEFINED -#include // seems like a hack to me, but eh +#include // seems like a hack to me, but eh, what is in the doc will be done ¯\_(ツ)_/¯ #include "../common/nlohmann_json_wstring.h" +#include +#include + namespace CEFInject { namespace internal @@ -51,80 +54,222 @@ namespace CEFInject return false; } - std::vector AvailableTabs(uint16_t port) + std::vector AvailableTabNames(uint16_t port) { std::vector tabs; + const auto json = AvailableTabs(port); + for (const auto& j : json) { + tabs.push_back(j["title"].get()); + } + return tabs; + } + + nlohmann::json::array_t AvailableTabs(uint16_t port) + { + if (!CEFDebugAvailable()) { + return nlohmann::json::array(); + } auto cli = internal::GetHttpClient(port); if (auto res = cli.Get("/json")) { if (res->status == 200) { - const auto json = nlohmann::json::parse(res->body); - for (const auto& j : json) { - tabs.push_back(j["title"].get()); + return nlohmann::json::parse(res->body); + } + } + return nlohmann::json::array(); + } + + nlohmann::json InjectJs(std::string_view debug_url, std::wstring_view js, uint16_t port) + { +#ifdef _WIN32 + INT rc; + WSADATA wsaData; + + rc = WSAStartup(MAKEWORD(2, 2), &wsaData); + if (rc) { + printf("WSAStartup Failed.\n"); + return nullptr; + } +#endif + + std::shared_ptr ws{ + easywsclient::WebSocket::from_url(debug_url.data()) + }; + if (ws) + { + auto json_payload = nlohmann::json{ + {"id", internal::msg_id++}, + {"method", "Runtime.evaluate"}, + {"params", { + {"userGesture", true}, + {"expression", std::wstring{js.data()}} + //{"expression", js} + }} + }; + auto payload_string = json_payload.dump(); + ws->send(payload_string); + nlohmann::json res = nullptr; + bool exit = false; + while (ws->getReadyState() != easywsclient::WebSocket::CLOSED) { + ws->poll(); + ws->dispatch([&ws, &res, &exit](const std::string& message) { + const auto msg = nlohmann::json::parse(message); + try + { + res = msg.at("result").at("result").at("value"); + } catch (...){ + // + } + try + { + if (res.is_null() && msg.at("result").at("result").at("type").get() != "undefined") { + res = nlohmann::json::parse(message); + } + } catch (...) { + res = nlohmann::json::parse(message); + } + exit = true; + }); + if (exit) { + ws->close(); + return res; } } +#ifdef _WIN32 + WSACleanup(); +#endif + return res; } - return tabs; +#ifdef _WIN32 + WSACleanup(); +#endif + return nullptr; } - nlohmann::json InjectJs(const std::wstring &tabname, const std::wstring &js, uint16_t port) - { + nlohmann::json InjectJsByName(std::wstring_view tabname, std::wstring_view js, uint16_t port) + { auto cli = internal::GetHttpClient(port); if (auto res = cli.Get("/json")) { if (res->status == 200) { const auto json = nlohmann::json::parse(res->body); for (const auto& tab : json) { if (tab["title"].get().starts_with(tabname)) { -#ifdef _WIN32 - INT rc; - WSADATA wsaData; + return InjectJs(tab["webSocketDebuggerUrl"].get(), js, port); + } + } + } + } + return nullptr; + } + + + bool SteamTweaks::injectGlosSITweaks(std::string_view tab_name, uint16_t port) + { + if (tab_name.empty()) { + for (const auto& tn : AvailableTabNames()) + { + // meh! + injectGlosSITweaks(util::string::to_string(tn), port); + } + return true; + } + return injectGlosSITweaks(Tab_Info{ std::string(tab_name) }, port); + } + + bool SteamTweaks::injectGlosSITweaks(const Tab_Info& info, uint16_t port) + { + if (!CEFDebugAvailable()) { + return false; + } + + auto glossi_path = util::path::getGlosSIDir(); + glossi_path /= steam_tweaks_path_; + glossi_path /= "GlosSITweaks.js"; + + if (!std::filesystem::exists(glossi_path)) + { + return false; + } - rc = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (rc) { - printf("WSAStartup Failed.\n"); - return nullptr; + std::wifstream js_file(glossi_path); + std::wstring glossitweaks_js{ (std::istreambuf_iterator(js_file)), + std::istreambuf_iterator() }; + if (glossitweaks_js.empty()) { + return false; + } + + const auto find_tab = ( + [&info]() -> std::function + { + if (!info.name.empty()) + { + return [&info](const nlohmann::json::array_t& tabList) + { + for (const auto& tab : tabList) { + if (tab["title"].get().starts_with(info.name.data())) { + return tab; + } } -#endif + return nlohmann::json{}; + }; + } + if (!info.id.empty()) + { + return [&info](const nlohmann::json::array_t& tabList) + { + for (const auto& tab : tabList) { + if (tab["id"].get() == info.id) { + return tab; + } + } + return nlohmann::json{}; + }; - std::shared_ptr ws{ - easywsclient::WebSocket::from_url(tab["webSocketDebuggerUrl"].get()) - }; - if (ws) - { - ws->send( - nlohmann::json{ - {"id", internal::msg_id++}, - {"method", "Runtime.evaluate"}, - {"params", { - {"userGesture", true}, - {"expression", js} - }} - }.dump()); - nlohmann::json res = nullptr; - bool exit = false; - while (ws->getReadyState() != easywsclient::WebSocket::CLOSED) { - ws->poll(); - ws->dispatch([&ws, &res, &exit](const std::string& message) { - res = nlohmann::json::parse(message)["result"]["result"]["value"]; - exit = true; - }); - if (exit) { - ws->close(); - return res; - } + } + if (!info.webSocketDebuggerUrl.empty()) + { + return [&info](const nlohmann::json::array_t& tabList) + { + for (const auto& tab : tabList) { + if (tab["webSocketDebuggerUrl"].get() == info.webSocketDebuggerUrl) { + return tab; } -#ifdef _WIN32 - WSACleanup(); -#endif - return res; } -#ifdef _WIN32 - WSACleanup(); -#endif - return nullptr; - } + return nlohmann::json{}; + }; + } - } + return nullptr; + } + )(); + if (find_tab == nullptr) { + return false; } - return nullptr; - } -} \ No newline at end of file + const auto tabs = AvailableTabs(port); + const auto tab = find_tab(tabs); + if (tab.empty()) { + return false; + } + + InjectJs(tab["webSocketDebuggerUrl"].get(), glossitweaks_js, port); + glossi_tweaks_injected_map_[tab["id"].get()] = true; + return true; + } + + bool SteamTweaks::uninstallTweaks() + { + if (!CEFDebugAvailable()) { + return false; + } + if (glossi_tweaks_injected_map_.empty()) { + return false; + } + + for (auto& tab : AvailableTabNames()) { + InjectJsByName(tab, uninstall_glossi_tweaks_js_); + } + + glossi_tweaks_injected_map_.clear(); + return true; + } + +} diff --git a/CEFInjectLib/CEFInject.h b/CEFInjectLib/CEFInject.h index fc077f6..1a877f0 100644 --- a/CEFInjectLib/CEFInject.h +++ b/CEFInjectLib/CEFInject.h @@ -30,7 +30,41 @@ namespace CEFInject internal::port_ = port; } bool CEFDebugAvailable(uint16_t port = internal::port_); - std::vector AvailableTabs(uint16_t port = internal::port_); - nlohmann::json InjectJs(const std::wstring& tabname, const std::wstring& js, uint16_t port = internal::port_); + std::vector AvailableTabNames(uint16_t port = internal::port_); + nlohmann::json::array_t AvailableTabs(uint16_t port = internal::port_); + nlohmann::json InjectJs(std::string_view debug_url, std::wstring_view js, uint16_t port = internal::port_); + nlohmann::json InjectJsByName(std::wstring_view tabname, std::wstring_view js, uint16_t port = internal::port_); + + class SteamTweaks + { + public: + SteamTweaks() = default; + + struct Tab_Info + { + std::string name; + std::string id; + std::string webSocketDebuggerUrl; + }; + bool injectGlosSITweaks(std::string_view tab_name, uint16_t port = internal::port_); + bool injectGlosSITweaks(const Tab_Info& info, uint16_t port = internal::port_); + public: + bool uninstallTweaks(); + + // TODO: Provide API to auto-inject + + // TODO: build system to auto inject "user plugins" + + private: + using tab_id = std::string; + std::map glossi_tweaks_injected_map_; + + static constexpr std::string_view steam_tweaks_path_ = "SteamTweaks"; + static constexpr std::wstring_view uninstall_glossi_tweaks_js_ = LR"( + (() => { + return window.GlosSITweaks?.GlosSI?.uninstall(); + })(); + )"; + }; } \ No newline at end of file From 08ffcad760f88af6ef0ee443d664e158c7fc4352 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 30 Jan 2023 21:09:03 +0100 Subject: [PATCH 35/65] Fix GlosSIConfig crashing on launch --- GlosSIConfig/qml/GlobalConf.qml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/GlosSIConfig/qml/GlobalConf.qml b/GlosSIConfig/qml/GlobalConf.qml index a2c39b1..616b1fc 100644 --- a/GlosSIConfig/qml/GlobalConf.qml +++ b/GlosSIConfig/qml/GlobalConf.qml @@ -153,18 +153,18 @@ Item { spacing: 16 Label { topPadding: 8 - id: GlobalModeGameIdLabel + id: globalModeGameIdLabel text: qsTr("GlobalMode GameId") } FluentTextInput { width: 128 - id: GlobalModeGameId + id: globalModeGameId enabled: false text: config.globalModeGameId onTextChanged: config.globalModeGameId = text } Button { - id: GlobalModeGameIdButton + id: globalModeGameIdButton text: qsTr("Create global-/desktop-mode shortcut") onClicked: { const globalModeConf = uiModel.getDefaultConf(); @@ -175,7 +175,7 @@ Item { steamChangedDialog.open(); } const globalModeGID = uiModel.globalModeShortcutGameId(); - GlobalModeGameId.text = globalModeGID; + globalModeGameId.text = globalModeGID; setTimeout(() => { uiModel.saveDefaultConf(config); done(); @@ -185,7 +185,7 @@ Item { visible: !uiModel.globalModeShortcutExists() } Button { - id: GlobalModeGameIdConfigButton + id: globalModeGameIdConfigButton text: qsTr("Open global-/desktop-mode controller config") onClicked: { Qt.openUrlExternally("steam://currentcontrollerconfig/" + uiModel.globalModeShortcutAppId() + "/"); From 4d96ed94a16cb8eb6c04441113f7357f4f594321 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Tue, 31 Jan 2023 10:08:57 +0100 Subject: [PATCH 36/65] Throw in some compiler optimizations (SSE2/fast floats) --- CEFInjectLib/CEFInjectLib.vcxproj | 7 +++++++ GlosSIConfig/GlosSIConfig.vcxproj | 8 ++++++++ GlosSITarget/GlosSITarget.vcxproj | 7 +++++++ GlosSIWatchdog/GlosSIWatchdog.vcxproj | 7 +++++++ UWPOverlayEnablerDLL/UWPOverlayEnablerDLL.vcxproj | 7 +++++++ 5 files changed, 36 insertions(+) diff --git a/CEFInjectLib/CEFInjectLib.vcxproj b/CEFInjectLib/CEFInjectLib.vcxproj index f39c501..78ba19e 100644 --- a/CEFInjectLib/CEFInjectLib.vcxproj +++ b/CEFInjectLib/CEFInjectLib.vcxproj @@ -125,6 +125,10 @@ NotUsing pch.h stdcpp20 + Speed + true + StreamingSIMDExtensions2 + Fast @@ -143,6 +147,9 @@ NotUsing pch.h stdcpp20 + Speed + StreamingSIMDExtensions2 + Fast diff --git a/GlosSIConfig/GlosSIConfig.vcxproj b/GlosSIConfig/GlosSIConfig.vcxproj index 4fab583..bdc24c2 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj +++ b/GlosSIConfig/GlosSIConfig.vcxproj @@ -71,6 +71,10 @@ false NOMINMAX;CONFIGAPP;%(PreprocessorDefinitions) ..\deps\WinReg;..\deps\fifo_map\src;..\deps\Shortcuts_VDF\include;%(AdditionalIncludeDirectories) + Speed + true + StreamingSIMDExtensions2 + Fast true @@ -94,6 +98,10 @@ false NOMINMAX;CONFIGAPP;%(PreprocessorDefinitions) ..\deps\WinReg;..\deps\fifo_map\src;..\deps\Shortcuts_VDF\include;%(AdditionalIncludeDirectories) + Speed + true + StreamingSIMDExtensions2 + Fast true diff --git a/GlosSITarget/GlosSITarget.vcxproj b/GlosSITarget/GlosSITarget.vcxproj index a11bfc3..26b7db1 100644 --- a/GlosSITarget/GlosSITarget.vcxproj +++ b/GlosSITarget/GlosSITarget.vcxproj @@ -51,6 +51,7 @@ v143 true Unicode + @@ -128,6 +129,9 @@ _DEBUG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;SUBHOOK_STATIC;%(PreprocessorDefinitions) true stdcpp20 + true + StreamingSIMDExtensions2 + Fast Windows @@ -151,6 +155,9 @@ NDEBUG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;SUBHOOK_STATIC;%(PreprocessorDefinitions) true stdcpp20 + Speed + StreamingSIMDExtensions2 + Fast Windows diff --git a/GlosSIWatchdog/GlosSIWatchdog.vcxproj b/GlosSIWatchdog/GlosSIWatchdog.vcxproj index 8e27f62..b756f12 100644 --- a/GlosSIWatchdog/GlosSIWatchdog.vcxproj +++ b/GlosSIWatchdog/GlosSIWatchdog.vcxproj @@ -111,6 +111,10 @@ _DEBUG;_WINDOWS;%(PreprocessorDefinitions);WATCHDOG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING true stdcpp20 + Speed + true + StreamingSIMDExtensions2 + Fast Windows @@ -131,6 +135,9 @@ NDEBUG;_WINDOWS;%(PreprocessorDefinitions);WATCHDOG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING true stdcpp20 + Speed + StreamingSIMDExtensions2 + Fast Windows diff --git a/UWPOverlayEnablerDLL/UWPOverlayEnablerDLL.vcxproj b/UWPOverlayEnablerDLL/UWPOverlayEnablerDLL.vcxproj index 34e5567..b237e10 100644 --- a/UWPOverlayEnablerDLL/UWPOverlayEnablerDLL.vcxproj +++ b/UWPOverlayEnablerDLL/UWPOverlayEnablerDLL.vcxproj @@ -127,6 +127,10 @@ NotUsing pch.h stdcpp20 + Speed + true + StreamingSIMDExtensions2 + Fast Windows @@ -145,6 +149,9 @@ NotUsing pch.h stdcpp20 + StreamingSIMDExtensions2 + Fast + Speed Windows From 4c847543fc68df643a3182cce95187ea90fff80a Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Tue, 31 Jan 2023 10:27:58 +0100 Subject: [PATCH 37/65] CEFInject: SteamTweaks: uninstallTweaks: option to uninstall regardless of stored-state TODO: use in watchDog --- CEFInjectLib/CEFInject.cpp | 4 ++-- CEFInjectLib/CEFInject.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp index df86899..96d8267 100644 --- a/CEFInjectLib/CEFInject.cpp +++ b/CEFInjectLib/CEFInject.cpp @@ -255,12 +255,12 @@ namespace CEFInject return true; } - bool SteamTweaks::uninstallTweaks() + bool SteamTweaks::uninstallTweaks(bool force) { if (!CEFDebugAvailable()) { return false; } - if (glossi_tweaks_injected_map_.empty()) { + if (glossi_tweaks_injected_map_.empty() && !force) { return false; } diff --git a/CEFInjectLib/CEFInject.h b/CEFInjectLib/CEFInject.h index 1a877f0..2d7b36e 100644 --- a/CEFInjectLib/CEFInject.h +++ b/CEFInjectLib/CEFInject.h @@ -49,7 +49,7 @@ namespace CEFInject bool injectGlosSITweaks(std::string_view tab_name, uint16_t port = internal::port_); bool injectGlosSITweaks(const Tab_Info& info, uint16_t port = internal::port_); public: - bool uninstallTweaks(); + bool uninstallTweaks(bool force = false); // TODO: Provide API to auto-inject From cb6cbdeac66965f8dbcb02ba82206d5af153600b Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Tue, 31 Jan 2023 22:02:12 +0100 Subject: [PATCH 38/65] More work on CEFInject / SteamTweaks --- CEFInjectLib/CEFInject.cpp | 339 +++++++++++++----- CEFInjectLib/CEFInject.h | 34 +- CEFInjectLib/CEFInjectLib.vcxproj | 8 +- GlosSI.sln | 5 +- GlosSIConfig/Resource.rc | 8 +- GlosSITarget/HttpServer.cpp | 6 + GlosSITarget/Resource.rc | 8 +- GlosSITarget/SteamTarget.cpp | 12 + GlosSIWatchdog/GlosSIWatchdog.vcxproj | 10 +- GlosSIWatchdog/dllmain.cpp | 7 + Installer/Installer.nsi | 2 +- SteamTweaks/rollup.config.js | 2 + SteamTweaks/src/GlosSITweaks.ts | 27 ++ .../Overlay/SharedContext/HideFPSCounter.ts | 7 +- SteamTweaks/src/common/util/util.ts | 12 + 15 files changed, 379 insertions(+), 108 deletions(-) create mode 100644 SteamTweaks/src/common/util/util.ts diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp index 96d8267..edde29d 100644 --- a/CEFInjectLib/CEFInject.cpp +++ b/CEFInjectLib/CEFInject.cpp @@ -27,6 +27,10 @@ limitations under the License. #include #include +#include + +#include "../common/Settings.h" + namespace CEFInject { namespace internal @@ -34,7 +38,8 @@ namespace CEFInject httplib::Client GetHttpClient(uint16_t port) { httplib::Client cli("localhost", port); - cli.set_connection_timeout(1000); + cli.set_connection_timeout(0, 200000); + cli.set_read_timeout(0, 500000); return cli; } @@ -44,8 +49,6 @@ namespace CEFInject bool CEFDebugAvailable(uint16_t port) { auto cli = internal::GetHttpClient(port); - cli.set_connection_timeout(500); - cli.set_read_timeout(500); if (auto res = cli.Get("/json")) { if (res->status == 200) { return true; @@ -64,21 +67,28 @@ namespace CEFInject return tabs; } - nlohmann::json::array_t AvailableTabs(uint16_t port) + nlohmann::basic_json<> AvailableTabs(uint16_t port) { if (!CEFDebugAvailable()) { return nlohmann::json::array(); } + + //if (Settings::common.extendedLogging) + //{ + spdlog::trace("Fetching available Steam CEF tabs"); + //} + auto cli = internal::GetHttpClient(port); if (auto res = cli.Get("/json")) { if (res->status == 200) { return nlohmann::json::parse(res->body); } } + return nlohmann::json::array(); } - nlohmann::json InjectJs(std::string_view debug_url, std::wstring_view js, uint16_t port) + nlohmann::basic_json<> InjectJs(std::string_view tab_name, std::string_view debug_url, std::wstring_view js, uint16_t port) { #ifdef _WIN32 INT rc; @@ -96,43 +106,50 @@ namespace CEFInject }; if (ws) { - auto json_payload = nlohmann::json{ - {"id", internal::msg_id++}, - {"method", "Runtime.evaluate"}, - {"params", { - {"userGesture", true}, - {"expression", std::wstring{js.data()}} - //{"expression", js} - }} - }; - auto payload_string = json_payload.dump(); - ws->send(payload_string); nlohmann::json res = nullptr; - bool exit = false; - while (ws->getReadyState() != easywsclient::WebSocket::CLOSED) { - ws->poll(); - ws->dispatch([&ws, &res, &exit](const std::string& message) { - const auto msg = nlohmann::json::parse(message); - try - { - res = msg.at("result").at("result").at("value"); - } catch (...){ - // - } + try + { + auto json_payload = nlohmann::json{ + {"id", internal::msg_id++}, + {"method", "Runtime.evaluate"}, + {"params", { + {"userGesture", true}, + {"expression", std::wstring{js.data()}} + //{"expression", js} + }} + }; + auto payload_string = json_payload.dump(); + + spdlog::debug("Injecting JS into tab: {}, {}; JS: {}", tab_name, debug_url, payload_string); + + ws->send(payload_string); + bool exit = false; + while (ws->getReadyState() != easywsclient::WebSocket::CLOSED) { + ws->poll(); + ws->dispatch([&ws, &res, &exit](const std::string& message) { + const auto msg = nlohmann::json::parse(message); try { - if (res.is_null() && msg.at("result").at("result").at("type").get() != "undefined") { - res = nlohmann::json::parse(message); + if (msg.at("result").at("result").at("type").get() != "undefined") { + res = msg.at("result").at("result").at("value"); } - } catch (...) { - res = nlohmann::json::parse(message); } - exit = true; - }); - if (exit) { - ws->close(); - return res; + catch (...) { + spdlog::error("CEFInject: Error parsing injection-result value: {}", message); + res = msg; + } + exit = true; + }); + if (exit) { + ws->close(); + return res; + } } + } catch (...) { + spdlog::error( + "CEFInject: Error injecting JS into tab: {}, {}", + std::string(tab_name.data()), + std::string(debug_url.data())); } #ifdef _WIN32 WSACleanup(); @@ -145,7 +162,7 @@ namespace CEFInject return nullptr; } - nlohmann::json InjectJsByName(std::wstring_view tabname, std::wstring_view js, uint16_t port) + nlohmann::basic_json<> InjectJsByName(std::wstring_view tabname, std::wstring_view js, uint16_t port) { auto cli = internal::GetHttpClient(port); if (auto res = cli.Get("/json")) { @@ -153,7 +170,7 @@ namespace CEFInject const auto json = nlohmann::json::parse(res->body); for (const auto& tab : json) { if (tab["title"].get().starts_with(tabname)) { - return InjectJs(tab["webSocketDebuggerUrl"].get(), js, port); + return InjectJs(tab["title"].get(), tab["webSocketDebuggerUrl"].get(), js, port); } } } @@ -165,7 +182,7 @@ namespace CEFInject bool SteamTweaks::injectGlosSITweaks(std::string_view tab_name, uint16_t port) { if (tab_name.empty()) { - for (const auto& tn : AvailableTabNames()) + for (auto ts = AvailableTabNames(); const auto & tn : ts) { // meh! injectGlosSITweaks(util::string::to_string(tn), port); @@ -181,20 +198,11 @@ namespace CEFInject return false; } - auto glossi_path = util::path::getGlosSIDir(); - glossi_path /= steam_tweaks_path_; - glossi_path /= "GlosSITweaks.js"; - - if (!std::filesystem::exists(glossi_path)) + if (glossi_tweaks_js_.empty()) { - return false; - } - - std::wifstream js_file(glossi_path); - std::wstring glossitweaks_js{ (std::istreambuf_iterator(js_file)), - std::istreambuf_iterator() }; - if (glossitweaks_js.empty()) { - return false; + if (!readGlosSITweaksJs()) { + return false; + } } const auto find_tab = ( @@ -212,34 +220,34 @@ namespace CEFInject return nlohmann::json{}; }; } - if (!info.id.empty()) - { - return [&info](const nlohmann::json::array_t& tabList) - { - for (const auto& tab : tabList) { - if (tab["id"].get() == info.id) { - return tab; - } - } - return nlohmann::json{}; - }; - + if (!info.id.empty()) + { + return [&info](const nlohmann::json::array_t& tabList) + { + for (const auto& tab : tabList) { + if (tab["id"].get() == info.id) { + return tab; + } } - if (!info.webSocketDebuggerUrl.empty()) - { - return [&info](const nlohmann::json::array_t& tabList) - { - for (const auto& tab : tabList) { - if (tab["webSocketDebuggerUrl"].get() == info.webSocketDebuggerUrl) { - return tab; - } - } - return nlohmann::json{}; - }; + return nlohmann::json{}; + }; - } - return nullptr; + } + if (!info.webSocketDebuggerUrl.empty()) + { + return [&info](const nlohmann::json::array_t& tabList) + { + for (const auto& tab : tabList) { + if (tab["webSocketDebuggerUrl"].get() == info.webSocketDebuggerUrl) { + return tab; } + } + return nlohmann::json{}; + }; + + } + return nullptr; + } )(); if (find_tab == nullptr) { return false; @@ -250,9 +258,13 @@ namespace CEFInject return false; } - InjectJs(tab["webSocketDebuggerUrl"].get(), glossitweaks_js, port); - glossi_tweaks_injected_map_[tab["id"].get()] = true; - return true; + const auto res = InjectJs(tab["title"].get(), tab["webSocketDebuggerUrl"].get(), glossi_tweaks_js_, port); + if (res.is_boolean() && res.get()) { + glossi_tweaks_injected_map_[tab["id"].get()] = true; + spdlog::trace("CEFInject: GlosSITweaks injected into tab: {}", tab["title"].get()); + return true; + } + return false; } bool SteamTweaks::uninstallTweaks(bool force) @@ -260,16 +272,179 @@ namespace CEFInject if (!CEFDebugAvailable()) { return false; } + + auto_inject_ = false; + if (auto_inject_future_.valid()) { + auto_inject_future_.wait(); + } + if (glossi_tweaks_injected_map_.empty() && !force) { return false; } - for (auto& tab : AvailableTabNames()) { - InjectJsByName(tab, uninstall_glossi_tweaks_js_); + std::vector> futures; + for (auto ts = AvailableTabs(); auto & tab : ts) { + futures.push_back(std::async(std::launch::async, [this, &tab]() + { + InjectJs(tab["title"].get(), tab["webSocketDebuggerUrl"].get(), uninstall_glossi_tweaks_js_); + })); } + for (auto& f : futures) + { + if (f.valid()){ + f.wait(); + } + } glossi_tweaks_injected_map_.clear(); return true; } + void SteamTweaks::update(float elapsed_time) + { + if (!auto_inject_) { + return; + } + using namespace std::chrono_literals; + if (auto_inject_future_.valid() && auto_inject_future_.wait_for(0ms) != std::future_status::ready) { + time_since_last_update_ = 0.0f; + return; + } + + time_since_last_update_ += elapsed_time; + if (time_since_last_update_ < update_interval_) { + return; + } + time_since_last_update_ = 0.0f; + + spdlog::trace("CEFInject: Starting auto inject GlosSITweaks"); + auto_inject_future_ = std::async(std::launch::async, [this]() { + + if (js_tweaks_cache_.empty()) [[unlikely]] { + readAvailableTweaks(); + } + + if (glossi_tweaks_js_.empty()) [[unlikely]] + { + if (!readGlosSITweaksJs()) { + return; + } + } + + auto futures = std::vector>{}; + auto tabs = AvailableTabs(); + for (auto& tab : tabs) { + if (glossi_tweaks_injected_map_.contains(tab["id"].get())) { + continue; + } + glossi_tweaks_injected_map_[tab["id"].get()] = true; + + futures.push_back(std::async([this, &tab]() + { + InjectJs(tab["title"].get(), tab["webSocketDebuggerUrl"].get(), glossi_tweaks_js_); + + for (auto& [path, js] : js_tweaks_cache_) { + const auto dir_name = path.parent_path().filename(); + + if (path_tab_map_.contains(dir_name.wstring())) { + if (tab["title"].get().starts_with(path_tab_map_.at(dir_name.wstring()))) { + InjectJs(tab["title"].get(), tab["webSocketDebuggerUrl"].get(), js); + } + } + } + })); + } + for (auto& f : futures) + { + if (f.valid()) { + f.wait(); + } + } + spdlog::trace("CEFInject: Auto Inject thread done"); + }); + } + + bool SteamTweaks::isAutoInject() const + { + return auto_inject_; + } + + void SteamTweaks::setAutoInject(const bool auto_inject) + { + auto_inject_ = auto_inject; + } + + bool SteamTweaks::readGlosSITweaksJs() + { + if (glossi_tweaks_js_.empty()) + { + spdlog::trace("CEFInject: Loadings GlosSITweaks.js"); + + auto glossi_path = util::path::getGlosSIDir(); + glossi_path /= steam_tweaks_path_; + glossi_path /= "GlosSITweaks.js"; + + if (!std::filesystem::exists(glossi_path)) + { + spdlog::error("CEFInject: GlosSITweaks.js not found"); + return false; + } + + std::wifstream js_file(glossi_path); + glossi_tweaks_js_ = { (std::istreambuf_iterator(js_file)), + std::istreambuf_iterator() }; + if (glossi_tweaks_js_.empty()) { + spdlog::error("CEFInject: GlosSITweaks.js empty?"); + return false; + } + js_file.close(); + } + return true; + } + + void SteamTweaks::readAvailableTweaks(bool builtin) + { + auto tweaks_path = builtin ? util::path::getGlosSIDir() : util::path::getDataDirPath(); + spdlog::log( + builtin ? spdlog::level::trace : spdlog::level::debug, + "CEFInject: Loading {} {} {}", + builtin ? "builtin" : "user", + "tweaks from", + tweaks_path.string() + ); + tweaks_path /= steam_tweaks_path_; + if (!std::filesystem::exists(tweaks_path)) + { + return; + } + + auto find_tweak_files = [this](std::wstring_view path, auto&& recurse) -> void + { + for (const auto& dir_file : std::filesystem::directory_iterator(path)) + { + if (std::filesystem::is_directory(dir_file)) + { + recurse(dir_file.path().wstring(), recurse); + continue; + } + if (std::filesystem::is_regular_file(dir_file) && dir_file.path().extension() == ".js") + { + if (dir_file.path().filename() == "GlosSITweaks.js") { + continue; + } + std::wifstream js_file(dir_file.path()); + std::wstring tweaks_js = { (std::istreambuf_iterator(js_file)), + std::istreambuf_iterator() }; + if (tweaks_js.empty()) { + continue; + } + js_file.close(); + js_tweaks_cache_[dir_file.path().wstring()] = tweaks_js; + } + } + }; + find_tweak_files(tweaks_path.wstring(), find_tweak_files); + + + } } diff --git a/CEFInjectLib/CEFInject.h b/CEFInjectLib/CEFInject.h index 2d7b36e..2fd103d 100644 --- a/CEFInjectLib/CEFInject.h +++ b/CEFInjectLib/CEFInject.h @@ -15,6 +15,7 @@ limitations under the License. */ #pragma once +#include #include #include @@ -31,9 +32,9 @@ namespace CEFInject } bool CEFDebugAvailable(uint16_t port = internal::port_); std::vector AvailableTabNames(uint16_t port = internal::port_); - nlohmann::json::array_t AvailableTabs(uint16_t port = internal::port_); - nlohmann::json InjectJs(std::string_view debug_url, std::wstring_view js, uint16_t port = internal::port_); - nlohmann::json InjectJsByName(std::wstring_view tabname, std::wstring_view js, uint16_t port = internal::port_); + nlohmann::basic_json<> AvailableTabs(uint16_t port = internal::port_); + nlohmann::basic_json<> InjectJs(std::string_view tab_name, std::string_view debug_url, std::wstring_view js, uint16_t port = internal::port_); + nlohmann::basic_json<> InjectJsByName(std::wstring_view tabname, std::wstring_view js, uint16_t port = internal::port_); class SteamTweaks { @@ -48,17 +49,36 @@ namespace CEFInject }; bool injectGlosSITweaks(std::string_view tab_name, uint16_t port = internal::port_); bool injectGlosSITweaks(const Tab_Info& info, uint16_t port = internal::port_); - public: bool uninstallTweaks(bool force = false); - - // TODO: Provide API to auto-inject - // TODO: build system to auto inject "user plugins" + void update(float elapsed_time); + [[nodiscard]] bool isAutoInject() const; + void setAutoInject(const bool auto_inject); private: + bool readGlosSITweaksJs(); + void readAvailableTweaks(bool builtin = true); + bool auto_inject_ = false; + + static constexpr float update_interval_ = 30.f; + float time_since_last_update_ = update_interval_; using tab_id = std::string; std::map glossi_tweaks_injected_map_; + std::future auto_inject_future_; + + std::wstring glossi_tweaks_js_; + + std::map js_tweaks_cache_; + + using path_name = std::wstring; + using tab_name = std::string; + static inline const std::map path_tab_map_ = { + {L"SharedContext", "Steam Shared Context"}, + {L"Overlay", "HOW TF GET OVERLAY TAB NAME?"}, // TODO: Figure out how to get the overlay tab name + }; + + static constexpr std::string_view steam_shared_ctx_tab_name_ = "Steam Shared Context"; static constexpr std::string_view steam_tweaks_path_ = "SteamTweaks"; static constexpr std::wstring_view uninstall_glossi_tweaks_js_ = LR"( (() => { diff --git a/CEFInjectLib/CEFInjectLib.vcxproj b/CEFInjectLib/CEFInjectLib.vcxproj index 78ba19e..45991ac 100644 --- a/CEFInjectLib/CEFInjectLib.vcxproj +++ b/CEFInjectLib/CEFInjectLib.vcxproj @@ -77,10 +77,10 @@ - ..\deps\cpp-httplib;..\deps\json\include;..\deps\easywsclient;$(IncludePath) + ..\deps\cpp-httplib;..\deps\json\include;..\deps\easywsclient;..\deps\spdlog\include;$(IncludePath) - ..\deps\cpp-httplib;..\deps\json\include;..\deps\easywsclient;$(IncludePath) + ..\deps\cpp-httplib;..\deps\json\include;..\deps\easywsclient;..\deps\spdlog\include;$(IncludePath) @@ -120,7 +120,7 @@ Level3 true - _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_DEBUG;_LIB;%(PreprocessorDefinitions) + SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_CRT_SECURE_NO_WARNINGS;_DEBUG;_LIB;%(PreprocessorDefinitions) true NotUsing pch.h @@ -142,7 +142,7 @@ true true true - _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;NDEBUG;_LIB;%(PreprocessorDefinitions) + SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_CRT_SECURE_NO_WARNINGS;NDEBUG;_LIB;%(PreprocessorDefinitions) true NotUsing pch.h diff --git a/GlosSI.sln b/GlosSI.sln index 05714a1..7d692e5 100644 --- a/GlosSI.sln +++ b/GlosSI.sln @@ -13,6 +13,9 @@ EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UWPOverlayEnablerDLL", "UWPOverlayEnablerDLL\UWPOverlayEnablerDLL.vcxproj", "{50212575-87E2-40AB-87EE-EAED19C8EBB2}" EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GlosSIWatchdog", "GlosSIWatchdog\GlosSIWatchdog.vcxproj", "{BF273B90-CB69-43C8-9AF6-F3256DAFD41E}" + ProjectSection(ProjectDependencies) = postProject + {74FBA967-AB7E-43EA-B561-3F4821954B3B} = {74FBA967-AB7E-43EA-B561-3F4821954B3B} + EndProjectSection EndProject Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CEFInjectLib", "CEFInjectLib\CEFInjectLib.vcxproj", "{74FBA967-AB7E-43EA-B561-3F4821954B3B}" EndProject @@ -75,7 +78,7 @@ Global HideSolutionNode = FALSE EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {789386B6-7D1E-4F9C-BF2E-9B5EDC3BB7C8} Qt5Version = 6.2.2_msvc2019_64 + SolutionGuid = {789386B6-7D1E-4F9C-BF2E-9B5EDC3BB7C8} EndGlobalSection EndGlobal diff --git a/GlosSIConfig/Resource.rc b/GlosSIConfig/Resource.rc index 2d28795..276c41a 100644 --- a/GlosSIConfig/Resource.rc +++ b/GlosSIConfig/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,2,0030000050130 - PRODUCTVERSION 0,1,2,0030000050130 + FILEVERSION 0,1,2,0041001700025 + PRODUCTVERSION 0,1,2,0041001700025 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - Config" - VALUE "FileVersion", "0.1.2.0-30-geb5f13f" + VALUE "FileVersion", "0.1.2.0-41-g17b0d25" VALUE "InternalName", "GlosSIConfig" VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSIConfig.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.2.0-30-geb5f13f" + VALUE "ProductVersion", "0.1.2.0-41-g17b0d25" END END BLOCK "VarFileInfo" diff --git a/GlosSITarget/HttpServer.cpp b/GlosSITarget/HttpServer.cpp index b6da48b..e71c594 100644 --- a/GlosSITarget/HttpServer.cpp +++ b/GlosSITarget/HttpServer.cpp @@ -32,6 +32,12 @@ void HttpServer::run() auto setCorsHeader = [](httplib::Response& res) { res.set_header("Access-Control-Allow-Origin", "*"); }; + + server_.Get("/", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { + res.set_content("", "text/json"); + setCorsHeader(res); + }); + server_.Get("/launched-pids", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { const nlohmann::json j = app_launcher_.launchedPids(); res.set_content(j.dump(), "text/json"); diff --git a/GlosSITarget/Resource.rc b/GlosSITarget/Resource.rc index ba91c46..8284c44 100644 --- a/GlosSITarget/Resource.rc +++ b/GlosSITarget/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,2,0031000204300 - PRODUCTVERSION 0,1,2,0031000204300 + FILEVERSION 0,1,2,0041007165813 + PRODUCTVERSION 0,1,2,0041007165813 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - SteamTarget" - VALUE "FileVersion", "0.1.2.0-31-gd2b43ff" + VALUE "FileVersion", "0.1.2.0-41-g7165813" VALUE "InternalName", "GlosSITarget" VALUE "LegalCopyright", "Copyright (C) 2021-2022 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSITarget.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.2.0-31-gd2b43ff" + VALUE "ProductVersion", "0.1.2.0-41-g7165813" END END BLOCK "VarFileInfo" diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 54f97fa..a145465 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -58,6 +58,10 @@ int SteamTarget::run() if (Settings::common.standaloneModeGameId == L"") { spdlog::error("No game id set for standalone mode. Controller will use desktop-config!"); } + auto steam_tweaks = CEFInject::SteamTweaks(); + steam_tweaks.setAutoInject(true); + if (!overlay_.expired()) + overlay_.lock()->setEnabled(false); std::vector> end_frame_callbacks; @@ -116,6 +120,8 @@ int SteamTarget::run() server_.run(); bool delayed_full_init_1_frame = false; + sf::Clock frame_time_clock; + while (run_) { if (!fully_initialized_ && can_fully_initialize_ && delayed_full_init_1_frame) { init_FuckingRenameMe(); @@ -130,6 +136,9 @@ int SteamTarget::run() overlayHotkeyWorkaround(); window_.update(); + steam_tweaks.update(frame_time_clock.getElapsedTime().asSeconds()); + + // Wait on shutdown; User might get confused if window closes to fast if anything with launchApp get's borked. if (delayed_shutdown_) { if (delay_shutdown_clock_.getElapsedTime().asSeconds() >= 3) { @@ -145,7 +154,9 @@ int SteamTarget::run() efc(); } end_frame_callbacks.clear(); + frame_time_clock.restart(); } + steam_tweaks.uninstallTweaks(); tray->exit(); server_.stop(); @@ -156,6 +167,7 @@ int SteamTarget::run() #endif launcher_.close(); } + return 0; } diff --git a/GlosSIWatchdog/GlosSIWatchdog.vcxproj b/GlosSIWatchdog/GlosSIWatchdog.vcxproj index b756f12..1ab4186 100644 --- a/GlosSIWatchdog/GlosSIWatchdog.vcxproj +++ b/GlosSIWatchdog/GlosSIWatchdog.vcxproj @@ -71,10 +71,12 @@ - ..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;$(IncludePath) + ..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;..\CEFInjectLib;$(IncludePath) + ..\x64\Debug;$(LibraryPath) - ..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;$(IncludePath) + ..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;..\CEFInjectLib;$(IncludePath) + ..\x64\Release;$(LibraryPath) @@ -119,7 +121,7 @@ Windows true - hid.lib;Cfgmgr32.lib;setupapi.lib;%(AdditionalDependencies) + hid.lib;Cfgmgr32.lib;setupapi.lib;CefInjectLib.lib;%(AdditionalDependencies) powershell.exe $(SolutionDir)version_help.ps1 @@ -144,7 +146,7 @@ true true true - hid.lib;Cfgmgr32.lib;setupapi.lib;%(AdditionalDependencies) + hid.lib;Cfgmgr32.lib;setupapi.lib;CefInjectLib.lib;%(AdditionalDependencies) Upading version based on git;%(Outputs) diff --git a/GlosSIWatchdog/dllmain.cpp b/GlosSIWatchdog/dllmain.cpp index f885b1c..f9fac98 100644 --- a/GlosSIWatchdog/dllmain.cpp +++ b/GlosSIWatchdog/dllmain.cpp @@ -27,10 +27,13 @@ limitations under the License. #include + #include "../version.hpp" #include "../common/Settings.h" #include "../common/HidHide.h" +#include + bool IsProcessRunning(DWORD pid) { const HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid); @@ -116,6 +119,10 @@ DWORD WINAPI watchdog(HMODULE hModule) HidHide hidhide; hidhide.disableHidHide(); + spdlog::info("Uninstalling GlosSITweaks"); + auto steam_tweaks = CEFInject::SteamTweaks(); + steam_tweaks.uninstallTweaks(true); + if (Settings::launch.closeOnExit) { spdlog::info("Closing launched processes"); diff --git a/Installer/Installer.nsi b/Installer/Installer.nsi index a27ec7f..71d0dee 100644 --- a/Installer/Installer.nsi +++ b/Installer/Installer.nsi @@ -3,7 +3,7 @@ !define APP_NAME "GlosSI" !define COMP_NAME "Peter Repukat - Flatspotsoftware" !define WEB_SITE "https://glossi.flatspot.pictures/" -!define VERSION "0.1.2.0-31-gd2b43ff" +!define VERSION "0.1.2.0-41-g7165813" !define COPYRIGHT "Peter Repukat - FlatspotSoftware © 2017-2022" !define DESCRIPTION "SteamInput compatibility tool" !define INSTALLER_NAME "GlosSI-Installer.exe" diff --git a/SteamTweaks/rollup.config.js b/SteamTweaks/rollup.config.js index 3eec110..092579f 100644 --- a/SteamTweaks/rollup.config.js +++ b/SteamTweaks/rollup.config.js @@ -27,6 +27,7 @@ export default [ dir: 'dist', sourcemap: "inline", format: 'iife', + // name: 'GlosSITweaks' // don't use name; don't pollute global namespace }, plugins: [tsPluginConf], }, @@ -37,6 +38,7 @@ export default [ file: file.replace('src', 'dist').replace(/\.ts$/, '.js'), sourcemap: "inline", format: 'iife', + // name: path.basename(file).replace(/\.ts$/, '') // don't use name; don't pollute global namespace }, plugins: [tsPluginConf], } diff --git a/SteamTweaks/src/GlosSITweaks.ts b/SteamTweaks/src/GlosSITweaks.ts index 4f710af..29e5c6c 100644 --- a/SteamTweaks/src/GlosSITweaks.ts +++ b/SteamTweaks/src/GlosSITweaks.ts @@ -1,4 +1,8 @@ import type { SteamConfig } from './common/util/types'; +import { fetchWithTimeout } from './common/util/util'; + + + class SteamTargetApi { public getSteamSettings(): Promise { return fetch('http://localhost:8756/steam_settings') @@ -8,8 +12,16 @@ class SteamTargetApi { ) ); } + + public getGlosSIActive() { + return fetchWithTimeout('http://localhost:8756/', { timeout: 10000 }) + .then( + () => true + ).catch((e) => false); + } } + class GlosSIApiCtor { public readonly SteamTarget: SteamTargetApi = new SteamTargetApi(); } @@ -51,8 +63,23 @@ const installGlosSIApi = () => { } }; window.GlosSITweaks.GlosSI.install(); + + const glossiCheckInterval = setInterval(() => { + if (window.GlosSIApi) { + window.GlosSIApi.SteamTarget.getGlosSIActive().then((active) => { + if (!active) { + window?.GlosSITweaks?.GlosSI?.uninstall?.(); + } + }); + return; + } + clearTimeout(glossiCheckInterval) + }, 5000) + }; if (!window.GlosSITweaks || !window.GlosSIApi) { installGlosSIApi(); } + +export default !!window.GlosSITweaks && !!window.GlosSIApi; diff --git a/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts b/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts index bfb99d0..7bbf1bb 100644 --- a/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts +++ b/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts @@ -4,7 +4,7 @@ import { initTweak } from "../../../common/tweakApi"; const backup: { originalFpsCorner?: number } = {}; -initTweak('AnotherTweak', { +initTweak('HideFPSCounter', { install: async () => { backup.originalFpsCorner = Number( ((await GlosSIApi.SteamTarget.getSteamSettings()).system as SteamConfig) @@ -13,6 +13,11 @@ initTweak('AnotherTweak', { SteamClient.Settings.SetInGameOverlayShowFPSCorner(0); }, uninstall: () => { + console.log('uninstalling HideFPSCounter Tweak. Restoring FPS Counter corner: ', backup.originalFpsCorner); SteamClient.Settings.SetInGameOverlayShowFPSCorner((backup.originalFpsCorner ?? 0) as 0 | 1 | 2 | 3 | 4); + setTimeout(() => { + // steam might not actually register the setting?! Try again like 10 seconds later... ¯\_(ツ)_/¯ + SteamClient.Settings.SetInGameOverlayShowFPSCorner((backup.originalFpsCorner ?? 0) as 0 | 1 | 2 | 3 | 4); + }, 10 * 1000); } }); diff --git a/SteamTweaks/src/common/util/util.ts b/SteamTweaks/src/common/util/util.ts new file mode 100644 index 0000000..ac46143 --- /dev/null +++ b/SteamTweaks/src/common/util/util.ts @@ -0,0 +1,12 @@ +export const fetchWithTimeout = async (input: RequestInfo | URL, init?: RequestInit & { timeout: number }) => { + const { timeout = 8000 } = init || {}; + + const controller = new AbortController(); + const id = setTimeout(() => controller.abort(), timeout); + const response = await fetch(input, { + ...(init ||{}), + signal: controller.signal + }); + clearTimeout(id); + return response; + } \ No newline at end of file From bf8caca40e7b6ed538c463337940605e97a1cbd9 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Fri, 3 Feb 2023 20:28:50 +0100 Subject: [PATCH 39/65] Update build and bundle scripts --- SteamTweaks/package-lock.json | 33 +++++++++++++++++++++++++-------- SteamTweaks/package.json | 9 ++++++++- build.ps1 | 6 ++++++ bundle-zip.ps1 | 1 + 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/SteamTweaks/package-lock.json b/SteamTweaks/package-lock.json index e8233e5..fe56501 100644 --- a/SteamTweaks/package-lock.json +++ b/SteamTweaks/package-lock.json @@ -8,6 +8,9 @@ "name": "glossi_steamtweaks", "version": "0.0.0", "license": "Apache-2.0", + "dependencies": { + "rimraf": "^4.1.2" + }, "devDependencies": { "@rollup/plugin-typescript": "^11.0.0", "@typescript-eslint/eslint-plugin": "^5.49.0", @@ -1205,6 +1208,21 @@ "node": "^10.12.0 || >=12.0.0" } }, + "node_modules/flat-cache/node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/flatted": { "version": "3.2.7", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", @@ -2222,15 +2240,14 @@ } }, "node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-4.1.2.tgz", + "integrity": "sha512-BlIbgFryTbw3Dz6hyoWFhKk+unCcHMSkZGrTFVAx2WmttdBSonsdtRlwiuTbDqTKr+UlXIUqJVS4QT5tUzGENQ==", "bin": { - "rimraf": "bin.js" + "rimraf": "dist/cjs/src/bin.js" + }, + "engines": { + "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" diff --git a/SteamTweaks/package.json b/SteamTweaks/package.json index 2515d70..65a4b72 100644 --- a/SteamTweaks/package.json +++ b/SteamTweaks/package.json @@ -2,7 +2,11 @@ "name": "glossi_steamtweaks", "version": "0.0.0", "type": "module", - "scripts": {}, + "scripts": { + "clean": "npx rimraf dist .rollup.tscache tsconfig.tsbuildinfo ", + "build": "npx rollup -c rollup.config.js", + "build:clean": "npm run clean && npm run build" + }, "author": "Peter Repukat - FlatspotSoftware", "license": "Apache-2.0", "devDependencies": { @@ -15,5 +19,8 @@ "eslint-plugin-prefer-arrow": "^1.2.3", "rollup": "^3.12.0", "typescript": "^4.9.4" + }, + "dependencies": { + "rimraf": "^4.1.2" } } diff --git a/build.ps1 b/build.ps1 index 0dcaa40..60f181b 100644 --- a/build.ps1 +++ b/build.ps1 @@ -5,6 +5,10 @@ Remove-Item -Recurse -Force "x64\Release" $env:_CL_="/MD" msbuild.exe GlosSI.sln /t:Build /p:Configuration=Release /p:Platform=x64 +cd ./SteamTweaks +npm i +npm run build +cd .. cd ./x64/Release/ @@ -26,6 +30,8 @@ Copy-Item "..\..\vc_redist.x64.exe" -Destination "." Copy-Item "..\..\LICENSE" -Destination "./LICENSE" Copy-Item "..\..\QT_License" -Destination "./QT_License" Copy-Item "..\..\THIRD_PARTY_LICENSES.txt" -Destination "./THIRD_PARTY_LICENSES.txt" +Copy-Item "..\..\SteamTweaks\dist" -Destination "./SteamTweaks" -Recurse + #7z a GlosSI-snapshot.zip * diff --git a/bundle-zip.ps1 b/bundle-zip.ps1 index dffea33..ba57349 100644 --- a/bundle-zip.ps1 +++ b/bundle-zip.ps1 @@ -19,6 +19,7 @@ Copy-Item "..\..\LICENSE" -Destination "./LICENSE" Copy-Item "..\..\QT_License" -Destination "./QT_License" Copy-Item "..\..\THIRD_PARTY_LICENSES.txt" -Destination "./THIRD_PARTY_LICENSES.txt" Copy-Item "..\..\steamgrid.exe" -Destination "./steamgrid.exe" +Copy-Item "..\..\SteamTweaks\dist" -Destination "./SteamTweaks" -Recurse Copy-Item "C:\Qt\Tools\OpenSSL\Win_x64\bin\libcrypto-1_1-x64.dll" -Destination "./libcrypto-1_1-x64.dll" Copy-Item "C:\Qt\Tools\OpenSSL\Win_x64\bin\libssl-1_1-x64.dll" -Destination "./libssl-1_1-x64.dll" From 89c90a2ea069502f341e771649028bd1ee5407a2 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sat, 4 Feb 2023 13:42:56 +0100 Subject: [PATCH 40/65] update appveyor version helper --- version_appveyor.ps1 | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version_appveyor.ps1 b/version_appveyor.ps1 index d925f2c..ad08ad9 100644 --- a/version_appveyor.ps1 +++ b/version_appveyor.ps1 @@ -1,4 +1,4 @@ -$tag = git describe --tags --always +$tag = git describe --tags --always $(git rev-list --all --max-count=1) if (-Not ($tag -match ".+\..+\..+\..+")) { $tag = "0.0.0." + $tag } From 34b3f5322d3a1e3ef419359c65df89509fd7e2e6 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sat, 4 Feb 2023 15:25:49 +0100 Subject: [PATCH 41/65] GlosSIConfig: enable Steam CEF debugging by default --- GlosSIConfig/UIModel.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index 7fd66ba..219cff9 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -65,6 +65,10 @@ UIModel::UIModel() : QObject(nullptr) font.setPointSize(11); font.setFamily("Roboto"); QGuiApplication::setFont(font); + + std::ofstream{getSteamPath() / ".cef-enable-remote-debugging"}; + + } void UIModel::readTargetConfigs() From 9c98b28c72904277f05324ceb0561b1a4e6b549c Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sat, 4 Feb 2023 15:26:12 +0100 Subject: [PATCH 42/65] GlosSITarget: add disabled CEF debugging alert --- GlosSITarget/SteamTarget.cpp | 58 +++++++++++++++++++++++++++++++----- GlosSITarget/SteamTarget.h | 3 ++ 2 files changed, 54 insertions(+), 7 deletions(-) diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index a145465..38d0e05 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -50,6 +50,7 @@ SteamTarget::SteamTarget() int SteamTarget::run() { + run_ = true; auto closeBPM = false; auto closeBPMTimer = sf::Clock{}; if (!SteamOverlayDetector::IsSteamInjected()) { @@ -65,6 +66,43 @@ int SteamTarget::run() std::vector> end_frame_callbacks; + if (!CEFInject::CEFDebugAvailable()) { + auto overlay_id = std::make_shared(-1); + *overlay_id = Overlay::AddOverlayElem( + [this, overlay_id, &end_frame_callbacks](bool window_has_focus, ImGuiID dockspace_id) { + can_fully_initialize_ = false; + ImGui::Begin("GlosSI - CEF Debug not available"); + ImGui::Text("GlosSI makes use of Steam CEF Debugging for some functionality and plugins."); + ImGui::Text("GlosSI might not work fully without it."); + + if (ImGui::Button("Ignore and continue")) { + can_fully_initialize_ = true; + cef_tweaks_enabled_ = false; + if (*overlay_id != -1) { + end_frame_callbacks.emplace_back([this, overlay_id] { + Overlay::RemoveOverlayElem(*overlay_id); + }); + } + } + + if (ImGui::Button("Enable and restart Steam")) { + + std::ofstream{steam_path_ / ".cef-enable-remote-debugging"}; + system("taskkill.exe /im steam.exe /f"); + Sleep(200); + launcher_.launchApp((steam_path_ / "Steam.exe").wstring()); + + run_ = false; + } + ImGui::Text("GlosSI will close upon restarting Steam"); + + ImGui::End(); + }, + true); + can_fully_initialize_ = false; + cef_tweaks_enabled_ = false; + } + if (!SteamOverlayDetector::IsSteamInjected() && Settings::common.allowGlobalMode && Settings::common.globalModeGameId == L"") { auto overlay_id = std::make_shared(-1); *overlay_id = Overlay::AddOverlayElem( @@ -112,9 +150,7 @@ int SteamTarget::run() true); can_fully_initialize_ = false; } - - run_ = true; - + const auto tray = createTrayMenu(); server_.run(); @@ -136,8 +172,9 @@ int SteamTarget::run() overlayHotkeyWorkaround(); window_.update(); - steam_tweaks.update(frame_time_clock.getElapsedTime().asSeconds()); - + if (cef_tweaks_enabled_ && fully_initialized_) { + steam_tweaks_.update(frame_time_clock.getElapsedTime().asSeconds()); + } // Wait on shutdown; User might get confused if window closes to fast if anything with launchApp get's borked. if (delayed_shutdown_) { @@ -156,7 +193,6 @@ int SteamTarget::run() end_frame_callbacks.clear(); frame_time_clock.restart(); } - steam_tweaks.uninstallTweaks(); tray->exit(); server_.stop(); @@ -166,8 +202,11 @@ int SteamTarget::run() hidhide_.disableHidHide(); #endif launcher_.close(); + if (cef_tweaks_enabled_) { + steam_tweaks_.uninstallTweaks(); + } } - + return 0; } @@ -343,6 +382,11 @@ Application will not function!"); launcher_.launchApp(Settings::launch.launchPath, Settings::launch.launchAppArgs); } keepControllerConfig(true); + + if (cef_tweaks_enabled_) { + steam_tweaks_.setAutoInject(true); + } + fully_initialized_ = true; } diff --git a/GlosSITarget/SteamTarget.h b/GlosSITarget/SteamTarget.h index 70dc70b..17cb12c 100644 --- a/GlosSITarget/SteamTarget.h +++ b/GlosSITarget/SteamTarget.h @@ -29,6 +29,7 @@ limitations under the License. #include #include "AppLauncher.h" +#include "CEFInject.h" #include "Overlay.h" #include "HttpServer.h" @@ -50,6 +51,8 @@ class SteamTarget { std::filesystem::path steam_path_ = util::steam::getSteamPath(); std::wstring steam_user_id_ = util::steam::getSteamUserId(); + CEFInject::SteamTweaks steam_tweaks_; + bool cef_tweaks_enabled_ = true; bool steam_overlay_present_ = false; bool fully_initialized_ = false; bool can_fully_initialize_ = true; From 6c0a6145ecb3b97d0ea87eefccad6ce142a012b1 Mon Sep 17 00:00:00 2001 From: James Lamine Date: Sun, 5 Feb 2023 20:12:41 -0500 Subject: [PATCH 43/65] Allow launching win32 apps with admin permissions --- GlosSITarget/AppLauncher.cpp | 52 +++++++++++++++++++++++++++++------- 1 file changed, 43 insertions(+), 9 deletions(-) diff --git a/GlosSITarget/AppLauncher.cpp b/GlosSITarget/AppLauncher.cpp index 30f5fc1..b270a69 100644 --- a/GlosSITarget/AppLauncher.cpp +++ b/GlosSITarget/AppLauncher.cpp @@ -24,6 +24,7 @@ limitations under the License. #include #include #include +#include #pragma comment(lib, "Shell32.lib") #endif @@ -300,6 +301,9 @@ void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& a ? L"\"" + native_seps_path + L"\"" : native_seps_path) + L" " + args) ); + + DWORD pid; + spdlog::debug(L"Launching Win32App app \"{}\"; args \"{}\"", native_seps_path, args_cpy); if (CreateProcessW(native_seps_path.data(), args_cpy.empty() ? nullptr : args_cpy.data(), @@ -311,17 +315,47 @@ void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& a nullptr, // launch_dir.empty() ? nullptr : launch_dir.data(), &info, &process_info)) { - // spdlog::info(L"Started Program: \"{}\" in directory: \"{}\"", native_seps_path, launch_dir); - spdlog::info(L"Started Program: \"{}\"; PID: {}", native_seps_path, process_info.dwProcessId); - if (!watchdog) { - pid_mutex_.lock(); - pids_.push_back(process_info.dwProcessId); - pid_mutex_.unlock(); + + pid = process_info.dwProcessId; + } else { + DWORD error_code = GetLastError(); + + if (error_code == ERROR_ELEVATION_REQUIRED) { + spdlog::info("Elevated permissions required. Trying again with elevated permissions"); + + SHELLEXECUTEINFOW shExecInfo = {0}; + shExecInfo.cbSize = sizeof(SHELLEXECUTEINFOW); + shExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS; + shExecInfo.hwnd = NULL; + shExecInfo.lpVerb = L"runas"; + shExecInfo.lpFile = native_seps_path.data(); + shExecInfo.lpParameters = args_cpy.empty() ? nullptr : args_cpy.data(); + shExecInfo.lpDirectory = nullptr; // launch_dir.empty() ? nullptr : launch_dir.data(), + shExecInfo.nShow = SW_SHOW; + shExecInfo.hInstApp = NULL; + + if (ShellExecuteExW(&shExecInfo)) { + pid = GetProcessId(shExecInfo.hProcess); + if (pid == 0u) { + spdlog::error(L"Couldn't get process id after starting program: \"{}\"; Error code {}", native_seps_path, GetLastError()); + } + CloseHandle(shExecInfo.hProcess); + } else { + spdlog::error(L"Couldn't start program with elevated permissions: \"{}\"; Error code {}", native_seps_path, GetLastError()); + return; + } + } else { + spdlog::error(L"Could't start program: \"{}\"; Error code: {}", native_seps_path, error_code); + return; } } - else { - // spdlog::error(L"Couldn't start program: \"{}\" in directory: \"{}\"", native_seps_path, launch_dir); - spdlog::error(L"Couldn't start program: \"{}\"", native_seps_path); + + spdlog::info(L"Started Program: \"{}\"; PID: {}", native_seps_path, pid); + + if (!watchdog) { + pid_mutex_.lock(); + pids_.push_back(pid); + pid_mutex_.unlock(); } } From 0745e3684d676a751ed85edbf7a8bca2773c7727 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 6 Feb 2023 00:26:54 +0100 Subject: [PATCH 44/65] AppLauncher: Fix potential crash --- GlosSITarget/AppLauncher.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/GlosSITarget/AppLauncher.cpp b/GlosSITarget/AppLauncher.cpp index b270a69..fa9137a 100644 --- a/GlosSITarget/AppLauncher.cpp +++ b/GlosSITarget/AppLauncher.cpp @@ -251,9 +251,11 @@ void AppLauncher::getProcessHwnds() IPropertyStore* propStore; SHGetPropertyStoreForWindow(curr_wnd, IID_IPropertyStore, reinterpret_cast(&propStore)); PROPVARIANT prop; - propStore->GetValue(PKEY_AppUserModel_ID, &prop); - if (prop.bstrVal != nullptr && std::wstring(prop.bstrVal) == launched_uwp_path_) { - process_hwnds_.push_back(curr_wnd); + if (propStore != nullptr) { + propStore->GetValue(PKEY_AppUserModel_ID, &prop); + if (prop.bstrVal != nullptr && std::wstring(prop.bstrVal) == launched_uwp_path_) { + process_hwnds_.push_back(curr_wnd); + } } } while (curr_wnd != nullptr); } From 50a4ea814d985431cd2566298374d99bdb209d33 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 6 Feb 2023 23:13:01 +0100 Subject: [PATCH 45/65] Add auto controller count detection and make it default Note: Existing users have to manually adjust this setting --- GlosSIConfig/UIModel.cpp | 2 +- GlosSIConfig/qml/AdvancedTargetSettings.qml | 15 +++++++++-- GlosSITarget/InputRedirector.cpp | 30 +++++++++++++++++---- GlosSITarget/InputRedirector.h | 1 + common/Settings.h | 2 +- 5 files changed, 41 insertions(+), 9 deletions(-) diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index 219cff9..2ba051e 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -404,7 +404,7 @@ QVariantMap UIModel::getDefaultConf() const QJsonValue::fromVariant(QString::fromStdWString(getSteamUserId(false)))}, {"globalModeGameId", ""}, {"globalModeUseGamepadUI", false}, - {"controller", QJsonObject{{"maxControllers", 1}, {"emulateDS4", false}, {"allowDesktopConfig", false}}}, + {"controller", QJsonObject{{"maxControllers", -1}, {"emulateDS4", false}, {"allowDesktopConfig", false}}}, {"devices", QJsonObject{ {"hideDevices", true}, diff --git a/GlosSIConfig/qml/AdvancedTargetSettings.qml b/GlosSIConfig/qml/AdvancedTargetSettings.qml index c6327cd..1c536af 100644 --- a/GlosSIConfig/qml/AdvancedTargetSettings.qml +++ b/GlosSIConfig/qml/AdvancedTargetSettings.qml @@ -261,8 +261,14 @@ CollapsiblePane { width: 128 editable: true value: shortcutInfo.controller.maxControllers - from: 0 + from: -1 to: 4 + textFromValue: function(value) { + if (value == -1) { + return qsTr("auto"); + } + return Number(value); + } onValueChanged: shortcutInfo.controller.maxControllers = value } RoundButton { @@ -271,7 +277,12 @@ CollapsiblePane { helpInfoDialog.text = qsTr("GlosSI will only provide [NUMBER] of controllers") + "\n" - + qsTr("Required to set to actually connected controller count when using \"real device IDs\" ") + + qsTr("-1 ^= auto-detect") + + "\n" + + qsTr("auto detection only works upon launch") + + "\n" + + "\n" + + qsTr("Required to manuelly set to actually connected controller count when using \"real device IDs\" ") helpInfoDialog.open() } width: 48 diff --git a/GlosSITarget/InputRedirector.cpp b/GlosSITarget/InputRedirector.cpp index 1854861..1631766 100644 --- a/GlosSITarget/InputRedirector.cpp +++ b/GlosSITarget/InputRedirector.cpp @@ -49,6 +49,22 @@ InputRedirector::~InputRedirector() void InputRedirector::run() { run_ = vigem_connected_; + max_controllers_ = Settings::controller.maxControllers; + if (max_controllers_ < 0) { + for (int i = 0; i < XUSER_MAX_COUNT; i++) { + XINPUT_STATE state{}; + if (XInputGetState(i, &state) == ERROR_SUCCESS) { + max_controllers_ = i + 1; + } + } + if (max_controllers_ < 0) { + max_controllers_ = 1; + spdlog::error("Failed to auto detect controller count. Defaulting to 1"); + } + else { + spdlog::info("Auto detected {} controllers", max_controllers_); + } + } controller_thread_ = std::thread(&InputRedirector::runLoop, this); #ifdef _WIN32 Overlay::AddOverlayElem([this](bool window_has_focus, ImGuiID dockspace_id) { @@ -58,13 +74,17 @@ void InputRedirector::run() ImGui::Text("Max. controller count"); ImGui::SameLine(); ImGui::InputInt("##Max. controller count", &countcopy, 1, 1); + ImGui::Text("-1 = Auto-detection (auto-detection only works on launch"); if (countcopy > XUSER_MAX_COUNT) { countcopy = XUSER_MAX_COUNT; } - if (countcopy < 0) { - countcopy = 0; + if (countcopy < -1) { + countcopy = -1; } Settings::controller.maxControllers = countcopy; + if (Settings::controller.maxControllers > -1) { + max_controllers_ = countcopy; + } if (ImGui::Checkbox("Emulate DS4 (instead of Xbox360 controller)", &Settings::controller.emulateDS4)) { controller_settings_changed_ = true; @@ -131,12 +151,12 @@ void InputRedirector::runLoop() unplugVigemPad(i); } } - if (Settings::controller.maxControllers < XUSER_MAX_COUNT) { - for (int i = Settings::controller.maxControllers; i < XUSER_MAX_COUNT; i++) { + if (max_controllers_ < XUSER_MAX_COUNT) { + for (int i = max_controllers_; i < XUSER_MAX_COUNT; i++) { unplugVigemPad(i); } } - for (int i = 0; i < XUSER_MAX_COUNT && i < Settings::controller.maxControllers; i++) { + for (int i = 0; i < XUSER_MAX_COUNT && i < max_controllers_; i++) { XINPUT_STATE state{}; if (XInputGetState(i, &state) == ERROR_SUCCESS) { if (vt_pad_[i] != nullptr) { diff --git a/GlosSITarget/InputRedirector.h b/GlosSITarget/InputRedirector.h index cc456a9..94b1f10 100644 --- a/GlosSITarget/InputRedirector.h +++ b/GlosSITarget/InputRedirector.h @@ -35,6 +35,7 @@ class InputRedirector { private: void runLoop(); + int max_controllers_ = -1; static constexpr int start_delay_ms_ = 2000; bool run_ = false; int overlay_elem_id_ = -1; diff --git a/common/Settings.h b/common/Settings.h index ece94ac..2d83b58 100644 --- a/common/Settings.h +++ b/common/Settings.h @@ -65,7 +65,7 @@ namespace Settings inline struct Controller { - int maxControllers = 1; + int maxControllers = -1; bool allowDesktopConfig = false; bool emulateDS4 = false; } controller; From ffdb227d2bf0285858386cbe2427c318293747b5 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Wed, 8 Feb 2023 23:03:03 +0100 Subject: [PATCH 46/65] GlosSITweaks: make linter happy --- SteamTweaks/{.eslintrc.js => .eslintrc.cjs} | 0 SteamTweaks/src/GlosSITweaks.ts | 34 ++++++++++++------- .../Overlay/SharedContext/HideFPSCounter.ts | 9 ++--- SteamTweaks/src/common/tweakApi.ts | 4 +-- SteamTweaks/src/common/util/util.ts | 8 ++--- 5 files changed, 33 insertions(+), 22 deletions(-) rename SteamTweaks/{.eslintrc.js => .eslintrc.cjs} (100%) diff --git a/SteamTweaks/.eslintrc.js b/SteamTweaks/.eslintrc.cjs similarity index 100% rename from SteamTweaks/.eslintrc.js rename to SteamTweaks/.eslintrc.cjs diff --git a/SteamTweaks/src/GlosSITweaks.ts b/SteamTweaks/src/GlosSITweaks.ts index 29e5c6c..ea0cf35 100644 --- a/SteamTweaks/src/GlosSITweaks.ts +++ b/SteamTweaks/src/GlosSITweaks.ts @@ -1,9 +1,15 @@ import type { SteamConfig } from './common/util/types'; import { fetchWithTimeout } from './common/util/util'; - +import type { GlosSISettings } from './@types/GlosSISettings'; class SteamTargetApi { + public getGlosSIActive() { + return fetchWithTimeout('http://localhost:8756/', { timeout: 500 }) + .then( + () => true + ).catch(() => false); + } public getSteamSettings(): Promise { return fetch('http://localhost:8756/steam_settings') .then( @@ -13,12 +19,15 @@ class SteamTargetApi { ); } - public getGlosSIActive() { - return fetchWithTimeout('http://localhost:8756/', { timeout: 10000 }) + public getGlosSISettings() { + return fetch('http://localhost:8756/settings') .then( - () => true - ).catch((e) => false); + (res) => res.json().then( + (json) => json as GlosSISettings + ) + ); } + } @@ -27,18 +36,19 @@ class GlosSIApiCtor { } interface GlosSITweaks { - [tweakName: string]: { readonly install: () => unknown; readonly uninstall?: () => void } + [tweakName: string]: { readonly install: () => unknown; readonly uninstall?: () => void }; } declare global { interface Window { - GlosSITweaks: GlosSITweaks + GlosSITweaks: GlosSITweaks; GlosSIApi: InstanceType; } // eslint-disable-next-line const GlosSIApi: InstanceType; - const GlosSITweaks: GlosSITweaks + // eslint-disable-next-line + const GlosSITweaks: GlosSITweaks; } @@ -51,7 +61,7 @@ const installGlosSIApi = () => { }, uninstall: () => { Object.entries(window.GlosSITweaks) - .filter(([tweakName, obj]) => (tweakName !== 'GlosSI')) + .filter(([tweakName]) => (tweakName !== 'GlosSI')) .forEach(([, obj]) => obj.uninstall?.()); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -66,15 +76,15 @@ const installGlosSIApi = () => { const glossiCheckInterval = setInterval(() => { if (window.GlosSIApi) { - window.GlosSIApi.SteamTarget.getGlosSIActive().then((active) => { + void window.GlosSIApi.SteamTarget.getGlosSIActive().then((active) => { if (!active) { window?.GlosSITweaks?.GlosSI?.uninstall?.(); } }); return; } - clearTimeout(glossiCheckInterval) - }, 5000) + clearTimeout(glossiCheckInterval); + }, 5000); }; diff --git a/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts b/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts index 7bbf1bb..1b1b404 100644 --- a/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts +++ b/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts @@ -1,6 +1,5 @@ - -import type { SteamConfig } from "../../../common/util/types"; -import { initTweak } from "../../../common/tweakApi"; +import type { SteamConfig } from '../../../common/util/types'; +import { initTweak } from '../../../common/tweakApi'; const backup: { originalFpsCorner?: number } = {}; @@ -20,4 +19,6 @@ initTweak('HideFPSCounter', { SteamClient.Settings.SetInGameOverlayShowFPSCorner((backup.originalFpsCorner ?? 0) as 0 | 1 | 2 | 3 | 4); }, 10 * 1000); } -}); +}).then(() => { + console.log('HideFPSCounter installed'); +}).catch((e) => console.error('HideFPSCounter failed to install', e)); diff --git a/SteamTweaks/src/common/tweakApi.ts b/SteamTweaks/src/common/tweakApi.ts index 7b3ad99..cac7880 100644 --- a/SteamTweaks/src/common/tweakApi.ts +++ b/SteamTweaks/src/common/tweakApi.ts @@ -2,9 +2,9 @@ export const initTweak = (name: string, tweakMain: (() => T)|{ install: () => T; uninstall: () => void; -}, force = false): T|Error => { +}, force = false): T => { if (!force && window.GlosSITweaks[name]) { - return new Error(`Tweak ${name} is already installed!`); + throw new Error(`Tweak ${name} is already installed!`); } if (typeof tweakMain === 'object') { diff --git a/SteamTweaks/src/common/util/util.ts b/SteamTweaks/src/common/util/util.ts index ac46143..be6a91c 100644 --- a/SteamTweaks/src/common/util/util.ts +++ b/SteamTweaks/src/common/util/util.ts @@ -1,12 +1,12 @@ export const fetchWithTimeout = async (input: RequestInfo | URL, init?: RequestInit & { timeout: number }) => { const { timeout = 8000 } = init || {}; - + const controller = new AbortController(); const id = setTimeout(() => controller.abort(), timeout); const response = await fetch(input, { - ...(init ||{}), - signal: controller.signal + ...(init ||{}), + signal: controller.signal }); clearTimeout(id); return response; - } \ No newline at end of file +}; From b9cb0414a7dabefd3589c8990365cb2fa29dcfbc Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Wed, 8 Feb 2023 23:17:15 +0100 Subject: [PATCH 47/65] CefInject: Steam table title matching by substring instead of startsWith --- CEFInjectLib/CEFInject.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp index edde29d..ff37a86 100644 --- a/CEFInjectLib/CEFInject.cpp +++ b/CEFInjectLib/CEFInject.cpp @@ -169,7 +169,7 @@ namespace CEFInject if (res->status == 200) { const auto json = nlohmann::json::parse(res->body); for (const auto& tab : json) { - if (tab["title"].get().starts_with(tabname)) { + if (tab["title"].get().find(tabname) != std::string::npos) { return InjectJs(tab["title"].get(), tab["webSocketDebuggerUrl"].get(), js, port); } } @@ -213,7 +213,7 @@ namespace CEFInject return [&info](const nlohmann::json::array_t& tabList) { for (const auto& tab : tabList) { - if (tab["title"].get().starts_with(info.name.data())) { + if (tab["title"].get().find(info.name.data()) != std::string::npos) { return tab; } } @@ -347,7 +347,7 @@ namespace CEFInject const auto dir_name = path.parent_path().filename(); if (path_tab_map_.contains(dir_name.wstring())) { - if (tab["title"].get().starts_with(path_tab_map_.at(dir_name.wstring()))) { + if (tab["title"].get().find(path_tab_map_.at(dir_name.wstring())) != std::string::npos) { InjectJs(tab["title"].get(), tab["webSocketDebuggerUrl"].get(), js); } } From 88856be11a8da8544357cc878757e4d0c91a7922 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Wed, 8 Feb 2023 23:17:49 +0100 Subject: [PATCH 48/65] Add minimize SteamGamepadUI feature (untested) --- CEFInjectLib/CEFInject.h | 1 + GlosSIConfig/UIModel.cpp | 3 +- SteamTweaks/src/@types/GlosSISettings.d.ts | 44 +++++++++++++++++++ SteamTweaks/src/@types/SteamClient.d.ts | 10 +++++ .../GamepadUI/MinimizeSteamGamepadUI.ts | 21 +++++++++ SteamTweaks/src/common/Steam.ts | 8 ++++ common/Settings.h | 2 + 7 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 SteamTweaks/src/@types/GlosSISettings.d.ts create mode 100644 SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts create mode 100644 SteamTweaks/src/common/Steam.ts diff --git a/CEFInjectLib/CEFInject.h b/CEFInjectLib/CEFInject.h index 2fd103d..c712354 100644 --- a/CEFInjectLib/CEFInject.h +++ b/CEFInjectLib/CEFInject.h @@ -76,6 +76,7 @@ namespace CEFInject static inline const std::map path_tab_map_ = { {L"SharedContext", "Steam Shared Context"}, {L"Overlay", "HOW TF GET OVERLAY TAB NAME?"}, // TODO: Figure out how to get the overlay tab name + {L"GamepadUI", "Steam Big Picture Mode"}, }; static constexpr std::string_view steam_shared_ctx_tab_name_ = "Steam Shared Context"; diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index 2ba051e..8343107 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -403,7 +403,8 @@ QVariantMap UIModel::getDefaultConf() const {"steamUserId", QJsonValue::fromVariant(QString::fromStdWString(getSteamUserId(false)))}, {"globalModeGameId", ""}, - {"globalModeUseGamepadUI", false}, + {"globalModeUseGamepadUI", true}, + {"minimizeSteamGamepadUI", true}, {"controller", QJsonObject{{"maxControllers", -1}, {"emulateDS4", false}, {"allowDesktopConfig", false}}}, {"devices", QJsonObject{ diff --git a/SteamTweaks/src/@types/GlosSISettings.d.ts b/SteamTweaks/src/@types/GlosSISettings.d.ts new file mode 100644 index 0000000..b94eea8 --- /dev/null +++ b/SteamTweaks/src/@types/GlosSISettings.d.ts @@ -0,0 +1,44 @@ +export interface GlosSISettings { + controller: { + allowDesktopConfig: boolean; + emulateDS4: boolean; + maxControllers: number; + }; + devices: { + hideDevices: boolean; + realDeviceIds: boolean; + }; + extendedLogging: boolean; + globalModeGameId: string; + globalModeUseGamepadUI: boolean; + icon?: string; + ignoreEGS: boolean; + killEGS: boolean; + launch: { + closeOnExit: boolean; + ignoreLauncher: boolean; + killLauncher: boolean; + launch: boolean; + launchAppArgs?: string; + launchPath?: string; + launcherProcesses: string[]; + waitForChildProcs: boolean; + }; + name?: string; + snapshotNotify: boolean; + standaloneModeGameId: string; + standaloneUseGamepadUI: boolean; + minimizeSteamGamepadUI: boolean; + steamPath: string; + steamUserId: string; + steamgridApiKey: string; + version: number; + window: { + disableGlosSIOverlay: boolean; + disableOverlay: boolean; + hideAltTab: boolean; + maxFps?: number; + scale?: number; + windowMode: boolean; + }; +} diff --git a/SteamTweaks/src/@types/SteamClient.d.ts b/SteamTweaks/src/@types/SteamClient.d.ts index c2072e5..a812952 100644 --- a/SteamTweaks/src/@types/SteamClient.d.ts +++ b/SteamTweaks/src/@types/SteamClient.d.ts @@ -3,8 +3,18 @@ export interface SteamClient { SetInGameOverlayShowFPSCorner: (value: 0|1|2|3|4) => void; SetInGameOverlayShowFPSContrast: (value: boolean) => void; }; + UI: { + GetUiMode: () => Promise; + SetUiMode: (mode: SteamUiMode) => void; + }; + Window: { + Minimize(); + HideWindow(); + }; } +export type FullSteamClient = Required; + declare global { interface Window { SteamClient: SteamClient; diff --git a/SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts b/SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts new file mode 100644 index 0000000..8245818 --- /dev/null +++ b/SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts @@ -0,0 +1,21 @@ +import { SteamUiMode } from '../../common/Steam'; +import { initTweak } from '../../common/tweakApi'; + + +initTweak('MinimizeSteamGamepadUI', async () => { + + const [isGamepadUI, minimizeGPUI] = await Promise.all([ + (async () => (await SteamClient.UI.GetUiMode()) === SteamUiMode.GamepadUI)(), + (async () => (await GlosSIApi.SteamTarget.getGlosSISettings()).minimizeSteamGamepadUI)() + ]); + if (isGamepadUI && minimizeGPUI) { + SteamClient.Window.Minimize(); + return true; + } + if (!isGamepadUI && minimizeGPUI) { + console.warn('MinimizeSteamGamepadUI is enabled but Steam is not in GamepadUI mode'); + } + return false; +}).then((minimized: boolean) => { + console.log('MinimizeSteamGamepadUI installed; Minimized GamepadUI:', minimized); +}).catch((e) => console.error('MinimizeSteamGamepadUI failed to install', e)); diff --git a/SteamTweaks/src/common/Steam.ts b/SteamTweaks/src/common/Steam.ts new file mode 100644 index 0000000..07be9e2 --- /dev/null +++ b/SteamTweaks/src/common/Steam.ts @@ -0,0 +1,8 @@ +// eslint-disable-next-line no-shadow +export enum SteamUiMode { + Desktop = 0, + Unknown1 = 1, + Unknown2 = 2, + Unknown3 = 3, + GamepadUI = 4, +} diff --git a/common/Settings.h b/common/Settings.h index 2d83b58..6413dcd 100644 --- a/common/Settings.h +++ b/common/Settings.h @@ -83,6 +83,7 @@ namespace Settings std::wstring globalModeGameId; /* = L"12605636929694728192"; */ bool globalModeUseGamepadUI = false; bool allowGlobalMode = true; + bool minimizeSteam = true; } common; inline const std::map> cmd_args = { @@ -235,6 +236,7 @@ namespace Settings safeParseValue(json, "globalModeGameId", common.globalModeGameId); safeParseValue(json, "globalModeUseGamepadUI", common.globalModeUseGamepadUI); + safeParseValue(json, "minimizeSteam", common.minimizeSteam); } catch (const nlohmann::json::exception &e) { From fe21af67878fd8eec4bd4da1427ab2f793f7e0e9 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sat, 25 Feb 2023 11:50:34 +0100 Subject: [PATCH 49/65] Fix multiple WSASTartup/Cleanup calls (Improve Tweak inject perf) --- CEFInjectLib/CEFInject.cpp | 59 +++++++++++++++++++++----------------- CEFInjectLib/CEFInject.h | 13 +++++++++ 2 files changed, 46 insertions(+), 26 deletions(-) diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp index ff37a86..ccb585c 100644 --- a/CEFInjectLib/CEFInject.cpp +++ b/CEFInjectLib/CEFInject.cpp @@ -90,16 +90,6 @@ namespace CEFInject nlohmann::basic_json<> InjectJs(std::string_view tab_name, std::string_view debug_url, std::wstring_view js, uint16_t port) { -#ifdef _WIN32 - INT rc; - WSADATA wsaData; - - rc = WSAStartup(MAKEWORD(2, 2), &wsaData); - if (rc) { - printf("WSAStartup Failed.\n"); - return nullptr; - } -#endif std::shared_ptr ws{ easywsclient::WebSocket::from_url(debug_url.data()) @@ -145,20 +135,15 @@ namespace CEFInject return res; } } - } catch (...) { + } + catch (...) { spdlog::error( "CEFInject: Error injecting JS into tab: {}, {}", std::string(tab_name.data()), std::string(debug_url.data())); } -#ifdef _WIN32 - WSACleanup(); -#endif return res; } -#ifdef _WIN32 - WSACleanup(); -#endif return nullptr; } @@ -178,6 +163,28 @@ namespace CEFInject return nullptr; } + WSAStartupWrap::WSAStartupWrap() + { +#ifdef _WIN32 + WSADATA wsa_data; + if (const INT rc = WSAStartup(MAKEWORD(2, 2), &wsa_data)) { + spdlog::error("WSAStartup Failed."); + return; + } + wsa_startup_succeeded_ = true; +#endif + } + + WSAStartupWrap::~WSAStartupWrap() + { +#ifdef _WIN32 + if (wsa_startup_succeeded_) + { + WSACleanup(); + } +#endif + } + bool SteamTweaks::injectGlosSITweaks(std::string_view tab_name, uint16_t port) { @@ -292,7 +299,7 @@ namespace CEFInject for (auto& f : futures) { - if (f.valid()){ + if (f.valid()) { f.wait(); } } @@ -339,19 +346,19 @@ namespace CEFInject } glossi_tweaks_injected_map_[tab["id"].get()] = true; - futures.push_back(std::async([this, &tab]() + futures.push_back(std::async([this, &tab]() { InjectJs(tab["title"].get(), tab["webSocketDebuggerUrl"].get(), glossi_tweaks_js_); - for (auto& [path, js] : js_tweaks_cache_) { - const auto dir_name = path.parent_path().filename(); + for (auto& [path, js] : js_tweaks_cache_) { + const auto dir_name = path.parent_path().filename(); - if (path_tab_map_.contains(dir_name.wstring())) { - if (tab["title"].get().find(path_tab_map_.at(dir_name.wstring())) != std::string::npos) { - InjectJs(tab["title"].get(), tab["webSocketDebuggerUrl"].get(), js); - } - } + if (path_tab_map_.contains(dir_name.wstring())) { + if (tab["title"].get().find(path_tab_map_.at(dir_name.wstring())) != std::string::npos) { + InjectJs(tab["title"].get(), tab["webSocketDebuggerUrl"].get(), js); } + } + } })); } for (auto& f : futures) diff --git a/CEFInjectLib/CEFInject.h b/CEFInjectLib/CEFInject.h index c712354..356ed1c 100644 --- a/CEFInjectLib/CEFInject.h +++ b/CEFInjectLib/CEFInject.h @@ -36,6 +36,15 @@ namespace CEFInject nlohmann::basic_json<> InjectJs(std::string_view tab_name, std::string_view debug_url, std::wstring_view js, uint16_t port = internal::port_); nlohmann::basic_json<> InjectJsByName(std::wstring_view tabname, std::wstring_view js, uint16_t port = internal::port_); + class WSAStartupWrap + { + public: + explicit WSAStartupWrap(); + ~WSAStartupWrap(); + private: + bool wsa_startup_succeeded_ = false; + }; + class SteamTweaks { public: @@ -88,4 +97,8 @@ namespace CEFInject )"; }; + namespace internal { + static WSAStartupWrap wsa_startup_wrap{}; + } + } \ No newline at end of file From fe91680fd1b02d2c457275cbb9c84e5710e1eb39 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sat, 25 Feb 2023 14:51:14 +0100 Subject: [PATCH 50/65] CEFInject: Log found tweak files --- CEFInjectLib/CEFInject.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CEFInjectLib/CEFInject.cpp b/CEFInjectLib/CEFInject.cpp index ccb585c..040d246 100644 --- a/CEFInjectLib/CEFInject.cpp +++ b/CEFInjectLib/CEFInject.cpp @@ -452,6 +452,13 @@ namespace CEFInject }; find_tweak_files(tweaks_path.wstring(), find_tweak_files); + for (const auto& tweak : js_tweaks_cache_ | std::views::keys) { + spdlog::debug( + "CEFInject: Found {} tweak: {}", + builtin ? "builtin" : "user", + tweak.string() + ); + } } } From 8015853d1b5a1fa300a8b0aed967ef8bbd6214ec Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sat, 25 Feb 2023 14:51:35 +0100 Subject: [PATCH 51/65] SteamTarget: Start http server earlier --- GlosSITarget/SteamTarget.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 38d0e05..4437c88 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -61,6 +61,10 @@ int SteamTarget::run() } auto steam_tweaks = CEFInject::SteamTweaks(); steam_tweaks.setAutoInject(true); + + server_.run(); + + if (!overlay_.expired()) overlay_.lock()->setEnabled(false); @@ -152,9 +156,7 @@ int SteamTarget::run() } const auto tray = createTrayMenu(); - - server_.run(); - + bool delayed_full_init_1_frame = false; sf::Clock frame_time_clock; From 545fc6af1c791a019632e98b8793bb4aef0aadb0 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sat, 25 Feb 2023 14:51:59 +0100 Subject: [PATCH 52/65] Settings: Fix minimizeGamepadUI Setting / Json.dump --- common/Settings.h | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/common/Settings.h b/common/Settings.h index 6413dcd..d99d874 100644 --- a/common/Settings.h +++ b/common/Settings.h @@ -83,7 +83,7 @@ namespace Settings std::wstring globalModeGameId; /* = L"12605636929694728192"; */ bool globalModeUseGamepadUI = false; bool allowGlobalMode = true; - bool minimizeSteam = true; + bool minimizeSteamGamepadUI = true; } common; inline const std::map> cmd_args = { @@ -236,7 +236,7 @@ namespace Settings safeParseValue(json, "globalModeGameId", common.globalModeGameId); safeParseValue(json, "globalModeUseGamepadUI", common.globalModeUseGamepadUI); - safeParseValue(json, "minimizeSteam", common.minimizeSteam); + safeParseValue(json, "minimizeSteamGamepadUI", common.minimizeSteamGamepadUI); } catch (const nlohmann::json::exception &e) { @@ -338,6 +338,16 @@ namespace Settings json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig; json["controller"]["emulateDS4"] = controller.emulateDS4; + + json["globalModeGameId"] = common.globalModeGameId;; + json["globalModeUseGamepadUI"] = common.globalModeUseGamepadUI; + json["minimizeSteamGamepadUI"] = common.minimizeSteamGamepadUI; + + // json["steamgridApiKey"] = common.steamgridApiKey; + + json["steamPath"] = common.steamPath; + json["steamUserId"] = common.steamUserId; + json["extendedLogging"] = common.extendedLogging; json["name"] = common.name; json["icon"] = common.icon; From e9a02d046bbaa58e5fd73c1d8fbdafacfa278ada Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sat, 25 Feb 2023 14:52:17 +0100 Subject: [PATCH 53/65] Fix minimizing gamepadUI in global mode --- SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts b/SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts index 8245818..31e0274 100644 --- a/SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts +++ b/SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts @@ -1,11 +1,11 @@ -import { SteamUiMode } from '../../common/Steam'; import { initTweak } from '../../common/tweakApi'; initTweak('MinimizeSteamGamepadUI', async () => { const [isGamepadUI, minimizeGPUI] = await Promise.all([ - (async () => (await SteamClient.UI.GetUiMode()) === SteamUiMode.GamepadUI)(), + // (async () => (await SteamClient.UI.GetUiMode()) === SteamUiMode.GamepadUI)(), + true, // Steam is always GamepadUI if injected into GamepadUI, duh! (async () => (await GlosSIApi.SteamTarget.getGlosSISettings()).minimizeSteamGamepadUI)() ]); if (isGamepadUI && minimizeGPUI) { From 6bebae6c7ba793e1fa327f3364621a80db541025 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Sat, 25 Feb 2023 14:58:24 +0100 Subject: [PATCH 54/65] GlobalMode: inject tweaks early if globalmode && global mode BPM --- GlosSITarget/SteamTarget.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 4437c88..7568919 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -328,6 +328,12 @@ void SteamTarget::init_FuckingRenameMe() steamwindow = FindWindow(L"Steam Big Picture Mode", nullptr); Sleep(50); } + + if (cef_tweaks_enabled_) { + steam_tweaks_.setAutoInject(true); + steam_tweaks_.update(999); + } + Sleep(6000); // DIRTY HACK to wait until BPM (GamepadUI) is initialized // TODO: find way to force BPM even if BPM is not active LoadLibrary((steam_path_ / "GameOverlayRenderer64.dll").wstring().c_str()); From 906e40b75377b826891509b606055b07ef42d407 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 27 Feb 2023 13:12:14 +0100 Subject: [PATCH 55/65] Add controller update rate setting --- GlosSIConfig/UIModel.cpp | 7 ++++++- GlosSITarget/InputRedirector.cpp | 2 +- common/Settings.h | 3 +++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index 8343107..9ee8e6e 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -405,7 +405,12 @@ QVariantMap UIModel::getDefaultConf() const {"globalModeGameId", ""}, {"globalModeUseGamepadUI", true}, {"minimizeSteamGamepadUI", true}, - {"controller", QJsonObject{{"maxControllers", -1}, {"emulateDS4", false}, {"allowDesktopConfig", false}}}, + {"controller", + QJsonObject{{"maxControllers", -1}, + {"emulateDS4", false}, + {"allowDesktopConfig", false}, + {"updateRate", 144} + }}, {"devices", QJsonObject{ {"hideDevices", true}, diff --git a/GlosSITarget/InputRedirector.cpp b/GlosSITarget/InputRedirector.cpp index 1631766..b0e8dd8 100644 --- a/GlosSITarget/InputRedirector.cpp +++ b/GlosSITarget/InputRedirector.cpp @@ -266,7 +266,7 @@ void InputRedirector::runLoop() unplugVigemPad(i); } } - sf::sleep(sf::milliseconds(4)); + Sleep(static_cast(1000.f / Settings::controller.updateRate)); #endif } diff --git a/common/Settings.h b/common/Settings.h index d99d874..adada6b 100644 --- a/common/Settings.h +++ b/common/Settings.h @@ -68,6 +68,7 @@ namespace Settings int maxControllers = -1; bool allowDesktopConfig = false; bool emulateDS4 = false; + unsigned int updateRate = 144; } controller; inline struct Common @@ -225,6 +226,7 @@ namespace Settings safeParseValue(controllerConf, "maxControllers", controller.maxControllers); safeParseValue(controllerConf, "allowDesktopConfig", controller.allowDesktopConfig); safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4); + safeParseValue(controllerConf, "updateRate", controller.updateRate); } safeParseValue(json, "extendedLogging", common.extendedLogging); safeParseValue(json, "name", common.name); @@ -337,6 +339,7 @@ namespace Settings json["controller"]["maxControllers"] = controller.maxControllers; json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig; json["controller"]["emulateDS4"] = controller.emulateDS4; + json["controller"]["updateRate"] = controller.updateRate; json["globalModeGameId"] = common.globalModeGameId;; From 2ddb3f1bb06529db8def66b5510d0c1a32cf3df4 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 27 Feb 2023 13:12:42 +0100 Subject: [PATCH 56/65] SteamTweaks: add build:copy script --- SteamTweaks/package-lock.json | 268 ++++++++++++++++++++++++++++++++++ SteamTweaks/package.json | 8 +- 2 files changed, 273 insertions(+), 3 deletions(-) diff --git a/SteamTweaks/package-lock.json b/SteamTweaks/package-lock.json index fe56501..a490e23 100644 --- a/SteamTweaks/package-lock.json +++ b/SteamTweaks/package-lock.json @@ -15,6 +15,7 @@ "@rollup/plugin-typescript": "^11.0.0", "@typescript-eslint/eslint-plugin": "^5.49.0", "@typescript-eslint/parser": "^5.49.0", + "copyfiles": "^2.4.1", "eslint": "^8.33.0", "eslint-plugin-import": "^2.27.5", "eslint-plugin-no-null": "^1.0.2", @@ -604,6 +605,17 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -628,6 +640,31 @@ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, + "node_modules/copyfiles": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/copyfiles/-/copyfiles-2.4.1.tgz", + "integrity": "sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==", + "dev": true, + "dependencies": { + "glob": "^7.0.5", + "minimatch": "^3.0.3", + "mkdirp": "^1.0.4", + "noms": "0.0.0", + "through2": "^2.0.1", + "untildify": "^4.0.0", + "yargs": "^16.1.0" + }, + "bin": { + "copyfiles": "copyfiles", + "copyup": "copyfiles" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -705,6 +742,12 @@ "node": ">=6.0.0" } }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, "node_modules/es-abstract": { "version": "1.21.1", "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.21.1.tgz", @@ -792,6 +835,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "dev": true, + "engines": { + "node": ">=6" + } + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -1291,6 +1343,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, "node_modules/get-intrinsic": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.0.tgz", @@ -1656,6 +1717,15 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -1802,6 +1872,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", + "dev": true + }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -1943,6 +2019,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -1961,6 +2049,16 @@ "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", "dev": true }, + "node_modules/noms": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/noms/-/noms-0.0.0.tgz", + "integrity": "sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==", + "dev": true, + "dependencies": { + "inherits": "^2.0.1", + "readable-stream": "~1.0.31" + } + }, "node_modules/object-inspect": { "version": "1.12.3", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.3.tgz", @@ -2145,6 +2243,12 @@ "node": ">= 0.8.0" } }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", @@ -2174,6 +2278,18 @@ } ] }, + "node_modules/readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha512-ok1qVCJuRkNmvebYikljxJA/UEsKwLl2nI1OmaqAu4/UE+h0wKCHok4XkL/gvi39OacXvw59RJUOFUkDib2rHg==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, "node_modules/regexp.prototype.flags": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.4.3.tgz", @@ -2203,6 +2319,15 @@ "url": "https://github.com/sponsors/mysticatea" } }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -2292,6 +2417,12 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, "node_modules/safe-regex-test": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", @@ -2365,6 +2496,26 @@ "node": ">=8" } }, + "node_modules/string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", + "dev": true + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/string.prototype.trimend": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz", @@ -2456,6 +2607,46 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "node_modules/through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "dev": true, + "dependencies": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + } + }, + "node_modules/through2/node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/through2/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/through2/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -2567,6 +2758,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/untildify": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/untildify/-/untildify-4.0.0.tgz", + "integrity": "sha512-KK8xQ1mkzZeg9inewmFVDNkg3l5LUhoq9kN6iWYB/CC9YMG8HA+c1Q8HwDe6dEX7kErrEVNVBO3fWsVq5iDgtw==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", @@ -2576,6 +2776,12 @@ "punycode": "^2.1.0" } }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2636,18 +2842,80 @@ "node": ">=0.10.0" } }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", + "dev": true, + "engines": { + "node": ">=0.4" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "engines": { + "node": ">=10" + } + }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/SteamTweaks/package.json b/SteamTweaks/package.json index 65a4b72..cc16527 100644 --- a/SteamTweaks/package.json +++ b/SteamTweaks/package.json @@ -5,7 +5,8 @@ "scripts": { "clean": "npx rimraf dist .rollup.tscache tsconfig.tsbuildinfo ", "build": "npx rollup -c rollup.config.js", - "build:clean": "npm run clean && npm run build" + "build:clean": "npm run clean && npm run build", + "build:copy": "npx rimraf ../x64/Debug/SteamTweaks && npm run build && cd dist && npx copyfiles -a -V ./**/* ../../x64/Debug/SteamTweaks" }, "author": "Peter Repukat - FlatspotSoftware", "license": "Apache-2.0", @@ -13,14 +14,15 @@ "@rollup/plugin-typescript": "^11.0.0", "@typescript-eslint/eslint-plugin": "^5.49.0", "@typescript-eslint/parser": "^5.49.0", + "copyfiles": "^2.4.1", "eslint": "^8.33.0", "eslint-plugin-import": "^2.27.5", "eslint-plugin-no-null": "^1.0.2", "eslint-plugin-prefer-arrow": "^1.2.3", "rollup": "^3.12.0", - "typescript": "^4.9.4" + "typescript": "^4.9.4", + "rimraf": "^4.1.2" }, "dependencies": { - "rimraf": "^4.1.2" } } From 77c7b639ab163da3b7f173f14a2b9f719ab69c52 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 27 Feb 2023 13:57:30 +0100 Subject: [PATCH 57/65] Add setting for making window opaque when SteamOverlay is open --- GlosSIConfig/UIModel.cpp | 3 +- GlosSITarget/SteamTarget.cpp | 6 ++++ GlosSITarget/TargetWindow.cpp | 52 ++++++++++++++++++++++++----------- GlosSITarget/TargetWindow.h | 1 + common/Settings.h | 3 ++ 5 files changed, 48 insertions(+), 17 deletions(-) diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index 9ee8e6e..86a6f8e 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -434,7 +434,8 @@ QVariantMap UIModel::getDefaultConf() const {"maxFps", QJsonValue::Null}, {"scale", QJsonValue::Null}, {"windowMode", false}, - {"disableGlosSIOverlay", false} + {"disableGlosSIOverlay", false}, + {"opaqueSteamOverlay", false} }}, }; diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 7568919..56253f0 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -217,11 +217,17 @@ void SteamTarget::onOverlayChanged(bool overlay_open) if (overlay_open) { focusWindow(target_window_handle_); window_.setClickThrough(!overlay_open); + if (!Settings::window.windowMode && Settings::window.opaqueSteamOverlay) { + window_.setTransparent(false); + } } else { if (!(overlay_.expired() ? false : overlay_.lock()->isEnabled())) { window_.setClickThrough(!overlay_open); focusWindow(last_foreground_window_); + if (!Settings::window.windowMode && Settings::window.opaqueSteamOverlay) { + window_.setTransparent(true); + } } } if (!overlay_trigger_flag_) { diff --git a/GlosSITarget/TargetWindow.cpp b/GlosSITarget/TargetWindow.cpp index 41e296c..b79d56b 100644 --- a/GlosSITarget/TargetWindow.cpp +++ b/GlosSITarget/TargetWindow.cpp @@ -149,6 +149,41 @@ void TargetWindow::setClickThrough(bool click_through) #endif } +void TargetWindow::setTransparent(bool transparent) const +{ + HWND hwnd = window_.getSystemHandle(); + + if (transparent) { + // if (windowed_) { + // DWM_BLURBEHIND bb{.dwFlags = DWM_BB_ENABLE, .fEnable = true, .hRgnBlur = nullptr}; + // DwmEnableBlurBehindWindow(hwnd, &bb); + // } // semi-transparent in window mode, but deprecated api + // On Linux the window will (should) automagically be semi-transparent + + // transparent windows window... + auto style = GetWindowLong(hwnd, GWL_STYLE); + style &= ~WS_OVERLAPPED; + style |= WS_POPUP; + SetWindowLong(hwnd, GWL_STYLE, style); + + MARGINS margins = { -1 }; + DwmExtendFrameIntoClientArea(hwnd, &margins); + + spdlog::debug("Setting window to transparent"); + + } else { + auto style = GetWindowLong(hwnd, GWL_STYLE); + style |= WS_OVERLAPPED; + style &= ~WS_POPUP; + SetWindowLong(hwnd, GWL_STYLE, style); + + MARGINS margins = {0}; + DwmExtendFrameIntoClientArea(hwnd, &margins); + + spdlog::debug("Setting window to opaque"); + } +} + void TargetWindow::update() { sf::Event event{}; @@ -364,22 +399,7 @@ void TargetWindow::createWindow() auto dpi = GetWindowDPI(hwnd); spdlog::debug("Screen DPI: {}", dpi); - //if (windowed_) { - // DWM_BLURBEHIND bb{.dwFlags = DWM_BB_ENABLE, .fEnable = true, .hRgnBlur = nullptr}; - // DwmEnableBlurBehindWindow(hwnd, &bb); - //} // semi-transparent in window mode, but deprecated api - // On Linux the window will (should) automagically be semi-transparent - - // transparent windows window... - auto style = GetWindowLong(hwnd, GWL_STYLE); - style &= ~WS_OVERLAPPED; - style |= WS_POPUP; - SetWindowLong(hwnd, GWL_STYLE, style); - - - MARGINS margins; - margins.cxLeftWidth = -1; - DwmExtendFrameIntoClientArea(hwnd, &margins); + setTransparent(true); DEVMODE dev_mode = {}; dev_mode.dmSize = sizeof(DEVMODE); diff --git a/GlosSITarget/TargetWindow.h b/GlosSITarget/TargetWindow.h index b87f79c..b8a5aa8 100644 --- a/GlosSITarget/TargetWindow.h +++ b/GlosSITarget/TargetWindow.h @@ -39,6 +39,7 @@ class TargetWindow { void setFpsLimit(unsigned int fps_limit); void setClickThrough(bool click_through); + void setTransparent(bool transparent) const; void update(); void close(); diff --git a/common/Settings.h b/common/Settings.h index adada6b..75b251c 100644 --- a/common/Settings.h +++ b/common/Settings.h @@ -61,6 +61,7 @@ namespace Settings bool disableOverlay = false; bool hideAltTab = true; bool disableGlosSIOverlay = false; + bool opaqueSteamOverlay = false; } window; inline struct Controller @@ -219,6 +220,7 @@ namespace Settings safeParseValue(winconf, "disableOverlay", window.disableOverlay); safeParseValue(winconf, "hideAltTab", window.hideAltTab); safeParseValue(winconf, "disableGlosSIOverlay", window.disableGlosSIOverlay); + safeParseValue(winconf, "opaqueSteamOverlay", window.opaqueSteamOverlay); } if (const auto controllerConf = json["controller"]; !controllerConf.is_null() && !controllerConf.empty() && controllerConf.is_object()) @@ -336,6 +338,7 @@ namespace Settings json["window"]["scale"] = window.scale; json["window"]["disableOverlay"] = window.disableOverlay; json["window"]["hideAltTab"] = window.hideAltTab; + json["window"]["opaqueSteamOverlay"] = window.opaqueSteamOverlay; json["controller"]["maxControllers"] = controller.maxControllers; json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig; json["controller"]["emulateDS4"] = controller.emulateDS4; From 56c40038100c84201e8cd822895441dd8fee073b Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 27 Feb 2023 19:47:34 +0100 Subject: [PATCH 58/65] Refactor: HttpServer endpoints --- GlosSITarget/AppLauncher.cpp | 46 ++++++++-- GlosSITarget/CommonHttpEndpoints.h | 25 ++++++ GlosSITarget/GlosSITarget.vcxproj | 1 + GlosSITarget/GlosSITarget.vcxproj.filters | 3 + GlosSITarget/HttpServer.cpp | 100 ++++++++++++---------- GlosSITarget/HttpServer.h | 22 ++++- GlosSITarget/SteamTarget.cpp | 6 +- 7 files changed, 145 insertions(+), 58 deletions(-) create mode 100644 GlosSITarget/CommonHttpEndpoints.h diff --git a/GlosSITarget/AppLauncher.cpp b/GlosSITarget/AppLauncher.cpp index fa9137a..971ff0c 100644 --- a/GlosSITarget/AppLauncher.cpp +++ b/GlosSITarget/AppLauncher.cpp @@ -28,10 +28,11 @@ limitations under the License. #pragma comment(lib, "Shell32.lib") #endif -#include "..\common\Settings.h" +#include "../common/Settings.h" #include +#include "HttpServer.h" #include "Overlay.h" #include "../common/UnhookUtil.h" #include "../common/util.h" @@ -47,6 +48,32 @@ AppLauncher::AppLauncher( spdlog::debug("App launch requested"); } #endif + + HttpServer::AddEndpoint({"/launched-pids", + HttpServer::Method::GET, + [this](const httplib::Request& req, httplib::Response& res) { + const nlohmann::json j = launchedPids(); + res.set_content(j.dump(), "text/json"); + }}); + + HttpServer::AddEndpoint({"/launched-pids", + HttpServer::Method::POST, + [this](const httplib::Request& req, httplib::Response& res) { + try { + const nlohmann::json postbody = nlohmann::json::parse(req.body); + addPids(postbody.get>()); + } + catch (std::exception& e) { + res.status = 401; + res.set_content(nlohmann::json{ + {"code", 401}, + {"name", "Bad Request"}, + {"message", e.what()}, + } + .dump(), + "text/json"); + } + }}); }; void AppLauncher::launchApp(const std::wstring& path, const std::wstring& args) @@ -255,7 +282,7 @@ void AppLauncher::getProcessHwnds() propStore->GetValue(PKEY_AppUserModel_ID, &prop); if (prop.bstrVal != nullptr && std::wstring(prop.bstrVal) == launched_uwp_path_) { process_hwnds_.push_back(curr_wnd); - } + } } } while (curr_wnd != nullptr); } @@ -298,11 +325,11 @@ void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& a // } std::wstring args_cpy( args.empty() - ? L"" + ? L"" : ((native_seps_path.find(L" ") != std::wstring::npos ? L"\"" + native_seps_path + L"\"" - : native_seps_path) + L" " + args) - ); + : native_seps_path) + + L" " + args)); DWORD pid; @@ -319,7 +346,8 @@ void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& a &process_info)) { pid = process_info.dwProcessId; - } else { + } + else { DWORD error_code = GetLastError(); if (error_code == ERROR_ELEVATION_REQUIRED) { @@ -342,11 +370,13 @@ void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& a spdlog::error(L"Couldn't get process id after starting program: \"{}\"; Error code {}", native_seps_path, GetLastError()); } CloseHandle(shExecInfo.hProcess); - } else { + } + else { spdlog::error(L"Couldn't start program with elevated permissions: \"{}\"; Error code {}", native_seps_path, GetLastError()); return; } - } else { + } + else { spdlog::error(L"Could't start program: \"{}\"; Error code: {}", native_seps_path, error_code); return; } diff --git a/GlosSITarget/CommonHttpEndpoints.h b/GlosSITarget/CommonHttpEndpoints.h new file mode 100644 index 0000000..bd1c2a4 --- /dev/null +++ b/GlosSITarget/CommonHttpEndpoints.h @@ -0,0 +1,25 @@ +#pragma once +#include "HttpServer.h" +#include "../common/Settings.h" +#include "../common/steam_util.h" + +namespace CHTE { + +inline void addEndpoints() +{ + HttpServer::AddEndpoint( + {"/settings", + HttpServer::Method::GET, + [](const httplib::Request& req, httplib::Response& res) { + res.set_content(Settings::toJson().dump(), "text/json"); + }}); + + HttpServer::AddEndpoint( + {"/steam_settings", + HttpServer::Method::GET, + [](const httplib::Request& req, httplib::Response& res) { + res.set_content(util::steam::getSteamConfig().dump(4), "text/json"); + }}); +}; + +} // namespace CHTE diff --git a/GlosSITarget/GlosSITarget.vcxproj b/GlosSITarget/GlosSITarget.vcxproj index 26b7db1..2d09a17 100644 --- a/GlosSITarget/GlosSITarget.vcxproj +++ b/GlosSITarget/GlosSITarget.vcxproj @@ -209,6 +209,7 @@ + diff --git a/GlosSITarget/GlosSITarget.vcxproj.filters b/GlosSITarget/GlosSITarget.vcxproj.filters index ddb734e..f5f4110 100644 --- a/GlosSITarget/GlosSITarget.vcxproj.filters +++ b/GlosSITarget/GlosSITarget.vcxproj.filters @@ -179,6 +179,9 @@ Header Files + + Header Files + diff --git a/GlosSITarget/HttpServer.cpp b/GlosSITarget/HttpServer.cpp index e71c594..768b36f 100644 --- a/GlosSITarget/HttpServer.cpp +++ b/GlosSITarget/HttpServer.cpp @@ -23,10 +23,15 @@ limitations under the License. #include "../common/Settings.h" #include "../common/steam_util.h" -HttpServer::HttpServer(AppLauncher& app_launcher, std::function close) : app_launcher_(app_launcher), close_(std::move(close)) +HttpServer::HttpServer(std::function close) : close_(std::move(close)) { } +void HttpServer::AddEndpoint(const Endpoint&& e) +{ + endpoints_.push_back(e); +} + void HttpServer::run() { auto setCorsHeader = [](httplib::Response& res) { @@ -38,65 +43,66 @@ void HttpServer::run() setCorsHeader(res); }); - server_.Get("/launched-pids", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { - const nlohmann::json j = app_launcher_.launchedPids(); - res.set_content(j.dump(), "text/json"); - setCorsHeader(res); - }); + for (const auto& e : endpoints_) { + const auto fn = ([this, &e]() -> httplib::Server& (httplib::Server::*)(const std::string&, httplib::Server::Handler) { + switch (e.method) { + case POST: + return &httplib::Server::Post; + case PUT: + return &httplib::Server::Put; + case PATCH: + return &httplib::Server::Patch; + default: + return &httplib::Server::Get; + } + })(); - server_.Post("/launched-pids", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { - setCorsHeader(res); - try { - const nlohmann::json postbody = nlohmann::json::parse(req.body); - app_launcher_.addPids(postbody.get>()); - } catch (std::exception& e) { - res.status = 401; - res.set_content(nlohmann::json{ - {"code", 401}, - {"name", "Bad Request"}, - {"message", e.what()}, - } - .dump(), - "text/json"); - return; - } - catch (...) { - res.status = 500; - res.set_content(nlohmann::json{ - {"code", 500}, - {"name", "Internal Server Error"}, - {"message", "Unknown Error"}, - } - .dump(), - "text/json"); - return; - } - const nlohmann::json j = app_launcher_.launchedPids(); - res.set_content(j.dump(), "text/json"); - }); + (server_.*fn)(e.path, [this, &e, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { + setCorsHeader(res); + res.status = 0; + res.content_length_ = 0; + try { + e.handler(req, res); + } + catch (std::exception& err) { + spdlog::error("Exception in http handler: {}", err.what()); + res.status = res.status == 0 ? 500 : res.status; + if (res.content_length_ == 0) { + res.set_content(nlohmann::json{ + {"code", res.status}, + {"name", "HandlerError"}, + {"message", err.what()}, + } + .dump(), + "text/json"); + } + } + catch (...) { + res.status = 500; + res.set_content(nlohmann::json{ + {"code", res.status}, + {"name", "Internal Server Error"}, + {"message", "Unknown Error"}, + } + .dump(), + "text/json"); + return; + } + }); + } server_.Post("/quit", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { setCorsHeader(res); close_(); }); - server_.Get("/settings", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { - setCorsHeader(res); - res.set_content(Settings::toJson().dump(), "text/json"); - }); - - server_.Get("/steam_settings", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { - setCorsHeader(res); - res.set_content(util::steam::getSteamConfig().dump(4), "text/json"); - }); - server_thread_ = std::thread([this]() { if (!server_.listen("0.0.0.0", port_)) { spdlog::error("Couldn't start http-server"); return; } spdlog::debug("Started http-server on port {}", static_cast(port_)); -}); + }); } void HttpServer::stop() diff --git a/GlosSITarget/HttpServer.h b/GlosSITarget/HttpServer.h index 1660ffa..52fdd77 100644 --- a/GlosSITarget/HttpServer.h +++ b/GlosSITarget/HttpServer.h @@ -23,16 +23,34 @@ class AppLauncher; class HttpServer { public: - explicit HttpServer(AppLauncher& app_launcher, std::function close); + explicit HttpServer(std::function close); + + enum Method { + GET, + POST, + PUT, + PATCH, + }; + + struct Endpoint { + std::string path; + Method method; + std::function handler; + }; + + static void AddEndpoint(const Endpoint&& e); void run(); void stop(); + private: httplib::Server server_; std::thread server_thread_; uint16_t port_ = 8756; - AppLauncher& app_launcher_; std::function close_; + + static inline std::vector endpoints_; + }; \ No newline at end of file diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 56253f0..98b042c 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -28,6 +28,8 @@ limitations under the License. #include +#include "CommonHttpEndpoints.h" + SteamTarget::SteamTarget() : window_( [this] { run_ = false; }, @@ -43,7 +45,7 @@ SteamTarget::SteamTarget() delayed_shutdown_ = true; delay_shutdown_clock_.restart(); }), - server_(launcher_, [this] { run_ = false; }) + server_([this] { run_ = false; }) { target_window_handle_ = window_.getSystemHandle(); } @@ -62,6 +64,8 @@ int SteamTarget::run() auto steam_tweaks = CEFInject::SteamTweaks(); steam_tweaks.setAutoInject(true); + CHTE::addEndpoints(); + server_.run(); From de2bd579794a5b95764daaf8e1f513a6f505fbe3 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Tue, 28 Feb 2023 15:17:17 +0100 Subject: [PATCH 59/65] Alter CEF remote debugging alert wording --- GlosSITarget/SteamTarget.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 98b042c..09310f9 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -79,8 +79,8 @@ int SteamTarget::run() *overlay_id = Overlay::AddOverlayElem( [this, overlay_id, &end_frame_callbacks](bool window_has_focus, ImGuiID dockspace_id) { can_fully_initialize_ = false; - ImGui::Begin("GlosSI - CEF Debug not available"); - ImGui::Text("GlosSI makes use of Steam CEF Debugging for some functionality and plugins."); + ImGui::Begin("GlosSI - CEF remote debug not available"); + ImGui::Text("GlosSI makes use of Steam CEF remote debugging for some functionality and plugins."); ImGui::Text("GlosSI might not work fully without it."); if (ImGui::Button("Ignore and continue")) { From b3590537717523fd8935a401232ff284f2072d44 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Tue, 28 Feb 2023 16:34:29 +0100 Subject: [PATCH 60/65] Add global mode initialization hint --- GlosSITarget/SteamTarget.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/GlosSITarget/SteamTarget.cpp b/GlosSITarget/SteamTarget.cpp index 09310f9..f09b0c4 100644 --- a/GlosSITarget/SteamTarget.cpp +++ b/GlosSITarget/SteamTarget.cpp @@ -138,6 +138,23 @@ int SteamTarget::run() can_fully_initialize_ = false; } + if (!SteamOverlayDetector::IsSteamInjected() && Settings::common.allowGlobalMode) { + auto overlay_id = std::make_shared(-1); + *overlay_id = Overlay::AddOverlayElem( + [this, overlay_id, &end_frame_callbacks](bool window_has_focus, ImGuiID dockspace_id) { + ImGui::Begin("Global mode", nullptr, ImGuiWindowFlags_NoSavedSettings); + ImGui::Text("Global mode is initializing, please stand by..."); + ImGui::End(); + if (fully_initialized_) { + end_frame_callbacks.emplace_back([this, overlay_id] { + Overlay::RemoveOverlayElem(*overlay_id); + }); + } + }, + true); + window_.update(); + } + if (!util::steam::getXBCRebindingEnabled(steam_path_, steam_user_id_)) { auto overlay_id = std::make_shared(-1); *overlay_id = Overlay::AddOverlayElem( From 954d953f4bf48ae32774e35f41d2e65d964d07b4 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Tue, 28 Feb 2023 16:35:23 +0100 Subject: [PATCH 61/65] HttpEndpoints: Add endpoint to list avail endpoints + data hints --- GlosSITarget/AppLauncher.cpp | 57 +++++++++++++++++------------- GlosSITarget/CommonHttpEndpoints.h | 8 +++-- GlosSITarget/HttpServer.cpp | 43 ++++++++++++++++++---- GlosSITarget/HttpServer.h | 6 ++++ 4 files changed, 81 insertions(+), 33 deletions(-) diff --git a/GlosSITarget/AppLauncher.cpp b/GlosSITarget/AppLauncher.cpp index 971ff0c..9e5ac29 100644 --- a/GlosSITarget/AppLauncher.cpp +++ b/GlosSITarget/AppLauncher.cpp @@ -49,31 +49,38 @@ AppLauncher::AppLauncher( } #endif - HttpServer::AddEndpoint({"/launched-pids", - HttpServer::Method::GET, - [this](const httplib::Request& req, httplib::Response& res) { - const nlohmann::json j = launchedPids(); - res.set_content(j.dump(), "text/json"); - }}); - - HttpServer::AddEndpoint({"/launched-pids", - HttpServer::Method::POST, - [this](const httplib::Request& req, httplib::Response& res) { - try { - const nlohmann::json postbody = nlohmann::json::parse(req.body); - addPids(postbody.get>()); - } - catch (std::exception& e) { - res.status = 401; - res.set_content(nlohmann::json{ - {"code", 401}, - {"name", "Bad Request"}, - {"message", e.what()}, - } - .dump(), - "text/json"); - } - }}); + HttpServer::AddEndpoint({ + "/launched-pids", + HttpServer::Method::GET, + [this](const httplib::Request& req, httplib::Response& res) { + const nlohmann::json j = launchedPids(); + res.set_content(j.dump(), "text/json"); + }, + {1, 2, 3}, + }); + + HttpServer::AddEndpoint({ + "/launched-pids", + HttpServer::Method::POST, + [this](const httplib::Request& req, httplib::Response& res) { + try { + const nlohmann::json postbody = nlohmann::json::parse(req.body); + addPids(postbody.get>()); + } + catch (std::exception& e) { + res.status = 401; + res.set_content(nlohmann::json{ + {"code", 401}, + {"name", "Bad Request"}, + {"message", e.what()}, + } + .dump(), + "text/json"); + } + }, + {1, 2, 3, 4}, + {2, 3, 4}, + }); }; void AppLauncher::launchApp(const std::wstring& path, const std::wstring& args) diff --git a/GlosSITarget/CommonHttpEndpoints.h b/GlosSITarget/CommonHttpEndpoints.h index bd1c2a4..98c7098 100644 --- a/GlosSITarget/CommonHttpEndpoints.h +++ b/GlosSITarget/CommonHttpEndpoints.h @@ -7,19 +7,23 @@ namespace CHTE { inline void addEndpoints() { + HttpServer::AddEndpoint( {"/settings", HttpServer::Method::GET, [](const httplib::Request& req, httplib::Response& res) { res.set_content(Settings::toJson().dump(), "text/json"); - }}); + }, + "json"}); HttpServer::AddEndpoint( {"/steam_settings", HttpServer::Method::GET, [](const httplib::Request& req, httplib::Response& res) { res.set_content(util::steam::getSteamConfig().dump(4), "text/json"); - }}); + }, + "json"}); + }; } // namespace CHTE diff --git a/GlosSITarget/HttpServer.cpp b/GlosSITarget/HttpServer.cpp index 768b36f..d2b4fe1 100644 --- a/GlosSITarget/HttpServer.cpp +++ b/GlosSITarget/HttpServer.cpp @@ -16,17 +16,28 @@ limitations under the License. #include "HttpServer.h" #include -#include #include -#include "AppLauncher.h" -#include "../common/Settings.h" -#include "../common/steam_util.h" +#include HttpServer::HttpServer(std::function close) : close_(std::move(close)) { } +std::string HttpServer::ToString(Method m) +{ + switch (m) { + case POST: + return "POST"; + case PATCH: + return "PATCH"; + case PUT: + return "PUT"; + default: + return "GET"; + } +} + void HttpServer::AddEndpoint(const Endpoint&& e) { endpoints_.push_back(e); @@ -39,8 +50,29 @@ void HttpServer::run() }; server_.Get("/", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) { - res.set_content("", "text/json"); setCorsHeader(res); + + auto content_json = nlohmann::json{ + {"endpoints", nlohmann::json::array()}}; + + for (const auto& e : endpoints_) { + content_json["endpoints"].push_back( + nlohmann::json{ + {"path", e.path}, + {"method", ToString(e.method)}, + {"response", e.response_hint}, + {"payload", e.payload_hint}, + }); + } + + content_json["endpoints"].push_back( + nlohmann::json{ + {"path", "/quit"}, + {"method", "POST"} + }); + + res.set_content(content_json.dump(4), + "text/json"); }); for (const auto& e : endpoints_) { @@ -86,7 +118,6 @@ void HttpServer::run() } .dump(), "text/json"); - return; } }); } diff --git a/GlosSITarget/HttpServer.h b/GlosSITarget/HttpServer.h index 52fdd77..1fe27e3 100644 --- a/GlosSITarget/HttpServer.h +++ b/GlosSITarget/HttpServer.h @@ -17,6 +17,7 @@ limitations under the License. #include #include +#include class AppLauncher; @@ -25,17 +26,22 @@ class HttpServer { public: explicit HttpServer(std::function close); + // C++ enums suck. enum Method { GET, POST, PUT, PATCH, }; + // but im not in the mood of adding yet another dependency for just that shit here. + static std::string ToString(Method m); struct Endpoint { std::string path; Method method; std::function handler; + nlohmann::json response_hint = nullptr; + nlohmann::json payload_hint = nullptr; }; static void AddEndpoint(const Endpoint&& e); From d303f2be79d86ba422c9c54c6a37d9af3db890cd Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Tue, 28 Feb 2023 16:35:36 +0100 Subject: [PATCH 62/65] Add /running endpoint --- GlosSITarget/CommonHttpEndpoints.h | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/GlosSITarget/CommonHttpEndpoints.h b/GlosSITarget/CommonHttpEndpoints.h index 98c7098..8c74834 100644 --- a/GlosSITarget/CommonHttpEndpoints.h +++ b/GlosSITarget/CommonHttpEndpoints.h @@ -8,6 +8,14 @@ namespace CHTE { inline void addEndpoints() { + HttpServer::AddEndpoint( + {"/running", + HttpServer::Method::GET, + [](const httplib::Request& req, httplib::Response& res) { + // TODO: extend this when "passive" running of global mods is implemented + res.set_content(nlohmann::json{{"state", nlohmann::json{{"running", true}}}}.dump(), "text/json"); + }}); + HttpServer::AddEndpoint( {"/settings", HttpServer::Method::GET, From f2f2144017079b6d1259ae19120e3364f16f4437 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Tue, 28 Feb 2023 17:01:32 +0100 Subject: [PATCH 63/65] Remove GlosSITweaks uninstall from watchdog and let JS handle uninstall itself --- GlosSIWatchdog/dllmain.cpp | 6 ----- SteamTweaks/src/GlosSITweaks.ts | 39 ++++++++++++++++++++++++++++----- 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/GlosSIWatchdog/dllmain.cpp b/GlosSIWatchdog/dllmain.cpp index f9fac98..3e05df5 100644 --- a/GlosSIWatchdog/dllmain.cpp +++ b/GlosSIWatchdog/dllmain.cpp @@ -32,8 +32,6 @@ limitations under the License. #include "../common/Settings.h" #include "../common/HidHide.h" -#include - bool IsProcessRunning(DWORD pid) { const HANDLE process = OpenProcess(SYNCHRONIZE, FALSE, pid); @@ -119,10 +117,6 @@ DWORD WINAPI watchdog(HMODULE hModule) HidHide hidhide; hidhide.disableHidHide(); - spdlog::info("Uninstalling GlosSITweaks"); - auto steam_tweaks = CEFInject::SteamTweaks(); - steam_tweaks.uninstallTweaks(true); - if (Settings::launch.closeOnExit) { spdlog::info("Closing launched processes"); diff --git a/SteamTweaks/src/GlosSITweaks.ts b/SteamTweaks/src/GlosSITweaks.ts index ea0cf35..dc86ca7 100644 --- a/SteamTweaks/src/GlosSITweaks.ts +++ b/SteamTweaks/src/GlosSITweaks.ts @@ -4,11 +4,40 @@ import type { GlosSISettings } from './@types/GlosSISettings'; class SteamTargetApi { - public getGlosSIActive() { - return fetchWithTimeout('http://localhost:8756/', { timeout: 500 }) + + public static GlosSIActive = true; + + public static readonly ACTIVE_FAIL_THRESHOLD = 2; + private activeFailCounter = 0; + private static ActiveCheckTimer = 0; + + public constructor() { + if (SteamTargetApi.ActiveCheckTimer !== 0) { + clearInterval(SteamTargetApi.ActiveCheckTimer); + } + SteamTargetApi.ActiveCheckTimer = setInterval(() => { + void this.getGlosSIActive().then((active) => { + if (!active) { + this.activeFailCounter++; + if (this.activeFailCounter >= SteamTargetApi.ACTIVE_FAIL_THRESHOLD) { + window?.GlosSITweaks?.GlosSI?.uninstall?.(); + } + } + }); + }, 666); + } + + public async getGlosSIActive() { + return fetchWithTimeout('http://localhost:8756/running', { timeout: 500 }) .then( - () => true - ).catch(() => false); + () => { + SteamTargetApi.GlosSIActive = true; + return true; + } + ).catch(() => { + SteamTargetApi.GlosSIActive = false; + return false; + }); } public getSteamSettings(): Promise { return fetch('http://localhost:8756/steam_settings') @@ -33,6 +62,7 @@ class SteamTargetApi { class GlosSIApiCtor { public readonly SteamTarget: SteamTargetApi = new SteamTargetApi(); + } interface GlosSITweaks { @@ -51,7 +81,6 @@ declare global { const GlosSITweaks: GlosSITweaks; } - const installGlosSIApi = () => { window.GlosSITweaks = { GlosSI: { From 73226d3c6264dd6c95c182cb3f60d88e769ac7d6 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Wed, 1 Mar 2023 00:14:18 +0100 Subject: [PATCH 64/65] Add api to log from SteamTweaks --- GlosSITarget/CommonHttpEndpoints.h | 43 +++++++++++++++ SteamTweaks/.eslintrc.cjs | 52 +++++++++---------- SteamTweaks/src/@types/SteamClient.d.ts | 6 ++- SteamTweaks/src/GlosSITweaks.ts | 33 ++++++++++++ .../GamepadUI/MinimizeSteamGamepadUI.ts | 6 +-- .../Overlay/SharedContext/HideFPSCounter.ts | 21 +++++--- SteamTweaks/src/common/tweakApi.ts | 13 ++++- 7 files changed, 135 insertions(+), 39 deletions(-) diff --git a/GlosSITarget/CommonHttpEndpoints.h b/GlosSITarget/CommonHttpEndpoints.h index 8c74834..c5231a3 100644 --- a/GlosSITarget/CommonHttpEndpoints.h +++ b/GlosSITarget/CommonHttpEndpoints.h @@ -31,6 +31,49 @@ inline void addEndpoints() res.set_content(util::steam::getSteamConfig().dump(4), "text/json"); }, "json"}); + + HttpServer::AddEndpoint({ + "/log", + HttpServer::Method::POST, + [](const httplib::Request& req, httplib::Response& res) { + struct LogEntry { + std::string level; + std::string message; + }; + auto entry = LogEntry{}; + try { + const nlohmann::json postbody = nlohmann::json::parse(req.body); + entry.level = postbody.at("level"); + entry.message = postbody.at("message"); + } + catch (std::exception& e) { + res.status = 401; + res.set_content(nlohmann::json{ + {"code", 401}, + {"name", "Bad Request"}, + {"message", e.what()}, + } + .dump(), + "text/json"); + } + if (entry.level == "info") { + spdlog::info("GlosSITweaks: {}", entry.message); + } + else if (entry.level == "warn") { + spdlog::warn("GlosSITweaks: {}", entry.message); + } + else if (entry.level == "error") { + spdlog::error("GlosSITweaks: {}", entry.message); + } + else if (entry.level == "debug") { + spdlog::debug("GlosSITweaks: {}", entry.message); + } + else { + spdlog::trace("GlosSITweaks: {}", entry.message); + } + }, + + }); }; diff --git a/SteamTweaks/.eslintrc.cjs b/SteamTweaks/.eslintrc.cjs index d87fc32..51e4c64 100644 --- a/SteamTweaks/.eslintrc.cjs +++ b/SteamTweaks/.eslintrc.cjs @@ -86,32 +86,32 @@ module.exports = { } ], '@typescript-eslint/no-use-before-define': ['error', { 'functions': false }], - "@typescript-eslint/naming-convention": [ - "error", - { - "selector": "default", - "format": ["camelCase", "PascalCase"] - }, - { - "selector": "variable", - "format": ["camelCase", "UPPER_CASE"] - }, - { - "selector": "parameter", - "format": ["camelCase"], - "leadingUnderscore": "allow" - }, - { - "selector": "memberLike", - "modifiers": ["private"], - "format": ["camelCase"], - "leadingUnderscore": "require" - }, - { - "selector": "typeLike", - "format": ["PascalCase"] - } - ], + // "@typescript-eslint/naming-convention": [ + // "error", + // { + // "selector": "default", + // "format": ["camelCase", "PascalCase"] + // }, + // { + // "selector": "variable", + // "format": ["camelCase", "UPPER_CASE"] + // }, + // { + // "selector": "parameter", + // "format": ["camelCase"], + // "leadingUnderscore": "allow" + // }, + // { + // "selector": "memberLike", + // "modifiers": ["private"], + // "format": ["camelCase"], + // "leadingUnderscore": "require" + // }, + // { + // "selector": "typeLike", + // "format": ["PascalCase"] + // } + // ], 'no-console': 'off', 'no-return-await': 'error', 'arrow-body-style': 'error', diff --git a/SteamTweaks/src/@types/SteamClient.d.ts b/SteamTweaks/src/@types/SteamClient.d.ts index a812952..880676a 100644 --- a/SteamTweaks/src/@types/SteamClient.d.ts +++ b/SteamTweaks/src/@types/SteamClient.d.ts @@ -1,7 +1,9 @@ export interface SteamClient { Settings: { - SetInGameOverlayShowFPSCorner: (value: 0|1|2|3|4) => void; - SetInGameOverlayShowFPSContrast: (value: boolean) => void; + // Current stable (As time of commit); Beta does not have this anymore... + SetInGameOverlayShowFPSCorner?: (value: 0|1|2|3|4) => void; + SetInGameOverlayShowFPSContrast?: (value: boolean) => void; + // TODO: find a way to change setting on beta (and soon stable...) }; UI: { GetUiMode: () => Promise; diff --git a/SteamTweaks/src/GlosSITweaks.ts b/SteamTweaks/src/GlosSITweaks.ts index dc86ca7..5d5bf21 100644 --- a/SteamTweaks/src/GlosSITweaks.ts +++ b/SteamTweaks/src/GlosSITweaks.ts @@ -27,6 +27,39 @@ class SteamTargetApi { }, 666); } + // eslint-disable-next-line @typescript-eslint/no-explicit-any + public log(level: string, ...args: any[]) { + void fetch('http://localhost:8756/log', { + method: 'POST', + body: JSON.stringify({ + level, + // eslint-disable-next-line @typescript-eslint/restrict-template-expressions + message: `${args}` + }) + }); + switch (level) { + case 'error': + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + console.error(...args); + break; + case 'warn': + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + console.warn(...args); + break; + case 'info': + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + console.info(...args); + break; + case 'debug': + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + console.debug(...args); + break; + default: + // eslint-disable-next-line @typescript-eslint/no-unsafe-argument + console.log(...args); + } + } + public async getGlosSIActive() { return fetchWithTimeout('http://localhost:8756/running', { timeout: 500 }) .then( diff --git a/SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts b/SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts index 31e0274..081525b 100644 --- a/SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts +++ b/SteamTweaks/src/Tweaks/GamepadUI/MinimizeSteamGamepadUI.ts @@ -13,9 +13,9 @@ initTweak('MinimizeSteamGamepadUI', async () => { return true; } if (!isGamepadUI && minimizeGPUI) { - console.warn('MinimizeSteamGamepadUI is enabled but Steam is not in GamepadUI mode'); + GlosSIApi.SteamTarget.log('warn', 'MinimizeSteamGamepadUI is enabled but Steam is not in GamepadUI mode'); } return false; }).then((minimized: boolean) => { - console.log('MinimizeSteamGamepadUI installed; Minimized GamepadUI:', minimized); -}).catch((e) => console.error('MinimizeSteamGamepadUI failed to install', e)); + GlosSIApi.SteamTarget.log('debug', 'MinimizeSteamGamepadUI installed; Minimized GamepadUI:', minimized); +}).catch((e) =>GlosSIApi.SteamTarget.log('error', 'MinimizeSteamGamepadUI failed to install', e)); diff --git a/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts b/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts index 1b1b404..b2e1067 100644 --- a/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts +++ b/SteamTweaks/src/Tweaks/Overlay/SharedContext/HideFPSCounter.ts @@ -9,16 +9,25 @@ initTweak('HideFPSCounter', { ((await GlosSIApi.SteamTarget.getSteamSettings()).system as SteamConfig) .InGameOverlayShowFPSCorner ) as 0 | 1 | 2 | 3 | 4; - SteamClient.Settings.SetInGameOverlayShowFPSCorner(0); + if (!SteamClient.Settings.SetInGameOverlayShowFPSCorner) { + GlosSIApi.SteamTarget.log('warn', + 'HideFPSCounter: SteamClient.Settings.SetInGameOverlayShowFPSCorner is not available.' + +'Can\'t hide FPS Counter corner.' + ); + } + SteamClient.Settings.SetInGameOverlayShowFPSCorner?.(0); }, uninstall: () => { - console.log('uninstalling HideFPSCounter Tweak. Restoring FPS Counter corner: ', backup.originalFpsCorner); - SteamClient.Settings.SetInGameOverlayShowFPSCorner((backup.originalFpsCorner ?? 0) as 0 | 1 | 2 | 3 | 4); + if (!SteamClient.Settings.SetInGameOverlayShowFPSCorner) { + return; + } + GlosSIApi.SteamTarget.log('debug','uninstalling HideFPSCounter Tweak. Restoring FPS Counter corner: ', backup.originalFpsCorner); + SteamClient.Settings.SetInGameOverlayShowFPSCorner?.((backup.originalFpsCorner ?? 0) as 0 | 1 | 2 | 3 | 4); setTimeout(() => { // steam might not actually register the setting?! Try again like 10 seconds later... ¯\_(ツ)_/¯ - SteamClient.Settings.SetInGameOverlayShowFPSCorner((backup.originalFpsCorner ?? 0) as 0 | 1 | 2 | 3 | 4); + SteamClient.Settings.SetInGameOverlayShowFPSCorner?.((backup.originalFpsCorner ?? 0) as 0 | 1 | 2 | 3 | 4); }, 10 * 1000); } }).then(() => { - console.log('HideFPSCounter installed'); -}).catch((e) => console.error('HideFPSCounter failed to install', e)); + GlosSIApi.SteamTarget.log('debug', 'HideFPSCounter installed'); +}).catch((e) => GlosSIApi.SteamTarget.log('error', 'HideFPSCounter failed to install', e)); diff --git a/SteamTweaks/src/common/tweakApi.ts b/SteamTweaks/src/common/tweakApi.ts index cac7880..b3f7c55 100644 --- a/SteamTweaks/src/common/tweakApi.ts +++ b/SteamTweaks/src/common/tweakApi.ts @@ -9,12 +9,21 @@ export const initTweak = (name: string, tweakMain: (() => T)|{ if (typeof tweakMain === 'object') { window.GlosSITweaks[name] = { install: tweakMain.install, uninstall: () => { - tweakMain.uninstall(); + try { + tweakMain.uninstall(); + } catch (e) { + GlosSIApi.SteamTarget.log('error', e); + } delete window.GlosSITweaks[name]; } }; } else { window.GlosSITweaks[name] = { install: tweakMain }; } - return window.GlosSITweaks[name].install() as T; + try { + return window.GlosSITweaks[name].install() as T; + } catch (e) { + GlosSIApi.SteamTarget.log('error', e); + throw e; + } }; From 0ebfd1e9737fbcbe853c7a39158502eef151f5aa Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Fri, 22 Dec 2023 16:20:50 +0100 Subject: [PATCH 65/65] Update release deps --- GlosSIConfig/Resource.rc | 36 +++++++- GlosSITarget/Resource.rc | 176 +++++++++++++++++++++++++++++++++++++- Installer/Installer.nsi | 56 +++++++++++- download_release_deps.ps1 | 4 +- glossi.code-workspace | 92 ++++++++++++++++++++ 5 files changed, 353 insertions(+), 11 deletions(-) diff --git a/GlosSIConfig/Resource.rc b/GlosSIConfig/Resource.rc index 276c41a..1946067 100644 --- a/GlosSIConfig/Resource.rc +++ b/GlosSIConfig/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,2,0041001700025 - PRODUCTVERSION 0,1,2,0041001700025 + FILEVERSION 0,1,2,0067006100730 + PRODUCTVERSION 0,1,2,0067006100730 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - Config" - VALUE "FileVersion", "0.1.2.0-41-g17b0d25" + VALUE "FileVersion", "0.1.2.0-67-g61ff730" VALUE "InternalName", "GlosSIConfig" VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSIConfig.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.2.0-41-g17b0d25" + VALUE "ProductVersion", "0.1.2.0-67-g61ff730" END END BLOCK "VarFileInfo" @@ -107,3 +107,31 @@ IDI_ICON1 ICON "..\GlosSI_Icon.ico" ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GlosSITarget/Resource.rc b/GlosSITarget/Resource.rc index 8284c44..ee0b803 100644 --- a/GlosSITarget/Resource.rc +++ b/GlosSITarget/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,1,2,0041007165813 - PRODUCTVERSION 0,1,2,0041007165813 + FILEVERSION 0,1,2,0068000002000 + PRODUCTVERSION 0,1,2,0068000002000 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - SteamTarget" - VALUE "FileVersion", "0.1.2.0-41-g7165813" + VALUE "FileVersion", "0.1.2.0-68-g0f02eca" VALUE "InternalName", "GlosSITarget" VALUE "LegalCopyright", "Copyright (C) 2021-2022 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSITarget.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.1.2.0-41-g7165813" + VALUE "ProductVersion", "0.1.2.0-68-g0f02eca" END END BLOCK "VarFileInfo" @@ -107,3 +107,171 @@ IDI_ICON1 ICON "..\\GlosSI_Icon.ico" ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Installer/Installer.nsi b/Installer/Installer.nsi index 71d0dee..ae77fb6 100644 --- a/Installer/Installer.nsi +++ b/Installer/Installer.nsi @@ -3,7 +3,7 @@ !define APP_NAME "GlosSI" !define COMP_NAME "Peter Repukat - Flatspotsoftware" !define WEB_SITE "https://glossi.flatspot.pictures/" -!define VERSION "0.1.2.0-41-g7165813" +!define VERSION "0.1.2.0-68-g0f02eca" !define COPYRIGHT "Peter Repukat - FlatspotSoftware © 2017-2022" !define DESCRIPTION "SteamInput compatibility tool" !define INSTALLER_NAME "GlosSI-Installer.exe" @@ -191,3 +191,57 @@ DeleteRegKey ${REG_ROOT} "${REG_APP_PATH}" DeleteRegKey ${REG_ROOT} "${UNINSTALL_PATH}" SectionEnd + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/download_release_deps.ps1 b/download_release_deps.ps1 index 04c2692..93b8e23 100644 --- a/download_release_deps.ps1 +++ b/download_release_deps.ps1 @@ -1,3 +1,3 @@ -Invoke-WebRequest -o ViGEmBusSetup_x64.exe https://github.com/ViGEm/ViGEmBus/releases/download/v1.21.442.0/ViGEmBus_1.21.442_x64_x86_arm64.exe -Invoke-WebRequest -o HidHideSetup.exe https://github.com/ViGEm/HidHide/releases/download/v1.2.98.0/HidHide_1.2.98_x64.exe +Invoke-WebRequest -o ViGEmBusSetup_x64.exe https://github.com/nefarius/ViGEmBus/releases/download/v1.22.0/ViGEmBus_1.22.0_x64_x86_arm64.exe +Invoke-WebRequest -o HidHideSetup.exe https://github.com/nefarius/HidHide/releases/download/v1.4.192.0/HidHide_1.4.192_x64.exe Invoke-WebRequest -o vc_redist.x64.exe https://aka.ms/vs/16/release/vc_redist.x64.exe \ No newline at end of file diff --git a/glossi.code-workspace b/glossi.code-workspace index 83d064a..f61ef44 100644 --- a/glossi.code-workspace +++ b/glossi.code-workspace @@ -30,6 +30,98 @@ }, ], "settings": { + "files.associations": { + "algorithm": "cpp", + "chrono": "cpp", + "filesystem": "cpp", + "xstring": "cpp", + "xutility": "cpp", + "xhash": "cpp", + "xtree": "cpp", + "map": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "bit": "cpp", + "cctype": "cpp", + "charconv": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "concepts": "cpp", + "condition_variable": "cpp", + "coroutine": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdint": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "deque": "cpp", + "exception": "cpp", + "resumable": "cpp", + "format": "cpp", + "forward_list": "cpp", + "fstream": "cpp", + "functional": "cpp", + "future": "cpp", + "initializer_list": "cpp", + "iomanip": "cpp", + "ios": "cpp", + "iosfwd": "cpp", + "iostream": "cpp", + "istream": "cpp", + "iterator": "cpp", + "limits": "cpp", + "list": "cpp", + "locale": "cpp", + "memory": "cpp", + "mutex": "cpp", + "new": "cpp", + "numeric": "cpp", + "optional": "cpp", + "ostream": "cpp", + "queue": "cpp", + "random": "cpp", + "ranges": "cpp", + "ratio": "cpp", + "regex": "cpp", + "set": "cpp", + "span": "cpp", + "sstream": "cpp", + "stack": "cpp", + "stdexcept": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "string": "cpp", + "system_error": "cpp", + "thread": "cpp", + "tuple": "cpp", + "type_traits": "cpp", + "typeinfo": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "utility": "cpp", + "valarray": "cpp", + "variant": "cpp", + "vector": "cpp", + "xfacet": "cpp", + "xiosbase": "cpp", + "xlocale": "cpp", + "xlocbuf": "cpp", + "xlocinfo": "cpp", + "xlocmes": "cpp", + "xlocmon": "cpp", + "xlocnum": "cpp", + "xloctime": "cpp", + "xmemory": "cpp", + "xstddef": "cpp", + "xtr1common": "cpp" + } // uncomment if not already in user settings and wanting to debug/build c++ code (vs 2022 required!) // "terminal.integrated.profiles.windows": { // "Developer PowerShell for VS 2022": {