Compare commits

..

No commits in common. 'main' and '0.1.1.0' have entirely different histories.

@ -7,9 +7,6 @@ assignees: ''
---
- [ ] _**I have read and understand that tickets without Logs (or other info if Logs are 1000% not relevant) will be closed without comment.**_
## Expected Behavior
@ -32,7 +29,4 @@ assignees: ''
## Logs / Additional Files
Log and config files are located in `%appdata%/GlosSI`
The files are called: glossiconfig.log, glossitarget.log, GlosSIWatchdog.log and UWPOverlayEnabler.log
_(Windows-search may not pick up the files when searching for `log` **if** file endings are hidden (default setting))_
(Log and config files are located in `%appdata%/GlosSI`)

@ -7,8 +7,6 @@ assignees: ''
---
- [ ] _**I have read and understand that tickets without Logs (or other info if Logs are 1000% not relevant) will be closed without comment.**_
## General Information
- Version: (0.0.0.0-0-g1234567)
@ -18,7 +16,4 @@ assignees: ''
## Logs / Additional Files
Log and config files are located in `%appdata%/GlosSI`
The files are called: glossiconfig.log, glossitarget.log, GlosSIWatchdog.log and UWPOverlayEnabler.log
_(Windows-search may not pick up the files when searching for `log` **if** file endings are hidden (default setting))_
(Log and config files are located in `%appdata%/GlosSI`)

11
.gitmodules vendored

@ -27,16 +27,13 @@
url = https://github.com/nlohmann/json
[submodule "deps/traypp"]
path = deps/traypp
url = https://github.com/Soundux/traypp
url = https://github.com/Soundux/traypp.git
[submodule "deps/fifo_map"]
path = deps/fifo_map
url = https://github.com/nlohmann/fifo_map
url = git@github.com:nlohmann/fifo_map.git
[submodule "deps/Shortcuts_VDF"]
path = deps/Shortcuts_VDF
url = https://github.com/Alia5/Shortcuts_VDF
url = git@github.com:Alia5/Shortcuts_VDF.git
[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
url = git@github.com:yhirose/cpp-httplib.git

41
.vscode/tasks.json vendored

@ -1,41 +0,0 @@
{
"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",
},
}
]
}

@ -1,25 +0,0 @@
{
"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
}

@ -1,43 +0,0 @@
{
"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",
},
}
]
}

@ -1,464 +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.
*/
#include "CEFInject.h"
#include <easywsclient.hpp>
#define _SSIZE_T_DEFINED
#include <easywsclient.cpp> // seems like a hack to me, but eh, what is in the doc will be done ¯\_(ツ)_/¯
#include "../common/nlohmann_json_wstring.h"
#include <fstream>
#include <streambuf>
#include <spdlog/spdlog.h>
#include "../common/Settings.h"
namespace CEFInject
{
namespace internal
{
httplib::Client GetHttpClient(uint16_t port)
{
httplib::Client cli("localhost", port);
cli.set_connection_timeout(0, 200000);
cli.set_read_timeout(0, 500000);
return cli;
}
static uint32_t msg_id = 0;
}
bool CEFDebugAvailable(uint16_t port)
{
auto cli = internal::GetHttpClient(port);
if (auto res = cli.Get("/json")) {
if (res->status == 200) {
return true;
}
}
return false;
}
std::vector<std::wstring> AvailableTabNames(uint16_t port)
{
std::vector<std::wstring> tabs;
const auto json = AvailableTabs(port);
for (const auto& j : json) {
tabs.push_back(j["title"].get<std::wstring>());
}
return tabs;
}
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::basic_json<> InjectJs(std::string_view tab_name, std::string_view debug_url, std::wstring_view js, uint16_t port)
{
std::shared_ptr<easywsclient::WebSocket> ws{
easywsclient::WebSocket::from_url(debug_url.data())
};
if (ws)
{
nlohmann::json res = nullptr;
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 (msg.at("result").at("result").at("type").get<std::string>() != "undefined") {
res = msg.at("result").at("result").at("value");
}
}
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()));
}
return res;
}
return nullptr;
}
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")) {
if (res->status == 200) {
const auto json = nlohmann::json::parse(res->body);
for (const auto& tab : json) {
if (tab["title"].get<std::wstring>().find(tabname) != std::string::npos) {
return InjectJs(tab["title"].get<std::string>(), tab["webSocketDebuggerUrl"].get<std::string>(), js, port);
}
}
}
}
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)
{
if (tab_name.empty()) {
for (auto ts = AvailableTabNames(); const auto & tn : ts)
{
// 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;
}
if (glossi_tweaks_js_.empty())
{
if (!readGlosSITweaksJs()) {
return false;
}
}
const auto find_tab = (
[&info]() -> std::function<nlohmann::json(const nlohmann::json::array_t& tabList)>
{
if (!info.name.empty())
{
return [&info](const nlohmann::json::array_t& tabList)
{
for (const auto& tab : tabList) {
if (tab["title"].get<std::string>().find(info.name.data()) != std::string::npos) {
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<std::string>() == info.id) {
return tab;
}
}
return nlohmann::json{};
};
}
if (!info.webSocketDebuggerUrl.empty())
{
return [&info](const nlohmann::json::array_t& tabList)
{
for (const auto& tab : tabList) {
if (tab["webSocketDebuggerUrl"].get<std::string>() == info.webSocketDebuggerUrl) {
return tab;
}
}
return nlohmann::json{};
};
}
return nullptr;
}
)();
if (find_tab == nullptr) {
return false;
}
const auto tabs = AvailableTabs(port);
const auto tab = find_tab(tabs);
if (tab.empty()) {
return false;
}
const auto res = InjectJs(tab["title"].get<std::string>(), tab["webSocketDebuggerUrl"].get<std::string>(), glossi_tweaks_js_, port);
if (res.is_boolean() && res.get<bool>()) {
glossi_tweaks_injected_map_[tab["id"].get<std::string>()] = true;
spdlog::trace("CEFInject: GlosSITweaks injected into tab: {}", tab["title"].get<std::string>());
return true;
}
return false;
}
bool SteamTweaks::uninstallTweaks(bool force)
{
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;
}
std::vector<std::future<void>> futures;
for (auto ts = AvailableTabs(); auto & tab : ts) {
futures.push_back(std::async(std::launch::async, [this, &tab]()
{
InjectJs(tab["title"].get<std::string>(), tab["webSocketDebuggerUrl"].get<std::string>(), 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<std::future<void>>{};
auto tabs = AvailableTabs();
for (auto& tab : tabs) {
if (glossi_tweaks_injected_map_.contains(tab["id"].get<std::string>())) {
continue;
}
glossi_tweaks_injected_map_[tab["id"].get<std::string>()] = true;
futures.push_back(std::async([this, &tab]()
{
InjectJs(tab["title"].get<std::string>(), tab["webSocketDebuggerUrl"].get<std::string>(), 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<std::string>().find(path_tab_map_.at(dir_name.wstring())) != std::string::npos) {
InjectJs(tab["title"].get<std::string>(), tab["webSocketDebuggerUrl"].get<std::string>(), 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<wchar_t>(js_file)),
std::istreambuf_iterator<wchar_t>() };
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<wchar_t>(js_file)),
std::istreambuf_iterator<wchar_t>() };
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);
for (const auto& tweak : js_tweaks_cache_ | std::views::keys) {
spdlog::debug(
"CEFInject: Found {} tweak: {}",
builtin ? "builtin" : "user",
tweak.string()
);
}
}
}

@ -1,104 +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 <future>
#include <httplib.h>
#include <nlohmann/json.hpp>
namespace CEFInject
{
namespace internal {
httplib::Client GetHttpClient(uint16_t port);
static inline uint16_t port_ = 8080;
}
inline void setPort(uint16_t port)
{
internal::port_ = port;
}
bool CEFDebugAvailable(uint16_t port = internal::port_);
std::vector<std::wstring> AvailableTabNames(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 WSAStartupWrap
{
public:
explicit WSAStartupWrap();
~WSAStartupWrap();
private:
bool wsa_startup_succeeded_ = false;
};
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_);
bool uninstallTweaks(bool force = false);
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<tab_id, bool> glossi_tweaks_injected_map_;
std::future<void> auto_inject_future_;
std::wstring glossi_tweaks_js_;
std::map<std::filesystem::path, std::wstring> js_tweaks_cache_;
using path_name = std::wstring;
using tab_name = std::string;
static inline const std::map<path_name, tab_name> 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";
static constexpr std::string_view steam_tweaks_path_ = "SteamTweaks";
static constexpr std::wstring_view uninstall_glossi_tweaks_js_ = LR"(
(() => {
return window.GlosSITweaks?.GlosSI?.uninstall();
})();
)";
};
namespace internal {
static WSAStartupWrap wsa_startup_wrap{};
}
}

@ -1,165 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<ItemGroup>
<ClInclude Include="CEFInject.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="CEFInject.cpp" />
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{74fba967-ab7e-43ea-b561-3f4821954b3b}</ProjectGuid>
<RootNamespace>CEFInjectLib</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>..\deps\cpp-httplib;..\deps\json\include;..\deps\easywsclient;..\deps\spdlog\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>..\deps\cpp-httplib;..\deps\json\include;..\deps\easywsclient;..\deps\spdlog\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_CRT_SECURE_NO_WARNINGS;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;_CRT_SECURE_NO_WARNINGS;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>
</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -1,27 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="CEFInject.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="CEFInject.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

@ -4,22 +4,12 @@ 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
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
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "common", "common\common.vcxproj", "{DFED4B7E-D04C-442B-BB48-5B6068A6B31B}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -59,20 +49,6 @@ 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
{DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Debug|x64.ActiveCfg = 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|x86.ActiveCfg = Release|Win32
{DFED4B7E-D04C-442B-BB48-5B6068A6B31B}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

@ -1,43 +0,0 @@
{
"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",
},
}
]
}

@ -2,7 +2,6 @@
#include <QQuickImageProvider>
#include <Windows.h>
#include <QRegularExpression>
#include <shellapi.h>
class ExeImageProvider : public QQuickImageProvider {
public:
ExeImageProvider()

@ -69,12 +69,8 @@
<AdditionalUsingDirectories>%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/Zc:__cplusplus /Zc:twoPhase- %(AdditionalOptions)</AdditionalOptions>
<CompileAsWinRT>false</CompileAsWinRT>
<PreprocessorDefinitions>NOMINMAX;CONFIGAPP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\deps\WinReg;..\deps\fifo_map\src;..\deps\Shortcuts_VDF\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Manifest>
<EnableDpiAwareness>true</EnableDpiAwareness>
@ -96,12 +92,8 @@
<AdditionalUsingDirectories>%(AdditionalUsingDirectories)</AdditionalUsingDirectories>
<AdditionalOptions>/Zc:__cplusplus /Zc:zwoPhase- /permissive- %(AdditionalOptions)</AdditionalOptions>
<CompileAsWinRT>false</CompileAsWinRT>
<PreprocessorDefinitions>NOMINMAX;CONFIGAPP;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<PreprocessorDefinitions>NOMINMAX;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<AdditionalIncludeDirectories>..\deps\WinReg;..\deps\fifo_map\src;..\deps\Shortcuts_VDF\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Manifest>
<EnableDpiAwareness>true</EnableDpiAwareness>
@ -144,7 +136,6 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\common\UnhookUtil.cpp" />
<ClCompile Include="ExeImageProvider.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="UIModel.cpp" />
@ -173,6 +164,7 @@
<ItemGroup>
<ClInclude Include="ExeImageProvider.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="steamgrid_api_keys.h" />
<ClInclude Include="UWPFetch.h" />
<ClInclude Include="WinEventFilter.h" />
</ItemGroup>

@ -37,9 +37,6 @@
<ClCompile Include="ExeImageProvider.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<None Include="qml\main.qml">
@ -108,6 +105,9 @@
<ClInclude Include="ExeImageProvider.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="steamgrid_api_keys.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Xml Include="manifest.xml">

File diff suppressed because it is too large Load Diff

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -22,8 +22,6 @@ limitations under the License.
#include <QJsonArray>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QMetaType>
#include <WinReg/WinReg.hpp>
@ -32,18 +30,29 @@ limitations under the License.
#ifdef _WIN32
#include "UWPFetch.h"
#include <Windows.h>
#include <shlobj.h>
#endif
#include "ExeImageProvider.h"
#include "ExeImageProvider.h"
#include "../version.hpp"
#include "../common/UnhookUtil.h"
#include "../common/util.h"
#include "steamgrid_api_keys.h"
UIModel::UIModel() : QObject(nullptr)
{
auto path = util::path::getDataDirPath();
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);
qDebug() << "Version: " << getVersionString();
@ -53,22 +62,14 @@ UIModel::UIModel() : QObject(nullptr)
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
auto defaultConf = getDefaultConf();
saveDefaultConf(defaultConf);
parseShortcutVDF();
readTargetConfigs();
updateCheck();
readUnhookBytes();
auto font = QGuiApplication::font();
font.setPointSize(11);
font.setFamily("Roboto");
QGuiApplication::setFont(font);
std::ofstream{getSteamPath() / ".cef-enable-remote-debugging"};
}
void UIModel::readTargetConfigs()
@ -116,8 +117,6 @@ bool UIModel::updateTarget(int index, QVariant shortcut)
const auto map = shortcut.toMap();
const auto json = QJsonObject::fromVariantMap(map);
const auto was_in_steam_ = isInSteam(shortcut);
auto oldSteamName = targets_[index].toMap()["name"].toString();
auto oldName =
targets_[index].toMap()["name"].toString().replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json";
@ -135,19 +134,15 @@ bool UIModel::updateTarget(int index, QVariant shortcut)
path /= config_dir_name_.toStdString();
path /= (map["name"].toString()).toStdString();
if (was_in_steam_) {
if (removeFromSteam(oldSteamName, QString::fromStdWString(path.wstring()))) {
if (!addToSteam(shortcut, QString::fromStdWString(path.wstring()))) {
qDebug() << "Couldn't add shortcut \"" << (map["name"].toString()) << "\" to Steam when updating";
return false;
}
return true;
if (removeFromSteam(oldSteamName, QString::fromStdWString(path.wstring()))) {
if (!addToSteam(shortcut, QString::fromStdWString(path.wstring()))) {
qDebug() << "Couldn't add shortcut \"" << (map["name"].toString()) << "\" to Steam when updating";
return false;
}
qDebug() << "Couldn't remove shortcut \"" << oldName << "\" from Steam when updating";
return false;
} else {
return true;
}
qDebug() << "Couldn't remove shortcut \"" << oldName << "\" from Steam when updating";
return false;
}
void UIModel::deleteTarget(int index)
@ -166,9 +161,7 @@ bool UIModel::isInSteam(QVariant shortcut) const
{
const auto map = shortcut.toMap();
for (auto& steam_shortcut : shortcuts_vdf_) {
if (
map["name"].toString() == QString::fromStdString(steam_shortcut.appname) ||
map["oldName"].toString() == QString::fromStdString(steam_shortcut.appname)) {
if (map["name"].toString() == QString::fromStdString(steam_shortcut.appname)) {
if (QString::fromStdString(steam_shortcut.exe).toLower().contains("glossitarget.exe")) {
return true;
}
@ -178,7 +171,7 @@ bool UIModel::isInSteam(QVariant shortcut) const
return false;
}
uint32_t UIModel::getAppId(QVariant shortcut)
uint32_t UIModel::getAppId(QVariant shortcut) const
{
if (!isInSteam(shortcut)) {
return 0;
@ -187,10 +180,6 @@ uint32_t UIModel::getAppId(QVariant shortcut)
for (auto& steam_shortcut : shortcuts_vdf_) {
if (map["name"].toString() == QString::fromStdString(steam_shortcut.appname)) {
if (QString::fromStdString(steam_shortcut.exe).toLower().contains("glossitarget.exe")) {
if (steam_shortcut.appid == 0) {
parseShortcutVDF();
return getAppId(shortcut);
}
return steam_shortcut.appid;
}
}
@ -198,21 +187,6 @@ 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();
@ -350,18 +324,13 @@ void UIModel::enableSteamInputXboxSupport()
}
}
bool UIModel::restartSteam(const QString& steamURL)
bool UIModel::restartSteam()
{
const auto path = getSteamPath();
if (QProcess::execute("taskkill.exe", {"/im", steam_executable_name_, "/f"}) != 0) {
return false;
}
if (steamURL.isEmpty()) {
QProcess::startDetached(QString::fromStdWString(path) + "/" + steam_executable_name_);
}
else {
system((QString::fromLatin1("start ") + steamURL).toStdString().c_str());
}
QProcess::startDetached(QString::fromStdWString(path) + "/" + steam_executable_name_);
return true;
}
@ -387,8 +356,17 @@ void UIModel::updateCheck()
QVariantMap UIModel::getDefaultConf() const
{
auto path = util::path::getDataDirPath();
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";
path /= "default.json";
QJsonObject defaults = {
@ -397,20 +375,7 @@ QVariantMap UIModel::getDefaultConf() const
{"version", 1},
{"extendedLogging", false},
{"snapshotNotify", false},
{"steamgridApiKey", QJsonValue::Null},
{"steamPath",
QJsonValue::fromVariant(QString::fromStdWString(getSteamPath(false).wstring()))},
{"steamUserId",
QJsonValue::fromVariant(QString::fromStdWString(getSteamUserId(false)))},
{"globalModeGameId", ""},
{"globalModeUseGamepadUI", true},
{"minimizeSteamGamepadUI", true},
{"controller",
QJsonObject{{"maxControllers", -1},
{"emulateDS4", false},
{"allowDesktopConfig", false},
{"updateRate", 144}
}},
{"controller", QJsonObject{{"maxControllers", 1}, {"emulateDS4", false}, {"allowDesktopConfig", false}}},
{"devices",
QJsonObject{
{"hideDevices", true},
@ -430,12 +395,9 @@ QVariantMap UIModel::getDefaultConf() const
{"window",
QJsonObject{
{"disableOverlay", false},
{"hideAltTab", true},
{"maxFps", QJsonValue::Null},
{"scale", QJsonValue::Null},
{"windowMode", false},
{"disableGlosSIOverlay", false},
{"opaqueSteamOverlay", false}
}},
};
@ -471,8 +433,16 @@ QVariantMap UIModel::getDefaultConf() const
void UIModel::saveDefaultConf(QVariantMap conf) const
{
auto path = util::path::getDataDirPath();
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";
path /= "default.json";
QFile file(path);
@ -485,38 +455,6 @@ void UIModel::saveDefaultConf(QVariantMap conf) const
file.close();
}
Q_INVOKABLE QVariant UIModel::globalModeShortcutConf() {
for (auto& target : targets_) {
const auto map = target.toMap();
if (map["name"] == "GlosSI GlobalMode/Desktop") {
return target;
}
}
return QVariant();
}
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::globalModeShortcutAppId() {
if (!globalModeShortcutExists()) {
return 0;
}
return getAppId(globalModeShortcutConf());
}
Q_INVOKABLE QString UIModel::globalModeShortcutGameId() {
if (!globalModeShortcutExists()) {
return "";
}
return getGameId(globalModeShortcutConf());
}
#ifdef _WIN32
QVariantList UIModel::uwpApps() { return UWPFetch::UWPAppList(); }
#endif
@ -551,17 +489,14 @@ void UIModel::loadSteamGridImages()
std::filesystem::path path = QCoreApplication::applicationDirPath().toStdWString();
path /= "steamgrid.exe";
auto api_key = getDefaultConf().value("steamgridApiKey").toString();
steamgrid_output_.clear();
steamgrid_proc_.setProgram(path.string().c_str());
steamgrid_proc_.setArguments({"-nonsteamonly", "--onlymissingartwork", "--steamgriddb", api_key});
steamgrid_proc_.setArguments({"-nonsteamonly", "--onlymissingartwork", "--steamgriddb", steamgridb_key});
connect(&steamgrid_proc_, &QProcess::readyReadStandardOutput, this, &UIModel::onSteamGridReadReady);
steamgrid_proc_.start();
steamgrid_proc_.write("\n");
}
QString UIModel::getGridImagePath(QVariant shortcut)
QString UIModel::getGridImagePath(QVariant shortcut) const
{
if (!foundSteam()) {
return "";
@ -743,12 +678,8 @@ void UIModel::onAvailFilesResponse(QNetworkReply* reply)
void UIModel::onSteamGridReadReady()
{
const auto output = QString::fromLocal8Bit(steamgrid_proc_.readAllStandardOutput());
steamgrid_output_.push_back(output);
steamgrid_output_.push_back(QString::fromLocal8Bit(steamgrid_proc_.readAllStandardOutput()));
emit steamgridOutputChanged();
if (output.toLower().contains("token is missing or invalid")) {
steamgrid_proc_.kill();
}
}
void UIModel::writeTarget(const QJsonObject& json, const QString& name) const
@ -762,10 +693,7 @@ void UIModel::writeTarget(const QJsonObject& json, const QString& name) const
return;
}
auto json_ob = QJsonDocument(json).object();
json_ob.remove("steamgridApiKey");
file.write(QString(QJsonDocument(json_ob).toJson(QJsonDocument::Indented)).toStdString().data());
file.write(QString(QJsonDocument(json).toJson(QJsonDocument::Indented)).toStdString().data());
file.close();
}
@ -773,33 +701,20 @@ QString UIModel::getVersionString() const { return QString(version::VERSION_STR)
QString UIModel::getNewVersionName() const { return new_version_name_; }
std::filesystem::path UIModel::getSteamPath(bool tryConfig) const
std::filesystem::path UIModel::getSteamPath() const
{
QVariantMap defaultConf;
if (tryConfig) {
defaultConf = getDefaultConf();
}
try {
#ifdef _WIN32
// 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"};
if (!key.IsValid()) {
if (defaultConf.contains("steamPath") &&
QMetaType::canConvert(defaultConf["steamPath"].metaType(), QMetaType(QMetaType::QString))) {
return defaultConf["steamPath"].toString().toStdWString();
}
return "";
}
const auto res = key.GetStringValue(L"SteamPath");
return res;
}
catch (...) {
if (defaultConf.contains("steamPath") &&
QMetaType::canConvert(defaultConf["steamPath"].metaType(), QMetaType(QMetaType::QString))) {
return defaultConf["steamPath"].toString().toStdWString();
}
return "";
}
#else
@ -807,39 +722,23 @@ std::filesystem::path UIModel::getSteamPath(bool tryConfig) const
#endif
}
std::wstring UIModel::getSteamUserId(bool tryConfig) const
std::wstring UIModel::getSteamUserId() const
{
QVariantMap defaultConf;
if (tryConfig) {
defaultConf = getDefaultConf();
}
#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"};
if (!key.IsValid()) {
if (defaultConf.contains("steamUserId") &&
QMetaType::canConvert(defaultConf["steamUserId"].metaType(), QMetaType(QMetaType::QString))) {
return defaultConf["steamUserId"].toString().toStdWString();
}
return L"0";
}
const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser"));
if (res == L"0") {
qDebug() << "Steam not open?";
if (defaultConf.contains("steamUserId") &&
QMetaType::canConvert(defaultConf["steamUserId"].metaType(), QMetaType(QMetaType::QString))) {
return defaultConf["steamUserId"].toString().toStdWString();
}
}
return res;
}
catch (...) {
if (defaultConf.contains("steamUserId") &&
QMetaType::canConvert(defaultConf["steamUserId"].metaType(), QMetaType(QMetaType::QString))) {
return defaultConf["steamUserId"].toString().toStdWString();
}
return L"0";
}
#else
@ -913,35 +812,3 @@ bool UIModel::isSteamInputXboxSupportEnabled() const
}
return true;
}
void UIModel::readUnhookBytes() const
{
std::map<std::string, std::string> unhook_bytes;
for (const auto& name : UnhookUtil::UNHOOK_BYTES_ORIGINAL_22000 | std::views::keys) {
auto bytes = UnhookUtil::ReadOriginalBytes(
name,
name.starts_with("Hid")
? L"hid.dll"
: name.starts_with("Setup")
? L"setupapi.dll"
: L"Kernel32.dll"
);
unhook_bytes[name] = bytes;
}
auto path = config_path_;
path /= "unhook_bytes";
QFile file(path);
if (!file.open(QIODevice::Truncate | QIODevice::ReadWrite)) {
qDebug() << "Couldn't open file for writing: " << path;
return;
}
for (const auto& [name, bytes] : unhook_bytes) {
file.write(
QString::fromStdString(name + ":").toStdString().data()
);
file.write(bytes.data(), bytes.size());
file.write("\n");
}
file.close();
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -49,25 +49,19 @@ class UIModel : public QObject {
Q_INVOKABLE bool updateTarget(int index, QVariant shortcut);
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 uint32_t getAppId(QVariant shortcut) const;
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);
Q_INVOKABLE QVariantMap manualProps(QVariant shortcut);
Q_INVOKABLE void enableSteamInputXboxSupport();
Q_INVOKABLE bool restartSteam(const QString& steamURL = "");
Q_INVOKABLE bool restartSteam();
Q_INVOKABLE void updateCheck();
Q_INVOKABLE QVariantMap getDefaultConf() const;
Q_INVOKABLE void saveDefaultConf(QVariantMap conf) const;
Q_INVOKABLE QVariant globalModeShortcutConf();
Q_INVOKABLE bool globalModeShortcutExists();
Q_INVOKABLE uint32_t globalModeShortcutAppId();
Q_INVOKABLE QString globalModeShortcutGameId();
#ifdef _WIN32
Q_INVOKABLE QVariantList uwpApps();
@ -75,7 +69,7 @@ class UIModel : public QObject {
Q_INVOKABLE QVariantList egsGamesList() const;
Q_INVOKABLE void loadSteamGridImages();
Q_INVOKABLE QString getGridImagePath(QVariant shortcut);
Q_INVOKABLE QString getGridImagePath(QVariant shortcut) const;
[[nodiscard]] bool writeShortcutsVDF(const std::wstring& mode, const std::wstring& name,
const std::wstring& shortcutspath, bool is_admin_try = false) const;
@ -131,12 +125,10 @@ class UIModel : public QObject {
QString getVersionString() const;
QString getNewVersionName() const;
std::filesystem::path getSteamPath(bool tryConfig = true) const;
std::wstring getSteamUserId(bool tryConfig = true) const;
std::filesystem::path getSteamPath() const;
std::wstring getSteamUserId() const;
bool foundSteam() const;
void parseShortcutVDF();
bool isSteamInputXboxSupportEnabled() const;
void readUnhookBytes() const;
};

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -22,17 +22,15 @@ limitations under the License.
#include <QFile>
#include <QTextStream>
#include "../common/util.h"
#ifdef _WIN32
#include <Windows.h>
#include <VersionHelpers.h>
#include <dwmapi.h>
#include <ShlObj.h>
#pragma comment(lib, "Dwmapi.lib")
#include "ExeImageProvider.h"
#endif
#include "UIModel.h"
#include "WinEventFilter.h"
@ -92,7 +90,19 @@ void myMessageHandler(QtMsgType type, const QMessageLogContext&, const QString&
break;
}
auto path = util::path::getDataDirPath();
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);
QFile outFile(QString::fromStdWString(path) + "/glossiconfig.log");
outFile.open(QIODevice::WriteOnly | QIODevice::Append);
@ -110,7 +120,18 @@ int main(int argc, char* argv[])
#endif
if (argc < 3) {
auto path = util::path::getDataDirPath();
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);
QFile outFile(QString::fromStdWString(path) + "/glossiconfig.log");
outFile.open(QIODevice::WriteOnly);

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -29,8 +29,6 @@ CollapsiblePane {
property var shortcutInfo: ({})
readonly property bool isAppSettings: subTitle != ""
content:
Column {
spacing: 16
@ -140,7 +138,7 @@ CollapsiblePane {
RPane {
width: parent.width / 2 - 8
height: 324
height: 248
radius: 4
Material.elevation: 32
bgOpacity: 0.97
@ -261,14 +259,8 @@ CollapsiblePane {
width: 128
editable: true
value: shortcutInfo.controller.maxControllers
from: -1
from: 0
to: 4
textFromValue: function(value) {
if (value == -1) {
return qsTr("auto");
}
return Number(value);
}
onValueChanged: shortcutInfo.controller.maxControllers = value
}
RoundButton {
@ -277,12 +269,7 @@ CollapsiblePane {
helpInfoDialog.text =
qsTr("GlosSI will only provide [NUMBER] of controllers")
+ "\n"
+ 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\" ")
+ qsTr("Required to set to actually connected controller count when using \"real device IDs\" ")
helpInfoDialog.open()
}
width: 48
@ -301,7 +288,7 @@ CollapsiblePane {
}
RPane {
width: parent.width / 2 - 8
height: 324
height: 248
radius: 4
Material.elevation: 32
bgOpacity: 0.97
@ -370,76 +357,6 @@ CollapsiblePane {
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
CheckBox {
id: hideAltTabCheckbox
text: qsTr("Hide GlosSI from Windowlist (Alt+Tab)")
checked: shortcutInfo.window.hideAltTab
onCheckedChanged: shortcutInfo.window.hideAltTab = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Hide GlosSI from Windowlist (Alt+Tab)")
helpInfoDialog.text =
qsTr("Hides GlosSI from the Windowlist (Alt+Tab)")
+ "\n"
+ qsTr("You can close the GlosSI-Window via the system-tray")
+ "\n"
+ "\n"
+ qsTr("Might help with Steam remote play.")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
CheckBox {
id: disableGlosSIOverlayCheckbox
text: qsTr("Disable GlosSI overlay")
checked: shortcutInfo.window.disableGlosSIOverlay
onCheckedChanged: shortcutInfo.window.disableGlosSIOverlay = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Disable GlosSI overlay")
helpInfoDialog.text =
qsTr("Disables the additional GlosSI overlay")
+ "\n"
+ qsTr("but keeps the Steam overlay");
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -189,7 +189,7 @@ Dialog {
anchors.verticalCenter: parent.verticalCenter
spacing: 2
Label {
text: modelData.InstallLocation.split('/').pop().split('\\').pop().replace(/([a-z])([A-Z])/g, '$1 $2')
text: modelData.InstallLocation.split('/').pop().split('\\').pop()
font.pixelSize: 18
font.bold: true
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -81,25 +81,6 @@ Item {
config.snapshotNotify = checked
}
}
}
Row {
leftPadding: 12
Row {
spacing: 16
Label {
topPadding: 8
id: apiKeyLabel
font.bold: true
text: qsTr("SteamGridDB-API-Key")
}
FluentTextInput {
width: 128
id: apiKeyInput
placeholderText: qsTr("...")
text: config.steamgridApiKey
onTextChanged: config.steamgridApiKey = text
}
}
}
}
}
@ -118,85 +99,6 @@ 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: globalModeUseGamepadUI
text: qsTr("Use BPM for global-/desktop-mode")
checked: config.globalModeUseGamepadUI
onCheckedChanged: config.globalModeUseGamepadUI = checked
}
}
}
Row {
leftPadding: 12
Row {
spacing: 16
Label {
topPadding: 8
id: globalModeGameIdLabel
text: qsTr("GlobalMode GameId")
}
FluentTextInput {
width: 128
id: globalModeGameId
enabled: false
text: config.globalModeGameId
onTextChanged: config.globalModeGameId = text
}
Button {
id: globalModeGameIdButton
text: qsTr("Create global-/desktop-mode shortcut")
onClicked: {
const globalModeConf = uiModel.getDefaultConf();
globalModeConf.name = "GlosSI GlobalMode/Desktop";
globalModeConf.launch.launch = false;
uiModel.addTarget(globalModeConf);
if (uiModel.addToSteam(globalModeConf, "")) {
steamChangedDialog.open();
}
const globalModeGID = uiModel.globalModeShortcutGameId();
globalModeGameId.text = globalModeGID;
setTimeout(() => {
uiModel.saveDefaultConf(config);
done();
}, 10);
}
highlighted: true
visible: !uiModel.globalModeShortcutExists()
}
Button {
id: globalModeGameIdConfigButton
text: qsTr("Open global-/desktop-mode controller config")
onClicked: {
Qt.openUrlExternally("steam://currentcontrollerconfig/" + uiModel.globalModeShortcutAppId() + "/");
}
visible: uiModel.globalModeShortcutExists()
}
}
}
}
}
Item {
width: 1
height: 32

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -34,7 +34,7 @@ GridView {
property real margins: 16
cellWidth: 292 + 16
cellHeight: 212 + 16
cellHeight: 190 + 16
readonly property real displayedItems: Math.floor((parent.width - margins*2) / cellWidth)
width: displayedItems * cellWidth
model: uiModel.targetList;
@ -72,7 +72,7 @@ GridView {
bgOpacity: 0.3
radius: 8
width: 292
height: 212
height: 190
Material.elevation: 4
clip: true
property bool isInSteam: uiModel.isInSteam(modelData);
@ -145,73 +145,57 @@ GridView {
}
}
Column {
anchors.left: parent.left
Button {
id: steambutton
anchors.left: parent.left
anchors.bottom: parent.bottom
spacing: 4
Button {
highlighted: true
visible: delegateRoot.isInSteam
text: qsTr("Steam controller config")
onClicked: function() {
controllerConfigDialog.confirmedExtraParam = uiModel.getAppId(modelData)
controllerConfigDialog.confirmedParam = uiModel.getAppId(modelData)
controllerConfigDialog.open();
width: 72
onClicked: function(){
if (delegateRoot.isInSteam) {
if (!uiModel.removeFromSteam(modelData.name, "")) {
writeErrorDialog.open();
return;
}
} else {
if (!uiModel.addToSteam(modelData, "")) {
manualInfo = uiModel.manualProps(modelData);
writeErrorDialog.open();
return;
}
}
if (steamShortcutsChanged == false) {
steamChangedDialog.open();
}
delegateRoot.isInSteam = uiModel.isInSteam(modelData)
steamShortcutsChanged = true
}
Button {
id: steambutton
width: 72
onClicked: function(){
if (delegateRoot.isInSteam) {
if (!uiModel.removeFromSteam(modelData.name, "")) {
writeErrorDialog.open();
return;
}
} else {
if (!uiModel.addToSteam(modelData, "")) {
manualInfo = uiModel.manualProps(modelData);
writeErrorDialog.open();
return;
}
}
if (steamShortcutsChanged == false) {
steamChangedDialog.open();
}
delegateRoot.isInSteam = uiModel.isInSteam(modelData)
steamShortcutsChanged = true
highlighted: delegateRoot.isInSteam
Material.accent: Material.color(Material.Red, Material.Shade800)
Row {
anchors.centerIn: parent
spacing: 8
Label {
anchors.verticalCenter: parent.verticalCenter
text: delegateRoot.isInSteam ? "-" : "+"
font.bold: true
font.pixelSize: 24
}
highlighted: delegateRoot.isInSteam
Material.accent: Material.color(Material.Red, Material.Shade800)
Row {
anchors.centerIn: parent
spacing: 8
Label {
anchors.verticalCenter: parent.verticalCenter
text: delegateRoot.isInSteam ? "-" : "+"
font.bold: true
font.pixelSize: 24
}
Image {
anchors.verticalCenter: parent.verticalCenter
source: "qrc:/svg/steam.svg"
width: 22
height: 22
smooth: true
mipmap: true
ColorOverlay {
anchors.fill: parent
source: parent
color: "white"
}
Image {
anchors.verticalCenter: parent.verticalCenter
source: "qrc:/svg/steam.svg"
width: 22
height: 22
smooth: true
mipmap: true
ColorOverlay {
anchors.fill: parent
source: parent
color: "white"
}
}
}
}
Row {
id: buttonrow
anchors.right: parent.right

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -136,11 +136,7 @@ Item {
id: nameInput
placeholderText: qsTr("...")
text: shortcutInfo.name
onTextChanged: function() {
shortcutInfo.oldName = shortcutInfo.oldName || shortcutInfo.name
shortcutInfo.name = nameInput.text
shortcutInfo = shortcutInfo
}
onTextChanged: shortcutInfo.name = text
validator: RegularExpressionValidator { regularExpression: /([0-z]|\s|.)+/gm }
}
}
@ -378,7 +374,7 @@ Item {
id: egsSelectDialog
onConfirmed: function(modelData) {
if (nameInput.text == "") {
nameInput.text = modelData.InstallLocation.split('/').pop().split('\\').pop().replace(/([a-z])([A-Z])/g, '$1 $2')
nameInput.text = modelData.InstallLocation.split('/').pop().split('\\').pop()
}
pathInput.text = "com.epicgames.launcher://apps/"
+ modelData.NamespaceId

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -40,7 +40,6 @@ Dialog {
property real backdropOpacity: 1.0
property bool loading: true
property bool hasTokenError: false
onOpened: function() {
@ -81,55 +80,25 @@ Dialog {
font.bold: true
}
Item {
id: topContentContainer
BusyIndicator {
id: busyIndicator
running: visible
anchors.top: titlelabel.bottom
anchors.topMargin: 8
anchors.horizontalCenter: parent.horizontalCenter
anchors.left: parent.left
anchors.right: parent.right
height: loading || hasTokenError ? 72 : 0
BusyIndicator {
id: busyIndicator
running: visible
anchors.horizontalCenter: parent.horizontalCenter
opacity: loading ? 1 : 0
height: loading ? 72 : 0
Behavior on opacity {
NumberAnimation {
duration: 350
easing.type: Easing.InOutQuad
}
}
visible: loading
}
Label {
id: tokenErrorInfo
anchors.horizontalCenter: parent.horizontalCenter
opacity: hasTokenError ? 1 : 0
anchors.left: parent.left
anchors.right: parent.right
wrapMode: Text.Wrap
font.bold: true
color: "yellow"
textFormat: TextEdit.RichText
onLinkActivated: Qt.openUrlExternally(link)
Behavior on opacity {
NumberAnimation {
duration: 350
easing.type: Easing.InOutQuad
}
}
visible: hasTokenError
text: qsTr("Please go to <a href=\"https://www.steamgriddb.com/profile/preferences/api\">SteamGridDB</a> and gerenate a new API token. Then paste it into the settings dialog.")
}
opacity: loading ? 1 : 0
height: loading ? 72 : 0
Behavior on opacity {
NumberAnimation {
duration: 350
easing.type: Easing.InOutQuad
}
}
visible: loading
}
ListView {
anchors.top: topContentContainer.bottom
anchors.top: busyIndicator.bottom
anchors.topMargin: 16
anchors.bottom: parent.bottom
anchors.bottomMargin: 16
@ -141,10 +110,9 @@ Dialog {
model: uiModel.steamgridOutput
ScrollBar.vertical: ScrollBar {
}
onCountChanged: function() {
listview.positionViewAtIndex(listview.count - 1, ListView.Visible);
hasTokenError = listview.model.some((l) => l.includes("missing or invalid"));
loading = !(listview.model.some((l) => l.includes("Press enter")) || hasTokenError);
onCountChanged: {
listview.positionViewAtIndex(listview.count - 1, ListView.Visible)
loading = !listview.model[listview.count - 1].includes("Press enter")
}
Behavior on opacity {

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -266,23 +266,6 @@ Window {
}
}
InfoDialog {
id: controllerConfigDialog
titleText: qsTr("Open Steam controller config")
text: qsTr("Steams controller config can only open if a controller is connected")
+ "\n"
+ qsTr("and the shortcut is visible in Steam (Steam restarted after adding).")
buttonText: qsTr("Open")
extraButton: true
extraButtonText: qsTr("Restart and open")
onConfirmedExtra: function(data) {
uiModel.restartSteam("steam://currentcontrollerconfig/" + data + "/")
}
onConfirmed: function(data) {
Qt.openUrlExternally("steam://currentcontrollerconfig/" + data + "/");
}
}
Rectangle {
id: titleBar
visible: uiModel.isWindows
@ -492,12 +475,12 @@ Window {
if (windowContent.editedIndex < 0) {
uiModel.addTarget(shortcut)
} else {
if (uiModel.updateTarget(windowContent.editedIndex, shortcut)) {
if (uiModel.isInSteam(shortcut) && steamShortcutsChanged == false) {
steamChangedDialog.open();
}
} else {
if (uiModel.isInSteam(shortcut)) {
if (uiModel.isInSteam(shortcut)) {
if (uiModel.updateTarget(windowContent.editedIndex, shortcut)) {
if (steamShortcutsChanged == false) {
steamChangedDialog.open();
}
} else {
manualInfo = uiModel.manualProps(shortcut);
writeErrorDialog.open();
}

@ -1,37 +0,0 @@
{
"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
}

@ -5,18 +5,24 @@
"version": "0.2.0",
"configurations": [
{
"name": "Launch GlosSITarget",
"type": "cppvsdbg",
"name": "(gdb) Launch",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceFolder}/../x64/Debug/GlosSITarget.exe",
"preLaunchTask": "Build GlosSITarget (Debug)",
"args": [
"-window"
],
"program": "${workspaceFolder}/build/GlosSITarget",
"preLaunchTask": "Build",
"args": [],
"stopAtEntry": false,
"cwd": "${workspaceFolder}/../x64/Debug",
"cwd": "${workspaceFolder}/build",
"environment": [],
"externalConsole": false,
"MIMode": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}

@ -0,0 +1,3 @@
{
"C_Cpp.default.configurationProvider": "ms-vscode.cmake-tools"
}

@ -1,43 +1,15 @@
{
"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",
},
}
]
}
"version": "2.0.0",
"tasks": [
{
"type": "cmake",
"command": "build",
"label": "Build",
"group": {
"kind": "build",
"isDefault": true
},
"problemMatcher": []
}
]
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -24,18 +24,16 @@ limitations under the License.
#include <Propsys.h>
#include <propkey.h>
#include <shellapi.h>
#include <winerror.h>
#pragma comment(lib, "Shell32.lib")
#endif
#include "../common/Settings.h"
#include "Settings.h"
#include <regex>
#include "HttpServer.h"
#include "Overlay.h"
#include "../common/UnhookUtil.h"
#include "../common/util.h"
#include "UnhookUtil.h"
#include "util.h"
AppLauncher::AppLauncher(
std::vector<HWND>& process_hwnds,
@ -48,39 +46,6 @@ 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");
},
{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<std::vector<DWORD>>());
}
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)
@ -110,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", util::string::to_string(util::win::process::GetProcName(pid)).c_str(), pid);
ImGui::Text("%s | %d", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(glossi_util::GetProcName(pid)).c_str(), pid);
ImGui::SameLine();
if (ImGui::Button((" Kill ##" + std::to_string(pid)).c_str())) {
util::win::process::KillProcess(pid);
glossi_util::KillProcess(pid);
}
});
ImGui::EndChild();
@ -136,7 +101,7 @@ void AppLauncher::update()
getChildPids(pids_[0]);
}
if (!IsProcessRunning(pids_[0])) {
spdlog::info(L"Launched App \"{}\" with PID \"{}\" died", util::win::process::GetProcName(pids_[0]), pids_[0]);
spdlog::info(L"Launched App \"{}\" with PID \"{}\" died", glossi_util::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_();
@ -152,12 +117,12 @@ void AppLauncher::update()
}
const auto running = IsProcessRunning(pid);
if (!running)
spdlog::trace(L"Child process \"{}\" with PID \"{}\" died", util::win::process::GetProcName(pid), pid);
spdlog::trace(L"Child process \"{}\" with PID \"{}\" died", glossi_util::GetProcName(pid), pid);
return !running;
});
auto filtered_pids = pids_ | std::ranges::views::filter([](DWORD pid) {
return std::ranges::find(Settings::launch.launcherProcesses, util::win::process::GetProcName(pid)) == Settings::launch.launcherProcesses.end();
return std::ranges::find(Settings::launch.launcherProcesses, glossi_util::GetProcName(pid)) == Settings::launch.launcherProcesses.end();
});
if (has_extra_launchers_ && !filtered_pids.empty()) {
launcher_has_launched_game_ = true;
@ -204,7 +169,7 @@ std::vector<DWORD> AppLauncher::launchedPids()
[](DWORD pid) {
return std::ranges::find(
Settings::launch.launcherProcesses,
util::win::process::GetProcName(pid)) == Settings::launch.launcherProcesses.end();
glossi_util::GetProcName(pid)) == Settings::launch.launcherProcesses.end();
})) {
res.push_back(pid);
}
@ -252,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 \"{}\"", util::win::process::GetProcName(pe.th32ProcessID), pe.th32ProcessID);
spdlog::info(L"Found new child process \"{}\" with PID \"{}\"", glossi_util::GetProcName(pe.th32ProcessID), pe.th32ProcessID);
}
pids_.push_back(pe.th32ProcessID);
getChildPids(pe.th32ProcessID);
@ -285,11 +250,9 @@ void AppLauncher::getProcessHwnds()
IPropertyStore* propStore;
SHGetPropertyStoreForWindow(curr_wnd, IID_IPropertyStore, reinterpret_cast<void**>(&propStore));
PROPVARIANT prop;
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);
}
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);
}
@ -300,7 +263,7 @@ void AppLauncher::getProcessHwnds()
#ifdef _WIN32
bool AppLauncher::findLauncherPids()
{
if (const auto pid = util::win::process::PidByName(L"EpicGamesLauncher.exe")) {
if (const auto pid = glossi_util::PidByName(L"EpicGamesLauncher.exe")) {
spdlog::debug("Found EGS-Launcher running");
pids_.push_back(pid);
return true;
@ -332,14 +295,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));
DWORD pid;
: native_seps_path) + L" " + args)
);
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(),
@ -351,50 +311,17 @@ void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& a
nullptr, // launch_dir.empty() ? nullptr : launch_dir.data(),
&info,
&process_info)) {
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;
// 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();
}
}
spdlog::info(L"Started Program: \"{}\"; PID: {}", native_seps_path, pid);
if (!watchdog) {
pid_mutex_.lock();
pids_.push_back(pid);
pid_mutex_.unlock();
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);
}
}
@ -490,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 = util::win::process::PidByName(L"EpicGamesLauncher.exe");
const auto pid = glossi_util::PidByName(L"EpicGamesLauncher.exe");
if (!findLauncherPids()) {
spdlog::debug("Did not find EGS-Launcher not running, retrying later...");
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,80 +0,0 @@
#pragma once
#include "HttpServer.h"
#include "../common/Settings.h"
#include "../common/steam_util.h"
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,
[](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"});
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);
}
},
});
};
} // namespace CHTE

@ -1,8 +1,10 @@
#pragma once
#include <windows.h>
#include <tlhelp32.h>
#include <spdlog/spdlog.h>
#include "../common/util.h"
#include "util.h"
namespace DllInjector {
@ -110,7 +112,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 = util::win::process::PidByName(processName);
const auto explorer_pid = glossi_util::PidByName(processName);
if (explorer_pid != 0) {
if (DllInjector::TakeDebugPrivilege()) {
// No need to eject, as the dll is self-ejecting.

@ -51,7 +51,6 @@
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation />
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@ -129,9 +128,6 @@
<PreprocessorDefinitions>_DEBUG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;SUBHOOK_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -155,9 +151,6 @@
<PreprocessorDefinitions>NDEBUG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING;SUBHOOK_STATIC;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -175,8 +168,6 @@
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\common\HidHide.cpp" />
<ClCompile Include="..\common\UnhookUtil.cpp" />
<ClCompile Include="..\deps\imgui-sfml\imgui-SFML.cpp" />
<ClCompile Include="..\deps\imgui\imgui.cpp" />
<ClCompile Include="..\deps\imgui\imgui_draw.cpp" />
@ -196,6 +187,7 @@
<ClCompile Include="..\deps\traypp\tray\src\core\windows\image.cpp" />
<ClCompile Include="..\deps\traypp\tray\src\core\windows\tray.cpp" />
<ClCompile Include="AppLauncher.cpp" />
<ClCompile Include="HidHide.cpp" />
<ClCompile Include="HttpServer.cpp" />
<ClCompile Include="InputRedirector.cpp" />
<ClCompile Include="main.cpp" />
@ -203,15 +195,16 @@
<ClCompile Include="SteamOverlayDetector.cpp" />
<ClCompile Include="SteamTarget.cpp" />
<ClCompile Include="TargetWindow.cpp" />
<ClCompile Include="UnhookUtil.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="..\deps\imgui-sfml\imgui-SFML.h" />
<ClInclude Include="..\deps\imgui\imgui.h" />
<ClInclude Include="..\deps\subhook\subhook.h" />
<ClInclude Include="AppLauncher.h" />
<ClInclude Include="CommonHttpEndpoints.h" />
<ClInclude Include="DllInjector.h" />
<ClInclude Include="GlosSI_logo.h" />
<ClInclude Include="HidHide.h" />
<ClInclude Include="HttpServer.h" />
<ClInclude Include="imconfig.h" />
<ClInclude Include="InputRedirector.h" />
@ -220,10 +213,13 @@
<ClInclude Include="ProcessPriority.h" />
<ClInclude Include="resource.h" />
<ClInclude Include="Roboto.h" />
<ClInclude Include="Settings.h" />
<ClInclude Include="SteamOverlayDetector.h" />
<ClInclude Include="SteamTarget.h" />
<ClInclude Include="steam_sf_keymap.h" />
<ClInclude Include="TargetWindow.h" />
<ClInclude Include="UnhookUtil.h" />
<ClInclude Include="util.h" />
<ClInclude Include="UWPOverlayEnabler.h" />
</ItemGroup>
<ItemGroup>

@ -51,6 +51,9 @@
<ClCompile Include="InputRedirector.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HidHide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Overlay.cpp">
<Filter>Source Files</Filter>
</ClCompile>
@ -111,13 +114,10 @@
<ClCompile Include="..\deps\traypp\tray\src\components\toggle.cpp">
<Filter>Source Files\tray</Filter>
</ClCompile>
<ClCompile Include="HttpServer.cpp">
<ClCompile Include="UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\HidHide.cpp">
<ClCompile Include="HttpServer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
@ -137,6 +137,9 @@
<ClInclude Include="InputRedirector.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HidHide.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Overlay.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -158,6 +161,9 @@
<ClInclude Include="AppLauncher.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Settings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
@ -176,10 +182,13 @@
<ClInclude Include="GlosSI_logo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HttpServer.h">
<ClInclude Include="UnhookUtil.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CommonHttpEndpoints.h">
<ClInclude Include="util.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HttpServer.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -21,8 +21,6 @@ limitations under the License.
#include "HidHide.h"
#include <numeric>
#define SPDLOG_WCHAR_TO_UTF8_SUPPORT
#define SPDLOG_WCHAR_FILENAMES
#include <spdlog/spdlog.h>
#include <vector>
@ -31,7 +29,7 @@ limitations under the License.
#include <cfgmgr32.h>
#ifndef WATCHDOG
#include "../GlosSITarget/Overlay.h"
#include "Overlay.h"
#endif
#include "Settings.h"
@ -43,7 +41,7 @@ limitations under the License.
#include <atlbase.h>
#include "../common/UnhookUtil.h"
#include "UnhookUtil.h"
#pragma comment(lib, "Setupapi.lib")
@ -54,7 +52,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()
{
@ -87,7 +85,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();
@ -114,12 +112,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);
}
@ -127,7 +125,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);
@ -135,13 +133,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))) {
@ -162,7 +160,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();
@ -201,81 +199,80 @@ 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();
}
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);
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());
}
if (!device.device_instance_path.empty()) {
blacklisted_devices_.push_back(device.base_container_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);
});
}
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_);
ImGui::EndChild();
} else {
ImGui::Text("Enable \"Hide Devices\" to see a list of gaming-devices");
}
if (hidhide_active_ != Settings::devices.hideDevices) {
openCtrlDevice();
setActive(Settings::devices.hideDevices);
closeCtrlDevice();
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
@ -283,7 +280,7 @@ std::wstring HidHide::DosDeviceForVolume(const std::wstring& volume)
{
std::vector<WCHAR> buffer(UNICODE_STRING_MAX_CHARS);
QueryDosDeviceW(volume.c_str(), buffer.data(), static_cast<DWORD>(buffer.size()));
return { buffer.data() };
return {buffer.data()};
}
std::vector<std::wstring> HidHide::getAppWhiteList() const
@ -294,7 +291,7 @@ std::vector<std::wstring> HidHide::getAppWhiteList() const
}
std::vector<WCHAR> buffer(bytes_needed);
if (!DeviceIoControl(
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::GET_WHITELIST), nullptr, 0, buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) {
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::GET_WHITELIST), nullptr, 0, buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) {
spdlog::error("Couldn't retrieve HidHide Whitelist");
return std::vector<std::wstring>{};
}
@ -309,7 +306,7 @@ std::vector<std::wstring> HidHide::getBlackListDevices() const
}
std::vector<WCHAR> buffer(bytes_needed);
if (!DeviceIoControl(
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::GET_BLACKLIST), nullptr, 0, buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) {
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::GET_BLACKLIST), nullptr, 0, buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), &bytes_needed, nullptr)) {
spdlog::error("Couldn't retrieve HidHide Blacklist");
return std::vector<std::wstring>{};
}
@ -321,7 +318,7 @@ bool HidHide::getActive()
DWORD bytes_needed;
BOOLEAN res;
if (!DeviceIoControl(
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::GET_ACTIVE), nullptr, 0, &res, sizeof(BOOLEAN), &bytes_needed, nullptr)) {
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::GET_ACTIVE), nullptr, 0, &res, sizeof(BOOLEAN), &bytes_needed, nullptr)) {
spdlog::error("Couldn't retrieve HidHide State");
return false;
}
@ -334,7 +331,7 @@ void HidHide::setAppWhiteList(const std::vector<std::wstring>& whitelist) const
DWORD bytes_needed;
auto buffer = StringListToMultiString(whitelist);
if (!DeviceIoControl(
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::SET_WHITELIST), buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) {
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::SET_WHITELIST), buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) {
spdlog::error("Couldn't set HidHide WhiteList");
}
}
@ -344,7 +341,7 @@ void HidHide::setBlacklistDevices(const std::vector<std::wstring>& blacklist) co
DWORD bytes_needed;
auto buffer = StringListToMultiString(blacklist);
if (!DeviceIoControl(
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::SET_BLACKLIST), buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) {
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::SET_BLACKLIST), buffer.data(), static_cast<DWORD>(buffer.size() * sizeof(WCHAR)), nullptr, 0, &bytes_needed, nullptr)) {
spdlog::error("Couldn't set HidHide BlackList");
}
}
@ -353,7 +350,7 @@ void HidHide::setActive(bool active)
{
DWORD bytes_needed;
if (!DeviceIoControl(
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::SET_ACTIVE), &active, sizeof(BOOLEAN), nullptr, 0, &bytes_needed, nullptr)) {
hidhide_handle, static_cast<DWORD>(IOCTL_TYPE::SET_ACTIVE), &active, sizeof(BOOLEAN), nullptr, 0, &bytes_needed, nullptr)) {
spdlog::error("Couldn't set HidHide State");
return;
}
@ -397,9 +394,9 @@ std::vector<WCHAR> HidHide::StringListToMultiString(const std::vector<std::wstri
{
auto res = std::accumulate(stringlist.begin(), stringlist.end(), std::vector<WCHAR>{}, [](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;
}
@ -429,7 +426,7 @@ std::vector<HidHide::SmallHidInfo> 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{};
@ -446,7 +443,7 @@ std::vector<HidHide::SmallHidInfo> HidHide::GetHidDeviceList()
std::ranges::remove_if(
res,
[](const auto& dev) { return !dev.gaming_device; })
.begin(),
.begin(),
res.end());
return res;
@ -507,8 +504,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<ULONG>(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);
@ -569,13 +566,13 @@ std::filesystem::path HidHide::SymbolicLink(GUID const& interface_guid, DeviceIn
std::vector<BYTE> buffer(needed);
// Acquire the detailed data containing the symbolic link (aka. device path)
auto& [cbSize, DevicePath] {*reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W>(buffer.data())};
auto& [cbSize, DevicePath]{*reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W>(buffer.data())};
cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA_W);
if (!SetupDiGetDeviceInterfaceDetailW(handle.get(), &device_interface_data, reinterpret_cast<PSP_DEVICE_INTERFACE_DETAIL_DATA_W>(buffer.data()), static_cast<DWORD>(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)
@ -583,7 +580,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
@ -600,7 +597,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<DEVINSTID_W>(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 {};
@ -627,7 +624,7 @@ HidHide::DeviceInstancePath HidHide::DeviceInstancePathParent(DeviceInstancePath
DEVINST dev_inst_parent{};
std::wstring res;
res.resize(UNICODE_STRING_MAX_CHARS);
ULONG needed{ static_cast<ULONG>(res.size()) };
ULONG needed{static_cast<ULONG>(res.size())};
if (const auto result = CM_Locate_DevNodeW(&dev_inst, const_cast<DEVINSTID_W>(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 {};

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -34,7 +34,7 @@ limitations under the License.
#endif
class HidHide {
private:
private:
using DeviceInstancePath = std::wstring;
using SetupDiDestroyDeviceInfoListPtr = std::unique_ptr<std::remove_pointer_t<HDEVINFO>, decltype(&SetupDiDestroyDeviceInfoList)>;
using CloseHandlePtr = std::unique_ptr<std::remove_pointer_t<HANDLE>, decltype(&CloseHandle)>;
@ -62,7 +62,7 @@ private:
bool gaming_device = false;
};
public:
public:
HidHide();
void openCtrlDevice();
@ -72,7 +72,7 @@ public:
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 @@ private:
std::vector<std::wstring> blacklisted_devices_;
std::vector<SmallHidInfo> 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<std::wstring_view, 3> 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);

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -16,115 +16,54 @@ limitations under the License.
#include "HttpServer.h"
#include <spdlog/spdlog.h>
#include <utility>
#include <nlohmann/json.hpp>
#include <algorithm>
#include "AppLauncher.h"
#include "Settings.h"
HttpServer::HttpServer(std::function<void()> close) : close_(std::move(close))
HttpServer::HttpServer(AppLauncher& app_launcher) : app_launcher_(app_launcher)
{
}
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);
}
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) {
setCorsHeader(res);
auto content_json = nlohmann::json{
{"endpoints", nlohmann::json::array()}};
server_.Get("/launched-pids", [this](const httplib::Request& req, httplib::Response& res) {
const nlohmann::json j = app_launcher_.launchedPids();
res.set_content(j.dump(), "text/json");
});
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},
});
server_.Post("/launched-pids", [this](const httplib::Request& req, httplib::Response& res) {
try {
const nlohmann::json postbody = nlohmann::json::parse(req.body);
app_launcher_.addPids(postbody.get<std::vector<DWORD>>());
} catch (std::exception& e) {
res.status = 401;
res.set_content(nlohmann::json{
{"code", 401},
{"name", "Bad Request"},
{"message", e.what()},
}
.dump(),
"text/json");
return;
}
content_json["endpoints"].push_back(
nlohmann::json{
{"path", "/quit"},
{"method", "POST"}
});
res.set_content(content_json.dump(4),
"text/json");
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");
});
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_.*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");
}
});
}
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) {
res.set_content(Settings::toJson().dump(), "text/json");
});
server_thread_ = std::thread([this]() {

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,46 +17,21 @@ limitations under the License.
#include <thread>
#include <httplib.h>
#include <nlohmann/json.hpp>
class AppLauncher;
class HttpServer {
public:
explicit HttpServer(std::function<void()> 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<void(const httplib::Request& req, httplib::Response& res)> handler;
nlohmann::json response_hint = nullptr;
nlohmann::json payload_hint = nullptr;
};
static void AddEndpoint(const Endpoint&& e);
explicit HttpServer(AppLauncher& app_launcher);
void run();
void stop();
private:
httplib::Server server_;
std::thread server_thread_;
uint16_t port_ = 8756;
std::function<void()> close_;
static inline std::vector<Endpoint> endpoints_;
AppLauncher& app_launcher_;
};

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -20,7 +20,7 @@ limitations under the License.
#include <spdlog/spdlog.h>
#include "Overlay.h"
#include "..\common\Settings.h"
#include "Settings.h"
InputRedirector::InputRedirector()
{
@ -49,22 +49,6 @@ 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) {
@ -74,17 +58,13 @@ 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 < -1) {
countcopy = -1;
if (countcopy < 0) {
countcopy = 0;
}
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;
@ -151,12 +131,12 @@ void InputRedirector::runLoop()
unplugVigemPad(i);
}
}
if (max_controllers_ < XUSER_MAX_COUNT) {
for (int i = max_controllers_; i < XUSER_MAX_COUNT; i++) {
if (Settings::controller.maxControllers < XUSER_MAX_COUNT) {
for (int i = Settings::controller.maxControllers; i < XUSER_MAX_COUNT; i++) {
unplugVigemPad(i);
}
}
for (int i = 0; i < XUSER_MAX_COUNT && i < max_controllers_; i++) {
for (int i = 0; i < XUSER_MAX_COUNT && i < Settings::controller.maxControllers; i++) {
XINPUT_STATE state{};
if (XInputGetState(i, &state) == ERROR_SUCCESS) {
if (vt_pad_[i] != nullptr) {
@ -236,10 +216,6 @@ 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],
@ -266,7 +242,7 @@ void InputRedirector::runLoop()
unplugVigemPad(i);
}
}
Sleep(static_cast<int>(1000.f / Settings::controller.updateRate));
sf::sleep(sf::milliseconds(4));
#endif
}

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -35,7 +35,6 @@ class InputRedirector {
private:
void runLoop();
int max_controllers_ = -1;
static constexpr int start_delay_ms_ = 2000;
bool run_ = false;
int overlay_elem_id_ = -1;

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,11 +17,13 @@ limitations under the License.
#include <filesystem>
#include <utility>
#include <locale>
#include <codecvt>
#include <regex>
#include <shlobj_core.h>
#include "Roboto.h"
#include "..\common\Settings.h"
#include "Settings.h"
#include "GlosSI_logo.h"
#include "../version.hpp"
@ -50,11 +52,23 @@ Overlay::Overlay(
ImGui::SFML::UpdateFontTexture();
#ifdef _WIN32
auto config_path = util::path::getDataDirPath();
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);
config_path /= "imgui.ini";
// This assumes that char is utf8 and wchar_t is utf16, which is guaranteed on Windows.
config_file_name_ = util::string::to_string(config_path.wstring());
config_file_name_ = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(config_path.wstring());
io.IniFilename = config_file_name_.data();
#endif
@ -163,20 +177,11 @@ void Overlay::update()
const auto remain_millis = SPLASH_DURATION_S_ * 1000 - millis;
if (remain_millis <= fade_millis) {
showSplash(static_cast<float>(remain_millis) / static_cast<float>(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;
}
showLogs(0);
if (enabled_ || force_enable_) {
@ -215,17 +220,6 @@ 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_);
}
@ -244,14 +238,9 @@ 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<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn, bool force_show)
int Overlay::AddOverlayElem(const std::function<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn)
{
if (force_show) {
FORCED_OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn});
}
else {
OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn});
}
OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn});
// keep this non confusing, but longer...
const auto res = overlay_element_id_;
overlay_element_id_++;
@ -260,10 +249,7 @@ int Overlay::AddOverlayElem(const std::function<void(bool window_has_focus, ImGu
void Overlay::RemoveOverlayElem(int id)
{
if (OVERLAY_ELEMS_.contains(id))
OVERLAY_ELEMS_.erase(id);
if (FORCED_OVERLAY_ELEMS_.contains(id))
FORCED_OVERLAY_ELEMS_.erase(id);
OVERLAY_ELEMS_.erase(id);
}
void Overlay::showLogs(ImGuiID dockspace_id)

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -38,7 +38,7 @@ class Overlay {
static void Shutdown();
static void AddLog(const spdlog::details::log_msg& msg);
static int AddOverlayElem(const std::function<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn, bool force_show_ = false);
static int AddOverlayElem(const std::function<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn);
static void RemoveOverlayElem(int id);
private:
@ -71,8 +71,6 @@ class Overlay {
static inline int overlay_element_id_ = 0;
static inline std::map<int, std::function<void(bool window_has_focus, ImGuiID dockspace_id)>> OVERLAY_ELEMS_;
static inline std::map<int, std::function<void(bool window_has_focus, ImGuiID dockspace_id)>> FORCED_OVERLAY_ELEMS_;
#ifdef _WIN32
std::string config_file_name_;
#endif

@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,1,2,0068000002000
PRODUCTVERSION 0,1,2,0068000002000
FILEVERSION 0,1,0,2045006300001
PRODUCTVERSION 0,1,0,2045006300001
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-68-g0f02eca"
VALUE "FileVersion", "0.1.0.2-45-g63fdab1"
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-68-g0f02eca"
VALUE "ProductVersion", "0.1.0.2-45-g63fdab1"
END
END
BLOCK "VarFileInfo"
@ -220,41 +220,6 @@ IDI_ICON1 ICON "..\\GlosSI_Icon.ico"

@ -0,0 +1,318 @@
/*
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <fstream>
#include <regex>
#include <string>
#include <nlohmann/json.hpp>
#include <spdlog/spdlog.h>
#include <locale>
#include <codecvt>
#ifdef WIN32
#define NOMINMAX
#include <Windows.h>
#include <ShlObj.h>
#include <KnownFolders.h>
#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<std::wstring> 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;
} 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;
} 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<std::codecvt_utf8_utf16<wchar_t>>().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<std::codecvt_utf8_utf16<wchar_t>>().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);
}
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);
}
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<std::wstring>& 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 {
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<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(launch.launchPath);
json["launch"]["launchAppArgs"] = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().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["controller"]["maxControllers"] = controller.maxControllers;
json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig;
json["controller"]["emulateDS4"] = controller.emulateDS4;
json["extendedLogging"] = common.extendedLogging;
json["name"] = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(common.name);
json["icon"] = std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().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

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -17,7 +17,7 @@ limitations under the License.
#include <spdlog/spdlog.h>
#include "..\common\Settings.h"
#include "Settings.h"
#ifdef _WIN32
#define NOMINMAX

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -15,26 +15,27 @@ limitations under the License.
*/
#include "SteamTarget.h"
#include "../common/Settings.h"
#include "Settings.h"
#include "steam_sf_keymap.h"
#include <SFML/Window/Keyboard.hpp>
#include <WinReg/WinReg.hpp>
#include <numeric>
#include <regex>
#include <spdlog/spdlog.h>
#include <vdf_parser.hpp>
#ifdef _WIN32
#include "UWPOverlayEnabler.h"
#include <tray.hpp>
#endif
#include <CEFInject.h>
#include "CommonHttpEndpoints.h"
SteamTarget::SteamTarget()
: window_(
[this] { run_ = false; },
[this] { toggleGlossiOverlay(); },
util::steam::getScreenshotHotkey(steam_path_, steam_user_id_),
getScreenshotHotkey(),
[this]() {
target_window_handle_ = window_.getSystemHandle();
overlay_ = window_.getOverlay();
@ -45,160 +46,93 @@ SteamTarget::SteamTarget()
delayed_shutdown_ = true;
delay_shutdown_clock_.restart();
}),
server_([this] { run_ = false; })
server_(launcher_)
{
target_window_handle_ = window_.getSystemHandle();
#ifdef _WIN32
if (Settings::common.no_uwp_overlay) {
UWPOverlayEnabler::AddUwpOverlayOvWidget();
}
else {
UWPOverlayEnabler::EnableUwpOverlay();
}
#endif
}
int SteamTarget::run()
{
run_ = true;
auto closeBPM = false;
auto closeBPMTimer = sf::Clock{};
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!");
}
auto steam_tweaks = CEFInject::SteamTweaks();
steam_tweaks.setAutoInject(true);
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;
}
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;
CHTE::addEndpoints();
#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";
server_.run();
DllInjector::injectDllInto(watchDogPath, L"explorer.exe");
}
#endif
}
getXBCRebindingEnabled();
run_ = true;
if (!overlay_.expired())
overlay_.lock()->setEnabled(false);
std::vector<std::function<void()>> end_frame_callbacks;
if (!CEFInject::CEFDebugAvailable()) {
auto overlay_id = std::make_shared<int>(-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 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")) {
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<int>(-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 (!SteamOverlayDetector::IsSteamInjected() && Settings::common.allowGlobalMode) {
auto overlay_id = std::make_shared<int>(-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();
#ifdef _WIN32
hidhide_.hideDevices(steam_path_);
input_redirector_.run();
#endif
if (Settings::launch.launch) {
launcher_.launchApp(Settings::launch.launchPath, Settings::launch.launchAppArgs);
}
if (!util::steam::getXBCRebindingEnabled(steam_path_, steam_user_id_)) {
auto overlay_id = std::make_shared<int>(-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);
});
}
}
ImGui::End();
},
true);
can_fully_initialize_ = false;
}
const auto tray = createTrayMenu();
bool delayed_full_init_1_frame = false;
sf::Clock frame_time_clock;
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;
}});
server_.run();
while (run_) {
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();
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_) {
if (delay_shutdown_clock_.getElapsedTime().asSeconds() >= 3) {
@ -206,30 +140,17 @@ int SteamTarget::run()
}
}
else {
if (fully_initialized_) {
launcher_.update();
}
launcher_.update();
}
for (auto& efc : end_frame_callbacks) {
efc();
}
end_frame_callbacks.clear();
frame_time_clock.restart();
}
tray->exit();
tray.exit();
server_.stop();
if (fully_initialized_) {
#ifdef _WIN32
input_redirector_.stop();
hidhide_.disableHidHide();
input_redirector_.stop();
hidhide_.disableHidHide();
#endif
launcher_.close();
if (cef_tweaks_enabled_) {
steam_tweaks_.uninstallTweaks();
}
}
launcher_.close();
return 0;
}
@ -238,17 +159,11 @@ 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_) {
@ -265,9 +180,6 @@ void SteamTarget::onOverlayChanged(bool overlay_open)
void SteamTarget::toggleGlossiOverlay()
{
if (Settings::window.disableGlosSIOverlay) {
return;
}
if (overlay_.expired()) {
return;
}
@ -326,110 +238,155 @@ void SteamTarget::focusWindow(WindowHandle hndl)
#endif
}
void SteamTarget::init_FuckingRenameMe()
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 L"";
#else
return L""; // TODO
#endif
}
std::wstring SteamTarget::getSteamUserId() const
{
if (!SteamOverlayDetector::IsSteamInjected()) {
if (Settings::common.allowGlobalMode) {
spdlog::warn("GlosSI not launched via Steam.\nEnabling EXPERIMENTAL global controller and overlay...");
if (Settings::common.globalModeGameId == L"") {
spdlog::error("No game id set for global mode. Controller will use desktop-config!");
}
#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 L"";
#else
return L""; // TODO
#endif
}
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.globalModeUseGamepadUI ? L"1" : L"0");
// SetEnvironmentVariable(L"SteamTenfootHybrid", L"1");
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.globalModeUseGamepadUI) {
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);
}
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());
// 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());
}
std::vector<std::string> 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);
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;
}
std::shared_ptr<tyti::vdf::basic_object<char>> 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
}
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;
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
}
#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";
std::vector<std::string> 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;
}
DllInjector::injectDllInto(watchDogPath, L"explorer.exe");
std::vector<std::string> 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);
if (Settings::common.no_uwp_overlay) {
UWPOverlayEnabler::AddUwpOverlayOvWidget();
std::shared_ptr<tyti::vdf::basic_object<char>> 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
}
else {
UWPOverlayEnabler::EnableUwpOverlay();
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
}
hidhide_.hideDevices(steam_path_);
input_redirector_.run();
#endif
if (Settings::launch.launch) {
launcher_.launchApp(Settings::launch.launchPath, Settings::launch.launchAppArgs);
std::vector<std::string> res;
for (auto i = 1; i < m.size(); i++) {
const auto s = std::string(m[i]);
if (!s.empty()) {
res.push_back(s);
}
}
keepControllerConfig(true);
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;
}
if (cef_tweaks_enabled_) {
steam_tweaks_.setAutoInject(true);
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);
fully_initialized_ = true;
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"
* Detour function always returns HWND of own application window
* Deatour 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)
@ -449,7 +406,6 @@ void SteamTarget::keepControllerConfig(bool keep)
spdlog::error("Couldn't un-install GetForegroundWindow hook!");
}
}
#endif
}
@ -481,34 +437,6 @@ HWND SteamTarget::keepFgWindowHookFn()
}
#endif
std::unique_ptr<Tray::Tray> 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<Tray::Tray>("GlosSITarget", icon);
#else
auto tray = std::make_unique<Tray::Tray>("GlosSITarget", "ico.png");
#endif
tray->addEntry(Tray::Button{
"Quit", [this, &tray]() {
run_ = false;
}});
return tray;
}
void SteamTarget::overlayHotkeyWorkaround()
{
static bool pressed = false;

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -21,23 +21,18 @@ limitations under the License.
#include "TargetWindow.h"
#ifdef _WIN32
#include "../common/HidHide.h"
#include "HidHide.h"
#include "InputRedirector.h"
#include <subhook.h>
#endif
#include <filesystem>
#include "AppLauncher.h"
#include "CEFInject.h"
#include "Overlay.h"
#include "HttpServer.h"
#include "../common/steam_util.h"
namespace Tray {
class Tray;
}
#include <filesystem>
class SteamTarget {
public:
explicit SteamTarget();
@ -47,16 +42,17 @@ 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_ = util::steam::getSteamPath();
std::wstring steam_user_id_ = util::steam::getSteamUserId();
std::filesystem::path steam_path_ = getSteamPath();
std::wstring steam_user_id_ = getSteamUserId();
std::vector<std::string> getOverlayHotkey();
std::vector<std::string> getScreenshotHotkey();
bool getXBCRebindingEnabled();
CEFInject::SteamTweaks steam_tweaks_;
bool cef_tweaks_enabled_ = true;
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;
@ -69,8 +65,6 @@ class SteamTarget {
static inline HWND last_real_hwnd_ = nullptr;
#endif
std::unique_ptr<Tray::Tray> createTrayMenu();
/*
* Run once per frame
* detects steam configured overlay hotkey, and simulates key presses to window
@ -80,7 +74,7 @@ class SteamTarget {
void overlayHotkeyWorkaround();
bool run_ = false;
std::vector<std::string> overlay_hotkey_ = util::steam::getOverlayHotkey(steam_path_, steam_user_id_);
std::vector<std::string> overlay_hotkey_ = getOverlayHotkey();
#ifdef _WIN32
HidHide hidhide_;
@ -95,9 +89,14 @@ class SteamTarget {
static inline WindowHandle target_window_handle_ = nullptr;
sf::Clock overlay_trigger_clock_;
float overlay_trigger_max_seconds_ = 2.5;
uint32_t overlay_trigger_max_seconds_ = 1;
bool overlay_trigger_flag_ = false;
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 ";
};

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -30,7 +30,7 @@ limitations under the License.
#include "ProcessPriority.h"
#include "..\common\Settings.h"
#include "Settings.h"
#if !defined(WM_DPICHANGED)
#define WM_DPICHANGED 0x02E0
@ -56,11 +56,6 @@ TargetWindow::TargetWindow(
if (ImGui::Checkbox("Window mode", &Settings::window.windowMode)) {
toggle_window_mode_after_frame_ = true;
}
#ifdef _WIN32
if (ImGui::Checkbox("Hide from Alt+Tab", &Settings::window.hideAltTab)) {
toggle_hidealttab_after_frame_ = true;
}
#endif
ImGui::Text("Max. FPS");
ImGui::SameLine();
int max_fps_copy = Settings::window.maxFps;
@ -73,7 +68,7 @@ TargetWindow::TargetWindow(
}
if (Settings::window.maxFps < 15 && Settings::window.maxFps > 0) {
Settings::window.maxFps = 0;
setFpsLimit(TargetWindow::calcAutoRefreshRate(screen_refresh_rate_));
setFpsLimit(screen_refresh_rate_);
} else {
setFpsLimit(Settings::window.maxFps);
}
@ -121,69 +116,17 @@ void TargetWindow::setClickThrough(bool click_through)
}
#ifdef _WIN32
HWND hwnd = window_.getSystemHandle();
// hiding GlosSI from Alt-Tab list
// https://learn.microsoft.com/en-us/windows/win32/winmsg/extended-window-styles
if (Settings::window.hideAltTab) {
toggle_hidealttab_after_frame_ = false;
if (click_through) {
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_COMPOSITED | WS_EX_TOOLWINDOW);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
else {
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_COMPOSITED | WS_EX_TOOLWINDOW);
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
if (click_through) {
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_COMPOSITED);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
else {
if (click_through) {
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_TRANSPARENT | WS_EX_TOPMOST | WS_EX_COMPOSITED);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
else {
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_COMPOSITED);
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
SetWindowLong(hwnd, GWL_EXSTYLE, WS_EX_LAYERED | WS_EX_COMPOSITED);
SetWindowPos(hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
#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{};
@ -199,15 +142,9 @@ void TargetWindow::update()
screenShotWorkaround();
overlay_->update();
window_.display();
#ifdef _WIN32
if (toggle_hidealttab_after_frame_) {
toggle_hidealttab_after_frame_ = false;
}
#endif
if (toggle_window_mode_after_frame_) {
createWindow();
}
// As SFML screws us out of most windows-events, just poll resolution every once in a while
// If changed, recreate window.
// Fixes Blackscreen issues when user does funky stuff and still uses GlosSI in non windowed mod...
@ -342,18 +279,6 @@ 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;
@ -399,7 +324,21 @@ void TargetWindow::createWindow()
auto dpi = GetWindowDPI(hwnd);
spdlog::debug("Screen DPI: {}", dpi);
setTransparent(true);
//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);
DEVMODE dev_mode = {};
dev_mode.dmSize = sizeof(DEVMODE);
@ -411,14 +350,13 @@ void TargetWindow::createWindow()
screen_refresh_rate_ = 60;
}
else {
setFpsLimit(TargetWindow::calcAutoRefreshRate(dev_mode.dmDisplayFrequency));
setFpsLimit(dev_mode.dmDisplayFrequency);
screen_refresh_rate_ = dev_mode.dmDisplayFrequency;
}
overlay_ = std::make_shared<Overlay>(
window_, [this]() { close(); }, toggle_overlay_state_, Settings::window.windowMode);
spdlog::debug("auto screen Scale: {}", dpi/96.f);
ImGuiIO& io = ImGui::GetIO();
io.FontGlobalScale = dpi / 96.f;
ImGui::SFML::UpdateFontTexture();
@ -432,13 +370,12 @@ void TargetWindow::createWindow()
setFpsLimit(Settings::window.maxFps);
}
if (Settings::window.scale > 0.3f) { // Now that's just getting ridicoulus
spdlog::debug("setting screen scale by config: {}", Settings::window.scale);
ImGuiIO& io = ImGui::GetIO();
io.FontGlobalScale = Settings::window.scale;
ImGui::SFML::UpdateFontTexture();
}
else {
spdlog::debug("Not applying too low screen scale setting");
spdlog::warn("Not applying too low screen scale setting");
}
// window_.setSize({desktop_mode.width - 1, desktop_mode.height - 1 });

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -39,7 +39,6 @@ class TargetWindow {
void setFpsLimit(unsigned int fps_limit);
void setClickThrough(bool click_through);
void setTransparent(bool transparent) const;
void update();
void close();
@ -80,9 +79,7 @@ class TargetWindow {
std::shared_ptr<Overlay> overlay_;
static unsigned int calcAutoRefreshRate(unsigned int rate);
void createWindow();
bool toggle_window_mode_after_frame_ = false;
bool toggle_hidealttab_after_frame_ = false;
};

@ -5,6 +5,7 @@
#include "DllInjector.h"
#include "Overlay.h"
#include "util.h"
namespace UWPOverlayEnabler {

@ -0,0 +1,55 @@
/*
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "UnhookUtil.h"
#include <spdlog/spdlog.h>
#include "Settings.h"
void UnhookUtil::UnPatchHook(const std::string& name, HMODULE module)
{
spdlog::trace("Patching \"{}\"...", name);
BYTE* address = reinterpret_cast<BYTE*>(GetProcAddress(module, name.c_str()));
if (!address) {
spdlog::error("failed to unpatch \"{}\"", name);
}
std::string bytes;
if (Settings::isWin10 && UNHOOK_BYTES_ORIGINAL_WIN10.contains(name)) {
bytes = UNHOOK_BYTES_ORIGINAL_WIN10.at(name);
}
else {
bytes = UNHOOK_BYTES_ORIGINAL_22000.at(name);
}
DWORD dw_old_protect, dw_bkup;
const auto len = bytes.size();
if (!VirtualProtect(address, len, PAGE_EXECUTE_READWRITE, &dw_old_protect)) { // Change permissions of memory..
spdlog::error("Couldn't change permissions of memory for \"{}\"", name);
return;
}
const auto opcode = *(address);
if (!std::ranges::any_of(JUMP_INSTR_OPCODES, [&opcode](const auto& op) { return op == opcode; })) {
spdlog::debug("\"{}\" Doesn't appear to be hooked, skipping!", name);
}
else {
for (DWORD i = 0; i < len; i++) // unpatch Valve's hook
{
*(address + i) = bytes[i];
}
spdlog::trace("Unpatched \"{}\"", name);
}
VirtualProtect(address, len, dw_old_protect, &dw_bkup); // Revert permission change...
}

@ -0,0 +1,61 @@
/*
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#define NOMINMAX
#include <map>
#include <Windows.h>
#include <string>
#include <vector>
namespace UnhookUtil {
void UnPatchHook(const std::string& name, HMODULE module);
static inline const std::vector<uint8_t> 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...
// 22000 ^= Windows build number
static inline const std::map<std::string, std::string> 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<std::string, std::string> UNHOOK_BYTES_ORIGINAL_WIN10 = {
{"SetupDiEnumDeviceInfo", "\x40\x53\x56\x57\x41\x54\x41\x55"},
{"SetupDiGetClassDevsW", "\x48\x8B\xC4\x48\x89\x58\x08"},
};
} // namespace UnhookUtil

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -30,7 +30,7 @@ limitations under the License.
#include "SteamTarget.h"
#include "OverlayLogSink.h"
#include "..\common\Settings.h"
#include "Settings.h"
#include <iostream>
#include "../version.hpp"
@ -71,7 +71,19 @@ LONG Win32FaultHandler(struct _EXCEPTION_POINTERS* ExInfo)
MINIDUMP_EXCEPTION_INFORMATION M;
HANDLE hDump_File;
auto path = util::path::getDataDirPath();
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);
path /= "glossitarget.dmp";
M.ThreadId = GetCurrentThreadId();
@ -115,7 +127,19 @@ int main(int argc, char* argv[])
const auto console_sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
console_sink->set_level(spdlog::level::trace);
#ifdef _WIN32
auto path = util::path::getDataDirPath();
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);
path /= "glossitarget.log";
// For "path.wstring()" to be usable here, SPDLOG_WCHAR_FILENAMES must be defined.
const auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(path.wstring(), true);
@ -144,9 +168,8 @@ int main(int argc, char* argv[])
auto existingwindow = FindWindowA(nullptr, "GlosSITarget");
if (existingwindow) {
spdlog::error("GlosSITarget is already running! Closing old process...");
httplib::Client client("http://localhost:8756");
client.Post("/quit");
spdlog::error("GlosSITarget is already running!");
return 1;
}
int numArgs;

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -0,0 +1,76 @@
/*
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <tlhelp32.h>
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

@ -1,41 +0,0 @@
{
"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",
},
}
]
}

@ -71,12 +71,10 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;..\CEFInjectLib;$(IncludePath)</IncludePath>
<LibraryPath>..\x64\Debug;$(LibraryPath)</LibraryPath>
<IncludePath>..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;..\CEFInjectLib;$(IncludePath)</IncludePath>
<LibraryPath>..\x64\Release;$(LibraryPath)</LibraryPath>
<IncludePath>..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@ -113,15 +111,11 @@
<PreprocessorDefinitions>_DEBUG;_WINDOWS;%(PreprocessorDefinitions);WATCHDOG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>hid.lib;Cfgmgr32.lib;setupapi.lib;CefInjectLib.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>hid.lib;Cfgmgr32.lib;setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<CustomBuildStep>
<Command>powershell.exe $(SolutionDir)version_help.ps1</Command>
@ -137,16 +131,13 @@
<PreprocessorDefinitions>NDEBUG;_WINDOWS;%(PreprocessorDefinitions);WATCHDOG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>hid.lib;Cfgmgr32.lib;setupapi.lib;CefInjectLib.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>hid.lib;Cfgmgr32.lib;setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<CustomBuildStep>
<Outputs>Upading version based on git;%(Outputs)</Outputs>
@ -154,8 +145,8 @@
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\common\HidHide.cpp" />
<ClCompile Include="..\common\UnhookUtil.cpp" />
<ClCompile Include="..\GlosSITarget\HidHide.cpp" />
<ClCompile Include="..\GlosSITarget\UnhookUtil.cpp" />
<ClCompile Include="dllmain.cpp" />
</ItemGroup>
<ItemGroup>

@ -18,10 +18,10 @@
<ClCompile Include="dllmain.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\HidHide.cpp">
<ClCompile Include="..\GlosSITarget\HidHide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\UnhookUtil.cpp">
<ClCompile Include="..\GlosSITarget\UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -15,9 +15,10 @@ limitations under the License.
*/
#include <httplib.h>
#include "../common/util.h"
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <ShlObj.h>
#include <filesystem>
@ -27,10 +28,10 @@ limitations under the License.
#include <nlohmann/json.hpp>
#include "../version.hpp"
#include "../common/Settings.h"
#include "../common/HidHide.h"
#include "../GlosSITarget/Settings.h"
#include "../GlosSITarget/HidHide.h"
#include "../GlosSITarget/util.h"
bool IsProcessRunning(DWORD pid)
{
@ -65,7 +66,20 @@ void fetchSettings(httplib::Client& http_client, int retried_count = 0) {
DWORD WINAPI watchdog(HMODULE hModule)
{
auto configDirPath = util::path::getDataDirPath();
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 logPath = configDirPath;
logPath /= "GlosSIWatchdog.log";
const auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logPath.wstring(), true);
@ -129,7 +143,7 @@ DWORD WINAPI watchdog(HMODULE hModule)
}
if (IsProcessRunning(pid))
{
util::win::process::KillProcess(pid);
glossi_util::KillProcess(pid);
}
else
{

@ -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-68-g0f02eca"
!define VERSION "0.0.9.1-48-geb4ae9c"
!define COPYRIGHT "Peter Repukat - FlatspotSoftware © 2017-2022"
!define DESCRIPTION "SteamInput compatibility tool"
!define INSTALLER_NAME "GlosSI-Installer.exe"
@ -193,55 +193,3 @@ SectionEnd

@ -12,44 +12,6 @@ The primary use case of GlosSI is to use SteamInput (required for SteamControlle
GlosSI can, but isn't required to, launch any of your favorite games or applications and directly add them to Steam, be it Win32 or Windows Store (UWP)!
It is **the tool** to enjoy any game that has trouble with Steam and/or *add extra functionality* to your Steam-Input needs
---
```
```
# ViGEm End of Life
As you may or may not have already noticed ViGEm, a substantial part in making GlosSI work it's magic is End of Life.
You can read the announcement [here](https://docs.nefarius.at/projects/ViGEm/End-of-Life/)
As I don't think holding on to deprecated dependencies is a good way of moving forward, this effectively kills GlosSI as well since without ViGEm and HidHide GlosSI cannot function.
There will be a last deprecated version of ViGEm circumventing an issue stated in their announcement.
GlosSI will be updated a last time as well, providing this version bundled with it.
The GlosSI website will be taken down, however, you can then fin the last release, here on GitHub
### GlosSI won't be taken down or magically stop working, nor will it be unsafe to use. Just a maintenance stop.
---
I also want to take the opportunity to give a MASSIVE shoutout to @Nefarius , the creator of ViGEm, HidHide and many other awesome tools
Back when good old GloSC was just a cobbled together PoC using parts of the very old ScpToolkit he has been massively helpful and even shared ViGEm with me way before it was ready to be run on any machine that doesn't belong to a wizard (or should I say sorcerer?) like him.
---
Will GlosSI continue?
Probably. But most likely not in its current form.
@Nefarius wizardry is continuing and a successor to ViGEm is being worked on, but nothing has been publicly released, yet.
As I severely lack the time to properly maintain a project like GlosSI (as you probably have already noticed, I'm sure), I'm quite fond of the idea of rebooting the project yet again, once ViGEms successor becomes available.
However, I'm not sure if I find the time and motivation again to continue with GlosSI
Until I can (and want to) get my hands on it, the future is unsure...
I'll be back to silence for now then.
Thanks for all the support, it was a blast!
```
```
---
## How does it work? / What does it do?
@ -105,7 +67,7 @@ For Building instructions refer to [BUILDING.md](./docs/BUILDING.md)
## License
```license
Copyright 2017-2023 Peter Repukat - FlatspotSoftware
Copyright 2017-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

@ -1,187 +0,0 @@
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: {
},
};

@ -1,4 +0,0 @@
node_modules/
dist/
tsconfig.tsbuildinfo
.rollup.tscache/

@ -1,10 +0,0 @@
{
"configurations": [
{
"name": "Attach to Steam CEF",
"port": 8080,
"request": "attach",
"type": "chrome",
}
]
}

File diff suppressed because it is too large Load Diff

@ -1,28 +0,0 @@
{
"name": "glossi_steamtweaks",
"version": "0.0.0",
"type": "module",
"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: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",
"devDependencies": {
"@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",
"rimraf": "^4.1.2"
},
"dependencies": {
}
}

@ -1,46 +0,0 @@
import typescript from '@rollup/plugin-typescript';
import { readdirSync, lstatSync } from 'fs';
import path from 'path';
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',
// name: 'GlosSITweaks' // don't use name; don't pollute global namespace
},
plugins: [tsPluginConf],
},
...getFileListForDir('src/Tweaks').map((file) => {
return {
input: file,
output: {
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],
}
})
];

@ -1,44 +0,0 @@
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;
};
}

@ -1,26 +0,0 @@
export interface SteamClient {
Settings: {
// 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<SteamUiMode>;
SetUiMode: (mode: SteamUiMode) => void;
};
Window: {
Minimize();
HideWindow();
};
}
export type FullSteamClient = Required<SteamClient>;
declare global {
interface Window {
SteamClient: SteamClient;
}
// eslint-disable-next-line
declare const SteamClient: SteamClient;
}

@ -1,157 +0,0 @@
import type { SteamConfig } from './common/util/types';
import { fetchWithTimeout } from './common/util/util';
import type { GlosSISettings } from './@types/GlosSISettings';
class SteamTargetApi {
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);
}
// 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(
() => {
SteamTargetApi.GlosSIActive = true;
return true;
}
).catch(() => {
SteamTargetApi.GlosSIActive = false;
return false;
});
}
public getSteamSettings(): Promise<SteamConfig> {
return fetch('http://localhost:8756/steam_settings')
.then(
(res) => res.json().then(
(json) => (json as SteamConfig).UserLocalConfigStore as SteamConfig
)
);
}
public getGlosSISettings() {
return fetch('http://localhost:8756/settings')
.then(
(res) => res.json().then(
(json) => json as GlosSISettings
)
);
}
}
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<typeof GlosSIApiCtor>;
}
// eslint-disable-next-line
const GlosSIApi: InstanceType<typeof GlosSIApiCtor>;
// eslint-disable-next-line
const GlosSITweaks: GlosSITweaks;
}
const installGlosSIApi = () => {
window.GlosSITweaks = {
GlosSI: {
install: () => {
const api = new GlosSIApiCtor();
Object.assign(window, { GlosSIApi: api });
},
uninstall: () => {
Object.entries(window.GlosSITweaks)
.filter(([tweakName]) => (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;
}
}
};
window.GlosSITweaks.GlosSI.install();
const glossiCheckInterval = setInterval(() => {
if (window.GlosSIApi) {
void 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;

@ -1,21 +0,0 @@
import { initTweak } from '../../common/tweakApi';
initTweak('MinimizeSteamGamepadUI', async () => {
const [isGamepadUI, minimizeGPUI] = await Promise.all([
// (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) {
SteamClient.Window.Minimize();
return true;
}
if (!isGamepadUI && minimizeGPUI) {
GlosSIApi.SteamTarget.log('warn', 'MinimizeSteamGamepadUI is enabled but Steam is not in GamepadUI mode');
}
return false;
}).then((minimized: boolean) => {
GlosSIApi.SteamTarget.log('debug', 'MinimizeSteamGamepadUI installed; Minimized GamepadUI:', minimized);
}).catch((e) =>GlosSIApi.SteamTarget.log('error', 'MinimizeSteamGamepadUI failed to install', e));

@ -1,33 +0,0 @@
import type { SteamConfig } from '../../../common/util/types';
import { initTweak } from '../../../common/tweakApi';
const backup: { originalFpsCorner?: number } = {};
initTweak('HideFPSCounter', {
install: async () => {
backup.originalFpsCorner = Number(
((await GlosSIApi.SteamTarget.getSteamSettings()).system as SteamConfig)
.InGameOverlayShowFPSCorner
) as 0 | 1 | 2 | 3 | 4;
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: () => {
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);
}, 10 * 1000);
}
}).then(() => {
GlosSIApi.SteamTarget.log('debug', 'HideFPSCounter installed');
}).catch((e) => GlosSIApi.SteamTarget.log('error', 'HideFPSCounter failed to install', e));

@ -1,8 +0,0 @@
// eslint-disable-next-line no-shadow
export enum SteamUiMode {
Desktop = 0,
Unknown1 = 1,
Unknown2 = 2,
Unknown3 = 3,
GamepadUI = 4,
}

@ -1,29 +0,0 @@
export const initTweak = <T>(name: string, tweakMain: (() => T)|{
install: () => T;
uninstall: () => void;
}, force = false): T => {
if (!force && window.GlosSITweaks[name]) {
throw new Error(`Tweak ${name} is already installed!`);
}
if (typeof tweakMain === 'object') {
window.GlosSITweaks[name] = { install: tweakMain.install, uninstall: () => {
try {
tweakMain.uninstall();
} catch (e) {
GlosSIApi.SteamTarget.log('error', e);
}
delete window.GlosSITweaks[name];
} };
} else {
window.GlosSITweaks[name] = { install: tweakMain };
}
try {
return window.GlosSITweaks[name].install() as T;
} catch (e) {
GlosSIApi.SteamTarget.log('error', e);
throw e;
}
};

@ -1,3 +0,0 @@
export interface SteamConfig {
[key: string]: string|SteamConfig;
}

@ -1,12 +0,0 @@
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;
};

@ -1,26 +0,0 @@
{
"compilerOptions": {
"target": "ES2020",
"module": "ES2022",
"noImplicitAny": true,
"rootDir": "./src",
"outDir": "./dist",
"inlineSourceMap": true,
"esModuleInterop": true,
"strict": true,
"skipLibCheck": true,
"useDefineForClassFields": true,
"forceConsistentCasingInFileNames": true,
"incremental": true,
"lib": [
"esnext",
"DOM"
],
},
"include": [
"src/**/*"
],
"exclude": [
"node_modules",
]
}

@ -1,41 +0,0 @@
{
"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",
},
}
]
}

@ -127,10 +127,6 @@
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<EnableParallelCodeGeneration>true</EnableParallelCodeGeneration>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
@ -149,9 +145,6 @@
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp20</LanguageStandard>
<EnableEnhancedInstructionSet>StreamingSIMDExtensions2</EnableEnhancedInstructionSet>
<FloatingPointModel>Fast</FloatingPointModel>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>

@ -1,5 +1,5 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
@ -44,8 +44,7 @@ There are two (known to me, at time of writing) ways to get a working overlay fo
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include "../common/util.h"
#include <ShlObj.h>
#define SUBHOOK_STATIC
#include <atomic>
@ -142,7 +141,22 @@ BOOL APIENTRY DllMain( HMODULE hModule,
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
auto configDirPath = util::path::getDataDirPath();
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 logPath = configDirPath;
logPath /= "UWPOverlayEnabler.log";
const auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logPath.string(), true);

@ -1,38 +1,8 @@
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 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_LIB|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v142</PlatformToolset>
+ <PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_DLL|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
- <PlatformToolset>v142</PlatformToolset>
+ <PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_LIB|Win32'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v142</PlatformToolset>
+ <PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release_DLL|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
- <PlatformToolset>v142</PlatformToolset>
+ <PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
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 @@
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_LIB|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>

@ -5,10 +5,6 @@ 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/
@ -30,8 +26,6 @@ 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 *

@ -19,7 +19,6 @@ 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"

@ -1,33 +0,0 @@
{
"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
}

@ -1,379 +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 SPDLOG_WCHAR_TO_UTF8_SUPPORT
#define SPDLOG_WCHAR_FILENAMES
#include <spdlog/spdlog.h>
#include <fstream>
#include <regex>
#include <string>
#include <nlohmann/json.hpp>
#ifdef WIN32
#define NOMINMAX
#include <Windows.h>
#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<std::wstring> 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;
bool opaqueSteamOverlay = false;
} window;
inline struct Controller
{
int maxControllers = -1;
bool allowDesktopConfig = false;
bool emulateDS4 = false;
unsigned int updateRate = 144;
} 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;
bool minimizeSteamGamepadUI = true;
} common;
inline const std::map<std::wstring, std::function<void()>> 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);
}
}
#endif
inline void Parse(const nlohmann::basic_json<> &json)
{
constexpr auto safeParseValue = []<typename T>(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<T, std::wstring>)
{
value = util::string::to_wstring(object[key].get<std::string>());
}
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);
safeParseValue(winconf, "opaqueSteamOverlay", window.opaqueSteamOverlay);
}
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(controllerConf, "updateRate", controller.updateRate);
}
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);
safeParseValue(json, "minimizeSteamGamepadUI", common.minimizeSteamGamepadUI);
}
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<std::wstring> &args)
{
std::wstring configName;
std::vector<std::function<void()>> 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["window"]["opaqueSteamOverlay"] = window.opaqueSteamOverlay;
json["controller"]["maxControllers"] = controller.maxControllers;
json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig;
json["controller"]["emulateDS4"] = controller.emulateDS4;
json["controller"]["updateRate"] = controller.updateRate;
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;
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

@ -1,123 +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.
*/
#include "../common/UnhookUtil.h"
#include "util.h"
#ifndef CONFIGAPP
#define SPDLOG_WCHAR_TO_UTF8_SUPPORT
#define SPDLOG_WCHAR_FILENAMES
#include <spdlog/spdlog.h>
#include "Settings.h"
#endif
void UnhookUtil::UnPatchHook(const std::string& name, HMODULE module)
{
#ifndef CONFIGAPP
std::map<std::string, std::string> original_bytes_from_file;
auto configDirPath = util::path::getDataDirPath();
if (std::filesystem::exists(configDirPath)) {
auto unhook_file_path = configDirPath / "unhook_bytes";
if (std::filesystem::exists(unhook_file_path)) {
std::ifstream ifile;
ifile.open(unhook_file_path, std::ios::binary | std::ios::in);
if (ifile.is_open()) {
std::string funcName;
char buff;
do {
if (ifile.eof()) {
break;
}
ifile.read(&buff, sizeof(char));
if (buff != ':') {
funcName.push_back(buff);
}
else {
char bytes[8];
ifile.read(bytes, sizeof(char) * 8);
ifile.read(&buff, sizeof(char)); // newline
original_bytes_from_file[funcName] = std::string(bytes, 8);
funcName = "";
}
} while (!ifile.eof());
ifile.close();
}
}
}
spdlog::trace("Patching \"{}\"...", name);
BYTE* address = reinterpret_cast<BYTE*>(GetProcAddress(module, name.c_str()));
if (!address) {
spdlog::error("failed to unpatch \"{}\"", name);
}
std::string bytes;
if (original_bytes_from_file.contains(name)) {
bytes = original_bytes_from_file.at(name);
spdlog::trace("Using originalBytes from file for {}", name);
}
else {
if (Settings::isWin10 && UNHOOK_BYTES_ORIGINAL_WIN10.contains(name)) {
bytes = UNHOOK_BYTES_ORIGINAL_WIN10.at(name);
}
else {
bytes = UNHOOK_BYTES_ORIGINAL_22000.at(name);
}
spdlog::trace("Using fallback originalBytes for {}", name);
}
DWORD dw_old_protect, dw_bkup;
const auto len = bytes.size();
if (!VirtualProtect(address, len, PAGE_EXECUTE_READWRITE, &dw_old_protect)) { // Change permissions of memory..
spdlog::error("Couldn't change permissions of memory for \"{}\"", name);
return;
}
const auto opcode = *(address);
if (!std::ranges::any_of(JUMP_INSTR_OPCODES, [&opcode](const auto& op) { return op == opcode; })) {
spdlog::debug("\"{}\" Doesn't appear to be hooked, skipping!", name);
}
else {
for (DWORD i = 0; i < len; i++) // unpatch Valve's hook
{
*(address + i) = bytes[i];
}
spdlog::trace("Unpatched \"{}\"", name);
}
VirtualProtect(address, len, dw_old_protect, &dw_bkup); // Revert permission change...
#endif
}
std::string UnhookUtil::ReadOriginalBytes(const std::string& name, const std::wstring& moduleName)
{
auto module = LoadLibraryW(moduleName.c_str());
auto address = reinterpret_cast<BYTE*>(GetProcAddress(module, name.c_str()));
std::string res;
res.resize(8);
for (int i = 0; i < 8; i++) {
res[i] = static_cast<char>(*(address + i));
}
return res;
}

@ -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 <map>
#include <Windows.h>
#include <string>
#include <vector>
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<uint8_t> 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<std::string, std::string> 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<std::string, std::string> UNHOOK_BYTES_ORIGINAL_WIN10 = {
{"SetupDiEnumDeviceInfo", "\x40\x53\x56\x57\x41\x54\x41\x55"},
{"SetupDiGetClassDevsW", "\x48\x8B\xC4\x48\x89\x58\x08"},
};
} // namespace UnhookUtil

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save