Merge pull request #239 from Alia5/develop

Develop
main
Peter Repukat 4 months ago committed by GitHub
commit 2b41208e6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

3
.gitmodules vendored

@ -37,3 +37,6 @@
[submodule "deps/cpp-httplib"]
path = deps/cpp-httplib
url = https://github.com/yhirose/cpp-httplib
[submodule "deps/easywsclient"]
path = deps/easywsclient
url = https://github.com/dhbaird/easywsclient

41
.vscode/tasks.json vendored

@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build Solution (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:Build",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
},
{
"type": "shell",
"label": "Re-Build Solution (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:Rebuild",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
}
]
}

@ -0,0 +1,25 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/../deps/json/include",
"${workspaceFolder}/../deps/cpp-httplib",
"${workspaceFolder}/../deps/easywsclient"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING"
],
"windowsSdkVersion": "10.0.18362.0",
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe",
"cStandard": "c11",
"cppStandard": "c++20",
"intelliSenseMode": "msvc-x64"
}
],
"version": 4
}

@ -0,0 +1,43 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build CEFInjectLib (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:CEFInjectLib",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
},
{
"type": "shell",
"label": "Re-Build CEFInjectLib (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:CEFInjectLib:Rebuild",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
}
]
}

@ -0,0 +1,464 @@
/*
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()
);
}
}
}

@ -0,0 +1,104 @@
/*
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{};
}
}

@ -0,0 +1,165 @@
<?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>

@ -0,0 +1,27 @@
<?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,12 +4,22 @@ 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
@ -49,6 +59,20 @@ 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

@ -0,0 +1,43 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build GlosSIConfig (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:GlosSIConfig",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
},
{
"type": "shell",
"label": "Re-Build GlosSIConfig (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:GlosSIConfig:Rebuild",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
}
]
}

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

@ -71,6 +71,10 @@
<CompileAsWinRT>false</CompileAsWinRT>
<PreprocessorDefinitions>NOMINMAX;CONFIGAPP;%(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>
@ -94,6 +98,10 @@
<CompileAsWinRT>false</CompileAsWinRT>
<PreprocessorDefinitions>NOMINMAX;CONFIGAPP;%(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>
@ -136,7 +144,7 @@
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\GlosSITarget\UnhookUtil.cpp" />
<ClCompile Include="..\common\UnhookUtil.cpp" />
<ClCompile Include="ExeImageProvider.cpp" />
<ClCompile Include="main.cpp" />
<ClCompile Include="UIModel.cpp" />

@ -37,7 +37,7 @@
<ClCompile Include="ExeImageProvider.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\GlosSITarget\UnhookUtil.cpp">
<ClCompile Include="..\common\UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>

File diff suppressed because it is too large Load Diff

@ -32,30 +32,18 @@ limitations under the License.
#ifdef _WIN32
#include "UWPFetch.h"
#include <Windows.h>
#include <shlobj.h>
#endif
#include "ExeImageProvider.h"
#include "../version.hpp"
#include "../../GlosSITarget/UnhookUtil.h"
#include "../common/UnhookUtil.h"
#include "../common/util.h"
UIModel::UIModel() : QObject(nullptr)
{
wchar_t* localAppDataFolder;
std::filesystem::path path;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
path = std::filesystem::path(localAppDataFolder).parent_path();
}
path /= "Roaming";
path /= "GlosSI";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
auto path = util::path::getDataDirPath();
qDebug() << "Version: " << getVersionString();
@ -77,6 +65,10 @@ UIModel::UIModel() : QObject(nullptr)
font.setPointSize(11);
font.setFamily("Roboto");
QGuiApplication::setFont(font);
std::ofstream{getSteamPath() / ".cef-enable-remote-debugging"};
}
void UIModel::readTargetConfigs()
@ -206,6 +198,21 @@ uint32_t UIModel::getAppId(QVariant shortcut)
return 0;
}
Q_INVOKABLE QString UIModel::getGameId(QVariant shortcut) {
/*
* enum SteamLaunchableType
{
App = 0,
GameMod = 1,
Shortcut = 2,
P2P = 3
}
*/
uint64_t gameId = (((uint64_t)getAppId(shortcut) << 32) | ((uint32_t)2 << 24) | 0);
return QVariant(gameId).toString();
}
bool UIModel::addToSteam(QVariant shortcut, const QString& shortcutspath, bool from_cmd)
{
QDir appDir = QGuiApplication::applicationDirPath();
@ -380,17 +387,8 @@ void UIModel::updateCheck()
QVariantMap UIModel::getDefaultConf() const
{
wchar_t* localAppDataFolder;
std::filesystem::path path;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
path = std::filesystem::path(localAppDataFolder).parent_path();
}
auto path = util::path::getDataDirPath();
path /= "Roaming";
path /= "GlosSI";
path /= "default.json";
QJsonObject defaults = {
@ -404,7 +402,15 @@ QVariantMap UIModel::getDefaultConf() const
QJsonValue::fromVariant(QString::fromStdWString(getSteamPath(false).wstring()))},
{"steamUserId",
QJsonValue::fromVariant(QString::fromStdWString(getSteamUserId(false)))},
{"controller", QJsonObject{{"maxControllers", 1}, {"emulateDS4", false}, {"allowDesktopConfig", false}}},
{"globalModeGameId", ""},
{"globalModeUseGamepadUI", true},
{"minimizeSteamGamepadUI", true},
{"controller",
QJsonObject{{"maxControllers", -1},
{"emulateDS4", false},
{"allowDesktopConfig", false},
{"updateRate", 144}
}},
{"devices",
QJsonObject{
{"hideDevices", true},
@ -424,11 +430,12 @@ QVariantMap UIModel::getDefaultConf() const
{"window",
QJsonObject{
{"disableOverlay", false},
{"hideAltTab", false},
{"hideAltTab", true},
{"maxFps", QJsonValue::Null},
{"scale", QJsonValue::Null},
{"windowMode", false},
{"disableGlosSIOverlay", false},
{"opaqueSteamOverlay", false}
}},
};
@ -464,16 +471,8 @@ QVariantMap UIModel::getDefaultConf() const
void UIModel::saveDefaultConf(QVariantMap conf) const
{
wchar_t* localAppDataFolder;
std::filesystem::path path;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
path = std::filesystem::path(localAppDataFolder).parent_path();
}
path /= "Roaming";
path /= "GlosSI";
auto path = util::path::getDataDirPath();
path /= "default.json";
QFile file(path);
@ -486,6 +485,38 @@ 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

@ -50,6 +50,7 @@ class UIModel : public QObject {
Q_INVOKABLE void deleteTarget(int index);
Q_INVOKABLE bool isInSteam(QVariant shortcut) const;
Q_INVOKABLE uint32_t getAppId(QVariant shortcut);
Q_INVOKABLE QString getGameId(QVariant shortcut);
Q_INVOKABLE bool addToSteam(QVariant shortcut, const QString& shortcutspath, bool from_cmd = false);
bool addToSteam(const QString& name, const QString& shortcutspath, bool from_cmd = false);
Q_INVOKABLE bool removeFromSteam(const QString& name, const QString& shortcutspath, bool from_cmd = false);
@ -62,6 +63,11 @@ class UIModel : public QObject {
Q_INVOKABLE QVariantMap getDefaultConf() const;
Q_INVOKABLE void saveDefaultConf(QVariantMap conf) const;
Q_INVOKABLE QVariant globalModeShortcutConf();
Q_INVOKABLE bool globalModeShortcutExists();
Q_INVOKABLE uint32_t globalModeShortcutAppId();
Q_INVOKABLE QString globalModeShortcutGameId();
#ifdef _WIN32
Q_INVOKABLE QVariantList uwpApps();

@ -22,15 +22,17 @@ 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"
@ -90,19 +92,7 @@ void myMessageHandler(QtMsgType type, const QMessageLogContext&, const QString&
break;
}
wchar_t* localAppDataFolder;
std::filesystem::path path;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
path = std::filesystem::path(localAppDataFolder).parent_path();
}
path /= "Roaming";
path /= "GlosSI";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
auto path = util::path::getDataDirPath();
QFile outFile(QString::fromStdWString(path) + "/glossiconfig.log");
outFile.open(QIODevice::WriteOnly | QIODevice::Append);
@ -120,18 +110,7 @@ int main(int argc, char* argv[])
#endif
if (argc < 3) {
wchar_t* localAppDataFolder;
std::filesystem::path path;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
path = std::filesystem::path(localAppDataFolder).parent_path();
}
path /= "Roaming";
path /= "GlosSI";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
auto path = util::path::getDataDirPath();
QFile outFile(QString::fromStdWString(path) + "/glossiconfig.log");
outFile.open(QIODevice::WriteOnly);

@ -261,8 +261,14 @@ CollapsiblePane {
width: 128
editable: true
value: shortcutInfo.controller.maxControllers
from: 0
from: -1
to: 4
textFromValue: function(value) {
if (value == -1) {
return qsTr("auto");
}
return Number(value);
}
onValueChanged: shortcutInfo.controller.maxControllers = value
}
RoundButton {
@ -271,7 +277,12 @@ CollapsiblePane {
helpInfoDialog.text =
qsTr("GlosSI will only provide [NUMBER] of controllers")
+ "\n"
+ qsTr("Required to set to actually connected controller count when using \"real device IDs\" ")
+ qsTr("-1 ^= auto-detect")
+ "\n"
+ qsTr("auto detection only works upon launch")
+ "\n"
+ "\n"
+ qsTr("Required to manuelly set to actually connected controller count when using \"real device IDs\" ")
helpInfoDialog.open()
}
width: 48

@ -118,6 +118,85 @@ Item {
+ "as well as settings applied when launching GlosSITarget without config")
}
Item {
width: 1
height: 4
}
RPane {
width: parent.width
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
width: parent.width
height: parent.height
spacing: 4
Label {
font.bold: true
font.pixelSize: 24
text: qsTr("Experimental 🧪")
}
Row {
Row {
CheckBox {
id: 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

@ -0,0 +1,37 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/../deps/SFML/include",
"${workspaceFolder}/../deps/WinReg",
"${workspaceFolder}/../deps/spdlog/include",
"${workspaceFolder}/../deps/ValveFileVDF",
"${workspaceFolder}/../deps/subhook",
"${workspaceFolder}/../deps/ViGEmClient/include",
"${workspaceFolder}/../deps/imgui",
"${workspaceFolder}/../deps/imgui-sfml",
"${workspaceFolder}/../deps/json/include",
"${workspaceFolder}/../deps/traypp/tray/include",
"${workspaceFolder}/../deps/cpp-httplib",
"${workspaceFolder}/../CEFInjectLib"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE",
"SPDLOG_WCHAR_TO_UTF8_SUPPORT",
"SPDLOG_WCHAR_FILENAMES",
"_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING",
"SUBHOOK_STATIC"
],
"windowsSdkVersion": "10.0.18362.0",
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe",
"cStandard": "c11",
"cppStandard": "c++20",
"intelliSenseMode": "msvc-x64"
}
],
"version": 4
}

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

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

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

@ -24,16 +24,18 @@ limitations under the License.
#include <Propsys.h>
#include <propkey.h>
#include <shellapi.h>
#include <winerror.h>
#pragma comment(lib, "Shell32.lib")
#endif
#include "Settings.h"
#include "../common/Settings.h"
#include <regex>
#include "HttpServer.h"
#include "Overlay.h"
#include "UnhookUtil.h"
#include "util.h"
#include "../common/UnhookUtil.h"
#include "../common/util.h"
AppLauncher::AppLauncher(
std::vector<HWND>& process_hwnds,
@ -46,6 +48,39 @@ 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)
@ -75,10 +110,10 @@ void AppLauncher::launchApp(const std::wstring& path, const std::wstring& args)
if (ImGui::Begin("Launched Processes")) {
ImGui::BeginChild("Inner##LaunchedProcs", {0.f, ImGui::GetItemRectSize().y - 64}, true);
std::ranges::for_each(pids_, [](DWORD pid) {
ImGui::Text("%s | %d", std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>>().to_bytes(glossi_util::GetProcName(pid)).c_str(), pid);
ImGui::Text("%s | %d", util::string::to_string(util::win::process::GetProcName(pid)).c_str(), pid);
ImGui::SameLine();
if (ImGui::Button((" Kill ##" + std::to_string(pid)).c_str())) {
glossi_util::KillProcess(pid);
util::win::process::KillProcess(pid);
}
});
ImGui::EndChild();
@ -101,7 +136,7 @@ void AppLauncher::update()
getChildPids(pids_[0]);
}
if (!IsProcessRunning(pids_[0])) {
spdlog::info(L"Launched App \"{}\" with PID \"{}\" died", glossi_util::GetProcName(pids_[0]), pids_[0]);
spdlog::info(L"Launched App \"{}\" with PID \"{}\" died", util::win::process::GetProcName(pids_[0]), pids_[0]);
if (Settings::launch.closeOnExit && !Settings::launch.waitForChildProcs && Settings::launch.launch) {
spdlog::info("Configured to close on exit. Shutting down...");
shutdown_();
@ -117,12 +152,12 @@ void AppLauncher::update()
}
const auto running = IsProcessRunning(pid);
if (!running)
spdlog::trace(L"Child process \"{}\" with PID \"{}\" died", glossi_util::GetProcName(pid), pid);
spdlog::trace(L"Child process \"{}\" with PID \"{}\" died", util::win::process::GetProcName(pid), pid);
return !running;
});
auto filtered_pids = pids_ | std::ranges::views::filter([](DWORD pid) {
return std::ranges::find(Settings::launch.launcherProcesses, glossi_util::GetProcName(pid)) == Settings::launch.launcherProcesses.end();
return std::ranges::find(Settings::launch.launcherProcesses, util::win::process::GetProcName(pid)) == Settings::launch.launcherProcesses.end();
});
if (has_extra_launchers_ && !filtered_pids.empty()) {
launcher_has_launched_game_ = true;
@ -169,7 +204,7 @@ std::vector<DWORD> AppLauncher::launchedPids()
[](DWORD pid) {
return std::ranges::find(
Settings::launch.launcherProcesses,
glossi_util::GetProcName(pid)) == Settings::launch.launcherProcesses.end();
util::win::process::GetProcName(pid)) == Settings::launch.launcherProcesses.end();
})) {
res.push_back(pid);
}
@ -217,7 +252,7 @@ void AppLauncher::getChildPids(DWORD parent_pid)
if (pe.th32ParentProcessID == parent_pid) {
if (std::ranges::find(pids_, pe.th32ProcessID) == pids_.end()) {
if (Settings::common.extendedLogging) {
spdlog::info(L"Found new child process \"{}\" with PID \"{}\"", glossi_util::GetProcName(pe.th32ProcessID), pe.th32ProcessID);
spdlog::info(L"Found new child process \"{}\" with PID \"{}\"", util::win::process::GetProcName(pe.th32ProcessID), pe.th32ProcessID);
}
pids_.push_back(pe.th32ProcessID);
getChildPids(pe.th32ProcessID);
@ -250,9 +285,11 @@ void AppLauncher::getProcessHwnds()
IPropertyStore* propStore;
SHGetPropertyStoreForWindow(curr_wnd, IID_IPropertyStore, reinterpret_cast<void**>(&propStore));
PROPVARIANT prop;
propStore->GetValue(PKEY_AppUserModel_ID, &prop);
if (prop.bstrVal != nullptr && std::wstring(prop.bstrVal) == launched_uwp_path_) {
process_hwnds_.push_back(curr_wnd);
if (propStore != nullptr) {
propStore->GetValue(PKEY_AppUserModel_ID, &prop);
if (prop.bstrVal != nullptr && std::wstring(prop.bstrVal) == launched_uwp_path_) {
process_hwnds_.push_back(curr_wnd);
}
}
} while (curr_wnd != nullptr);
}
@ -263,7 +300,7 @@ void AppLauncher::getProcessHwnds()
#ifdef _WIN32
bool AppLauncher::findLauncherPids()
{
if (const auto pid = glossi_util::PidByName(L"EpicGamesLauncher.exe")) {
if (const auto pid = util::win::process::PidByName(L"EpicGamesLauncher.exe")) {
spdlog::debug("Found EGS-Launcher running");
pids_.push_back(pid);
return true;
@ -295,11 +332,14 @@ void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& a
// }
std::wstring args_cpy(
args.empty()
? L""
? L""
: ((native_seps_path.find(L" ") != std::wstring::npos
? L"\"" + native_seps_path + L"\""
: native_seps_path) + L" " + args)
);
: native_seps_path) +
L" " + args));
DWORD pid;
spdlog::debug(L"Launching Win32App app \"{}\"; args \"{}\"", native_seps_path, args_cpy);
if (CreateProcessW(native_seps_path.data(),
args_cpy.empty() ? nullptr : args_cpy.data(),
@ -311,17 +351,50 @@ void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& a
nullptr, // launch_dir.empty() ? nullptr : launch_dir.data(),
&info,
&process_info)) {
// spdlog::info(L"Started Program: \"{}\" in directory: \"{}\"", native_seps_path, launch_dir);
spdlog::info(L"Started Program: \"{}\"; PID: {}", native_seps_path, process_info.dwProcessId);
if (!watchdog) {
pid_mutex_.lock();
pids_.push_back(process_info.dwProcessId);
pid_mutex_.unlock();
}
pid = process_info.dwProcessId;
}
else {
// spdlog::error(L"Couldn't start program: \"{}\" in directory: \"{}\"", native_seps_path, launch_dir);
spdlog::error(L"Couldn't start program: \"{}\"", native_seps_path);
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: \"{}\"; PID: {}", native_seps_path, pid);
if (!watchdog) {
pid_mutex_.lock();
pids_.push_back(pid);
pid_mutex_.unlock();
}
}
@ -417,7 +490,7 @@ void AppLauncher::launchURL(const std::wstring& url, const std::wstring& args, c
spdlog::debug("Epic Games launch; Couldn't find egs launcher PID");
pid_mutex_.lock();
const auto pid = glossi_util::PidByName(L"EpicGamesLauncher.exe");
const auto pid = util::win::process::PidByName(L"EpicGamesLauncher.exe");
if (!findLauncherPids()) {
spdlog::debug("Did not find EGS-Launcher not running, retrying later...");
}

@ -0,0 +1,80 @@
#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,10 +1,8 @@
#pragma once
#include <windows.h>
#include <tlhelp32.h>
#include <spdlog/spdlog.h>
#include "util.h"
#include "../common/util.h"
namespace DllInjector {
@ -112,7 +110,7 @@ inline bool findModule(DWORD pid, std::wstring& lib_path, HMODULE& hMod)
inline void injectDllInto(std::filesystem::path dllPath, const std::wstring& processName)
{
if (std::filesystem::exists(dllPath)) {
const auto explorer_pid = glossi_util::PidByName(processName);
const auto explorer_pid = util::win::process::PidByName(processName);
if (explorer_pid != 0) {
if (DllInjector::TakeDebugPrivilege()) {
// No need to eject, as the dll is self-ejecting.

@ -51,6 +51,7 @@
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
<SpectreMitigation />
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
@ -128,6 +129,9 @@
<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>
@ -151,6 +155,9 @@
<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>
@ -168,6 +175,8 @@
</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" />
@ -187,7 +196,6 @@
<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" />
@ -195,16 +203,15 @@
<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" />
@ -213,13 +220,10 @@
<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,9 +51,6 @@
<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>
@ -114,10 +111,13 @@
<ClCompile Include="..\deps\traypp\tray\src\components\toggle.cpp">
<Filter>Source Files\tray</Filter>
</ClCompile>
<ClCompile Include="UnhookUtil.cpp">
<ClCompile Include="HttpServer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HttpServer.cpp">
<ClCompile Include="..\common\UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\common\HidHide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
@ -137,9 +137,6 @@
<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>
@ -161,9 +158,6 @@
<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>
@ -182,13 +176,10 @@
<ClInclude Include="GlosSI_logo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UnhookUtil.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="util.h">
<ClInclude Include="HttpServer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HttpServer.h">
<ClInclude Include="CommonHttpEndpoints.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>

@ -16,58 +16,115 @@ limitations under the License.
#include "HttpServer.h"
#include <spdlog/spdlog.h>
#include <nlohmann/json.hpp>
#include <utility>
#include "AppLauncher.h"
#include "Settings.h"
#include <algorithm>
HttpServer::HttpServer(AppLauncher& app_launcher, std::function<void()> close) : app_launcher_(app_launcher), close_(close)
HttpServer::HttpServer(std::function<void()> close) : close_(std::move(close))
{
}
std::string HttpServer::ToString(Method m)
{
switch (m) {
case POST:
return "POST";
case PATCH:
return "PATCH";
case PUT:
return "PUT";
default:
return "GET";
}
}
void HttpServer::AddEndpoint(const Endpoint&& e)
{
endpoints_.push_back(e);
}
void HttpServer::run()
{
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");
});
auto setCorsHeader = [](httplib::Response& res) {
res.set_header("Access-Control-Allow-Origin", "*");
};
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;
}
catch (...) {
res.status = 500;
res.set_content(nlohmann::json{
{"code", 500},
{"name", "Internal Server Error"},
{"message", "Unknown Error"},
}
.dump(),
"text/json");
return;
server_.Get("/", [this, &setCorsHeader](const httplib::Request& req, httplib::Response& res) {
setCorsHeader(res);
auto content_json = nlohmann::json{
{"endpoints", nlohmann::json::array()}};
for (const auto& e : endpoints_) {
content_json["endpoints"].push_back(
nlohmann::json{
{"path", e.path},
{"method", ToString(e.method)},
{"response", e.response_hint},
{"payload", e.payload_hint},
});
}
const nlohmann::json j = app_launcher_.launchedPids();
res.set_content(j.dump(), "text/json");
});
server_.Post("/quit", [this](const httplib::Request& req, httplib::Response& res) {
close_();
content_json["endpoints"].push_back(
nlohmann::json{
{"path", "/quit"},
{"method", "POST"}
});
res.set_content(content_json.dump(4),
"text/json");
});
server_.Get("/settings", [this](const httplib::Request& req, httplib::Response& res) {
res.set_content(Settings::toJson().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_thread_ = std::thread([this]() {

@ -17,22 +17,46 @@ limitations under the License.
#include <thread>
#include <httplib.h>
#include <nlohmann/json.hpp>
class AppLauncher;
class HttpServer {
public:
explicit HttpServer(AppLauncher& app_launcher, std::function<void()> close);
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);
void run();
void stop();
private:
httplib::Server server_;
std::thread server_thread_;
uint16_t port_ = 8756;
AppLauncher& app_launcher_;
std::function<void()> close_;
static inline std::vector<Endpoint> endpoints_;
};

@ -20,7 +20,7 @@ limitations under the License.
#include <spdlog/spdlog.h>
#include "Overlay.h"
#include "Settings.h"
#include "..\common\Settings.h"
InputRedirector::InputRedirector()
{
@ -49,6 +49,22 @@ InputRedirector::~InputRedirector()
void InputRedirector::run()
{
run_ = vigem_connected_;
max_controllers_ = Settings::controller.maxControllers;
if (max_controllers_ < 0) {
for (int i = 0; i < XUSER_MAX_COUNT; i++) {
XINPUT_STATE state{};
if (XInputGetState(i, &state) == ERROR_SUCCESS) {
max_controllers_ = i + 1;
}
}
if (max_controllers_ < 0) {
max_controllers_ = 1;
spdlog::error("Failed to auto detect controller count. Defaulting to 1");
}
else {
spdlog::info("Auto detected {} controllers", max_controllers_);
}
}
controller_thread_ = std::thread(&InputRedirector::runLoop, this);
#ifdef _WIN32
Overlay::AddOverlayElem([this](bool window_has_focus, ImGuiID dockspace_id) {
@ -58,13 +74,17 @@ void InputRedirector::run()
ImGui::Text("Max. controller count");
ImGui::SameLine();
ImGui::InputInt("##Max. controller count", &countcopy, 1, 1);
ImGui::Text("-1 = Auto-detection (auto-detection only works on launch");
if (countcopy > XUSER_MAX_COUNT) {
countcopy = XUSER_MAX_COUNT;
}
if (countcopy < 0) {
countcopy = 0;
if (countcopy < -1) {
countcopy = -1;
}
Settings::controller.maxControllers = countcopy;
if (Settings::controller.maxControllers > -1) {
max_controllers_ = countcopy;
}
if (ImGui::Checkbox("Emulate DS4 (instead of Xbox360 controller)", &Settings::controller.emulateDS4)) {
controller_settings_changed_ = true;
@ -131,12 +151,12 @@ void InputRedirector::runLoop()
unplugVigemPad(i);
}
}
if (Settings::controller.maxControllers < XUSER_MAX_COUNT) {
for (int i = Settings::controller.maxControllers; i < XUSER_MAX_COUNT; i++) {
if (max_controllers_ < XUSER_MAX_COUNT) {
for (int i = max_controllers_; i < XUSER_MAX_COUNT; i++) {
unplugVigemPad(i);
}
}
for (int i = 0; i < XUSER_MAX_COUNT && i < Settings::controller.maxControllers; i++) {
for (int i = 0; i < XUSER_MAX_COUNT && i < max_controllers_; i++) {
XINPUT_STATE state{};
if (XInputGetState(i, &state) == ERROR_SUCCESS) {
if (vt_pad_[i] != nullptr) {
@ -216,6 +236,10 @@ void InputRedirector::runLoop()
vigem_target_get_pid(vt_pad_[i]));
if (Settings::controller.emulateDS4) {
// TODO: make sense of DS4_OUTPUT_BUFFER
// there is no doc? Ask @Nef about this...
// ReSharper disable once CppDeprecatedEntity
#pragma warning(disable : 4996)
const auto callback_register_res = vigem_target_ds4_register_notification(
driver_,
vt_pad_[i],
@ -242,7 +266,7 @@ void InputRedirector::runLoop()
unplugVigemPad(i);
}
}
sf::sleep(sf::milliseconds(4));
Sleep(static_cast<int>(1000.f / Settings::controller.updateRate));
#endif
}

@ -35,6 +35,7 @@ class InputRedirector {
private:
void runLoop();
int max_controllers_ = -1;
static constexpr int start_delay_ms_ = 2000;
bool run_ = false;
int overlay_elem_id_ = -1;

@ -17,13 +17,11 @@ limitations under the License.
#include <filesystem>
#include <utility>
#include <locale>
#include <codecvt>
#include <regex>
#include <shlobj_core.h>
#include "Roboto.h"
#include "Settings.h"
#include "..\common\Settings.h"
#include "GlosSI_logo.h"
#include "../version.hpp"
@ -52,23 +50,11 @@ Overlay::Overlay(
ImGui::SFML::UpdateFontTexture();
#ifdef _WIN32
wchar_t* localAppDataFolder;
std::filesystem::path config_path;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
config_path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
config_path = std::filesystem::path(localAppDataFolder).parent_path();
}
config_path /= "Roaming";
config_path /= "GlosSI";
if (!std::filesystem::exists(config_path))
std::filesystem::create_directories(config_path);
auto config_path = util::path::getDataDirPath();
config_path /= "imgui.ini";
// This assumes that char is utf8 and wchar_t is utf16, which is guaranteed on Windows.
config_file_name_ = std::wstring_convert<std::codecvt_utf8<wchar_t>>().to_bytes(config_path.wstring());
config_file_name_ = util::string::to_string(config_path.wstring());
io.IniFilename = config_file_name_.data();
#endif
@ -177,12 +163,16 @@ 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;
}
@ -225,6 +215,17 @@ void Overlay::update()
closeOverlayButton();
}
std::ranges::for_each(FORCED_OVERLAY_ELEMS_, [this](const auto& elem) {
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.05f, 0.07f, 0.07f, 0.95f));
ImGui::SetNextWindowPos({
ImGui::GetMainViewport()->Size.x * 0.5f,
ImGui::GetMainViewport()->Size.y * 0.5f
}, ImGuiCond_Always,
{0.5f, 0.5f});
elem.second(window_.hasFocus(), 0);
ImGui::PopStyleColor();
});
ImGui::SFML::Render(window_);
}
@ -243,9 +244,14 @@ void Overlay::AddLog(const spdlog::details::log_msg& msg)
LOG_MSGS_.push_back({.time = msg.time, .level = msg.level, .payload = msg.payload.data()});
}
int Overlay::AddOverlayElem(const std::function<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn)
int Overlay::AddOverlayElem(const std::function<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn, bool force_show)
{
OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn});
if (force_show) {
FORCED_OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn});
}
else {
OVERLAY_ELEMS_.insert({overlay_element_id_, elem_fn});
}
// keep this non confusing, but longer...
const auto res = overlay_element_id_;
overlay_element_id_++;
@ -254,7 +260,10 @@ int Overlay::AddOverlayElem(const std::function<void(bool window_has_focus, ImGu
void Overlay::RemoveOverlayElem(int id)
{
OVERLAY_ELEMS_.erase(id);
if (OVERLAY_ELEMS_.contains(id))
OVERLAY_ELEMS_.erase(id);
if (FORCED_OVERLAY_ELEMS_.contains(id))
FORCED_OVERLAY_ELEMS_.erase(id);
}
void Overlay::showLogs(ImGuiID dockspace_id)

@ -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);
static int AddOverlayElem(const std::function<void(bool window_has_focus, ImGuiID dockspace_id)>& elem_fn, bool force_show_ = false);
static void RemoveOverlayElem(int id);
private:
@ -71,6 +71,8 @@ class Overlay {
static inline int overlay_element_id_ = 0;
static inline std::map<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,0,2045006300001
PRODUCTVERSION 0,1,0,2045006300001
FILEVERSION 0,1,2,0068000002000
PRODUCTVERSION 0,1,2,0068000002000
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -69,12 +69,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Peter Repukat - FlatspotSoftware"
VALUE "FileDescription", "GlosSI - SteamTarget"
VALUE "FileVersion", "0.1.0.2-45-g63fdab1"
VALUE "FileVersion", "0.1.2.0-68-g0f02eca"
VALUE "InternalName", "GlosSITarget"
VALUE "LegalCopyright", "Copyright (C) 2021-2022 Peter Repukat - FlatspotSoftware"
VALUE "OriginalFilename", "GlosSITarget.exe"
VALUE "ProductName", "GlosSI"
VALUE "ProductVersion", "0.1.0.2-45-g63fdab1"
VALUE "ProductVersion", "0.1.2.0-68-g0f02eca"
END
END
BLOCK "VarFileInfo"
@ -220,6 +220,41 @@ IDI_ICON1 ICON "..\\GlosSI_Icon.ico"

@ -1,331 +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 <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;
bool hideAltTab = false;
bool disableGlosSIOverlay = false;
} window;
inline struct Controller {
int maxControllers = 1;
bool allowDesktopConfig = false;
bool emulateDS4 = false;
} controller;
inline struct Common {
bool no_uwp_overlay = false;
bool disable_watchdog = false;
bool extendedLogging = false;
std::wstring name;
std::wstring icon;
int version;
std::wstring steamPath;
std::wstring steamUserId;
} 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);
safeParseValue(winconf, "hideAltTab", window.hideAltTab);
safeParseValue(winconf, "disableGlosSIOverlay", window.disableGlosSIOverlay);
}
if (auto controllerConf = json["controller"]; !controllerConf.is_null() && !controllerConf.empty() && controllerConf.is_object()) {
safeParseValue(controllerConf, "maxControllers", controller.maxControllers);
safeParseValue(controllerConf, "allowDesktopConfig", controller.allowDesktopConfig);
safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4);
}
safeParseValue(json, "extendedLogging", common.extendedLogging);
safeWStringParse(json, "name", common.name);
safeWStringParse(json, "icon", common.icon);
safeParseValue(json, "version", common.version);
safeWStringParse(json, "steamPath", common.steamPath);
safeWStringParse(json, "steamUserId", common.steamUserId);
}
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 if (arg == L"-window") {
window.windowMode = true;
}
else {
configName += L" " + std::wstring(arg.begin(), arg.end());
}
}
if (!configName.empty()) {
if (configName[0] == L' ') {
configName.erase(configName.begin());
}
if (!configName.ends_with(L".json")) {
configName += L".json";
}
}
wchar_t* localAppDataFolder;
std::filesystem::path path;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
path = std::filesystem::path(localAppDataFolder).parent_path();
}
path /= "Roaming";
path /= "GlosSI";
if (!configName.empty()) {
path /= "Targets";
path /= configName;
}
else {
spdlog::info("No config file specified, using default");
path /= "default.json";
}
std::ifstream json_file;
json_file.open(path);
if (!json_file.is_open()) {
spdlog::error(L"Couldn't open settings file {}", path.wstring());
spdlog::debug(L"Using sane defaults...");
return;
}
settings_path_ = path;
const auto& json = nlohmann::json::parse(json_file);
Parse(json);
spdlog::debug("Read config file \"{}\"; config: {}", path.string(), json.dump());
json_file.close();
}
inline nlohmann::json toJson()
{
nlohmann::json json;
json["version"] = 1;
json["launch"]["launch"] = launch.launch;
json["launch"]["launchPath"] = std::wstring_convert<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["window"]["hideAltTab"] = window.hideAltTab;
json["controller"]["maxControllers"] = controller.maxControllers;
json["controller"]["allowDesktopConfig"] = controller.allowDesktopConfig;
json["controller"]["emulateDS4"] = controller.emulateDS4;
json["extendedLogging"] = common.extendedLogging;
json["name"] = std::wstring_convert<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

@ -17,7 +17,7 @@ limitations under the License.
#include <spdlog/spdlog.h>
#include "Settings.h"
#include "..\common\Settings.h"
#ifdef _WIN32
#define NOMINMAX

@ -15,27 +15,26 @@ limitations under the License.
*/
#include "SteamTarget.h"
#include "Settings.h"
#include "../common/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(); },
getScreenshotHotkey(),
util::steam::getScreenshotHotkey(steam_path_, steam_user_id_),
[this]() {
target_window_handle_ = window_.getSystemHandle();
overlay_ = window_.getOverlay();
@ -46,93 +45,160 @@ SteamTarget::SteamTarget()
delayed_shutdown_ = true;
delay_shutdown_clock_.restart();
}),
server_(launcher_, [this] { run_ = false; })
server_([this] { run_ = false; })
{
target_window_handle_ = window_.getSystemHandle();
#ifdef _WIN32
if (Settings::common.no_uwp_overlay) {
UWPOverlayEnabler::AddUwpOverlayOvWidget();
}
else {
UWPOverlayEnabler::EnableUwpOverlay();
}
#endif
}
int SteamTarget::run()
{
if (!SteamOverlayDetector::IsSteamInjected()) {
spdlog::warn("Steam-overlay not detected. Showing GlosSI-overlay!\n\
Application will not function!");
window_.setClickThrough(false);
if (!overlay_.expired())
overlay_.lock()->setEnabled(true);
steam_overlay_present_ = false;
}
else {
spdlog::info("Steam-overlay detected.");
spdlog::warn("Double press Steam- overlay key(s)/Controller button to show GlosSI-overlay"); // Just to color output and really get users attention
window_.setClickThrough(true);
if (!overlay_.expired())
overlay_.lock()->setEnabled(false);
steam_overlay_present_ = true;
#ifdef WIN32
if (!Settings::common.disable_watchdog) {
wchar_t buff[MAX_PATH];
GetModuleFileName(GetModuleHandle(NULL), buff, MAX_PATH);
std::wstring watchDogPath(buff);
watchDogPath = watchDogPath.substr(0, 1 + watchDogPath.find_last_of(L'\\')) + L"GlosSIWatchdog.dll";
DllInjector::injectDllInto(watchDogPath, L"explorer.exe");
}
#endif
}
getXBCRebindingEnabled();
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);
#ifdef _WIN32
hidhide_.hideDevices(steam_path_);
input_redirector_.run();
#endif
CHTE::addEndpoints();
if (Settings::launch.launch) {
launcher_.launchApp(Settings::launch.launchPath, Settings::launch.launchAppArgs);
}
server_.run();
keepControllerConfig(true);
#ifdef _WIN32
HICON icon = 0;
TCHAR path[MAX_PATH];
GetModuleFileName(nullptr, path, MAX_PATH);
icon = (HICON)LoadImage(
0,
path,
IMAGE_ICON,
GetSystemMetrics(SM_CXSMICON),
GetSystemMetrics(SM_CYSMICON),
LR_LOADFROMFILE | LR_LOADMAP3DCOLORS);
if (!icon) {
ExtractIconEx(path, 0, &icon, nullptr, 1);
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();
}
Tray::Tray tray{"GlosSITarget", icon};
#else
Tray::Tray tray{"GlosSITarget", "ico.png"};
#endif
tray.addEntry(Tray::Button{
"Quit", [this, &tray]() {
run_ = false;
}});
server_.run();
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;
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) {
@ -140,17 +206,30 @@ Application will not function!");
}
}
else {
launcher_.update();
if (fully_initialized_) {
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();
launcher_.close();
if (cef_tweaks_enabled_) {
steam_tweaks_.uninstallTweaks();
}
}
return 0;
}
@ -159,11 +238,17 @@ void SteamTarget::onOverlayChanged(bool overlay_open)
if (overlay_open) {
focusWindow(target_window_handle_);
window_.setClickThrough(!overlay_open);
if (!Settings::window.windowMode && Settings::window.opaqueSteamOverlay) {
window_.setTransparent(false);
}
}
else {
if (!(overlay_.expired() ? false : overlay_.lock()->isEnabled())) {
window_.setClickThrough(!overlay_open);
focusWindow(last_foreground_window_);
if (!Settings::window.windowMode && Settings::window.opaqueSteamOverlay) {
window_.setTransparent(true);
}
}
}
if (!overlay_trigger_flag_) {
@ -241,155 +326,110 @@ void SteamTarget::focusWindow(WindowHandle hndl)
#endif
}
std::filesystem::path SteamTarget::getSteamPath() const
{
#ifdef _WIN32
try {
// TODO: check if keys/value exist
// steam should always be open and have written reg values...
winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam"};
const auto res = key.GetStringValue(L"SteamPath");
spdlog::info(L"Detected Steam Path: {}", res);
return res;
}
catch (const winreg::RegException& e) {
spdlog::error("Couldn't get Steam path from Registry; {}", e.what());
}
return Settings::common.steamPath;
#else
return L""; // TODO
#endif
}
std::wstring SteamTarget::getSteamUserId() const
{
#ifdef _WIN32
try {
// TODO: check if keys/value exist
// steam should always be open and have written reg values...
winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess"};
const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser"));
spdlog::info(L"Detected Steam UserId: {}", res);
return res;
}
catch (const winreg::RegException& e) {
spdlog::error("Couldn't get Steam path from Registry; {}", e.what());
}
return Settings::common.steamUserId;
#else
return L""; // TODO
#endif
}
std::vector<std::string> SteamTarget::getOverlayHotkey()
void SteamTarget::init_FuckingRenameMe()
{
const auto config_path = std::wstring(steam_path_) + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_);
if (!std::filesystem::exists(config_path)) {
spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path);
return {"Shift", "KEY_TAB"}; // default
}
std::ifstream config_file(config_path);
auto root = tyti::vdf::read(config_file);
std::shared_ptr<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
}
auto hotkeys = children->attribs.at("InGameOverlayShortcutKey");
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!");
}
// 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
}
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> res;
for (auto i = 1; i < m.size(); i++) {
const auto s = std::string(m[i]);
if (!s.empty()) {
res.push_back(s);
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;
}
}
if (res.empty()) {
spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab");
return {"Shift", "KEY_TAB"}; // default
}
spdlog::info("Detected Overlay hotkey(s): {}", std::accumulate(
res.begin() + 1, res.end(), res[0],
[](auto acc, const auto curr) { return acc += "+" + curr; }));
return res;
}
std::vector<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
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;
}
std::ifstream config_file(config_path);
auto root = tyti::vdf::read(config_file);
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
}
auto hotkeys = children->attribs.at("InGameOverlayScreenshotHotKey");
#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";
// 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
DllInjector::injectDllInto(watchDogPath, L"explorer.exe");
}
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 (Settings::common.no_uwp_overlay) {
UWPOverlayEnabler::AddUwpOverlayOvWidget();
}
if (res.empty()) {
spdlog::warn("Couldn't detect overlay hotkey, using default: F12");
return {"KEY_F12"}; // default
else {
UWPOverlayEnabler::EnableUwpOverlay();
}
spdlog::info("Detected screenshot hotkey(s): {}", std::accumulate(
res.begin() + 1, res.end(), res[0],
[](auto acc, const auto curr) { return acc += "+" + curr; }));
return res;
}
bool SteamTarget::getXBCRebindingEnabled()
{
const auto config_path = std::wstring(steam_path_) + std::wstring(user_data_path_) + steam_user_id_ + std::wstring(config_file_name_);
if (!std::filesystem::exists(config_path)) {
spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path);
return false;
hidhide_.hideDevices(steam_path_);
input_redirector_.run();
#endif
if (Settings::launch.launch) {
launcher_.launchApp(Settings::launch.launchPath, Settings::launch.launchAppArgs);
}
std::ifstream config_file(config_path);
auto root = tyti::vdf::read(config_file);
keepControllerConfig(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!");
if (cef_tweaks_enabled_) {
steam_tweaks_.setAutoInject(true);
}
return xbsup == "1";
fully_initialized_ = true;
}
/*
* The "magic" that keeps a controller-config forced (without hooking into Steam)
*
* Hook into own process and detour "GetForegroundWindow"
* Deatour function always returns HWND of own application window
* Detour function always returns HWND of own application window
* Steam now doesn't detect application changes and keeps the game-specific input config without reverting to desktop-conf
*/
void SteamTarget::keepControllerConfig(bool keep)
@ -409,6 +449,7 @@ void SteamTarget::keepControllerConfig(bool keep)
spdlog::error("Couldn't un-install GetForegroundWindow hook!");
}
}
#endif
}
@ -440,6 +481,34 @@ 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;

@ -21,18 +21,23 @@ limitations under the License.
#include "TargetWindow.h"
#ifdef _WIN32
#include "HidHide.h"
#include "../common/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"
#include <filesystem>
namespace Tray {
class Tray;
}
class SteamTarget {
public:
explicit SteamTarget();
@ -42,17 +47,16 @@ class SteamTarget {
void onOverlayChanged(bool overlay_open);
void toggleGlossiOverlay();
void focusWindow(WindowHandle hndl);
std::filesystem::path getSteamPath() const;
std::wstring getSteamUserId() const;
std::filesystem::path steam_path_ = getSteamPath();
std::wstring steam_user_id_ = getSteamUserId();
std::vector<std::string> getOverlayHotkey();
std::vector<std::string> getScreenshotHotkey();
bool getXBCRebindingEnabled();
std::filesystem::path steam_path_ = util::steam::getSteamPath();
std::wstring steam_user_id_ = util::steam::getSteamUserId();
CEFInject::SteamTweaks steam_tweaks_;
bool cef_tweaks_enabled_ = true;
bool steam_overlay_present_ = false;
bool fully_initialized_ = false;
bool can_fully_initialize_ = true;
void init_FuckingRenameMe();
// Keep controllerConfig even is window is switched.
// On Windoze hooking "GetForeGroundWindow" is enough;
@ -65,6 +69,8 @@ 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
@ -74,7 +80,7 @@ class SteamTarget {
void overlayHotkeyWorkaround();
bool run_ = false;
std::vector<std::string> overlay_hotkey_ = getOverlayHotkey();
std::vector<std::string> overlay_hotkey_ = util::steam::getOverlayHotkey(steam_path_, steam_user_id_);
#ifdef _WIN32
HidHide hidhide_;
@ -89,14 +95,9 @@ class SteamTarget {
static inline WindowHandle target_window_handle_ = nullptr;
sf::Clock overlay_trigger_clock_;
uint32_t overlay_trigger_max_seconds_ = 1;
float overlay_trigger_max_seconds_ = 2.5;
bool overlay_trigger_flag_ = false;
bool delayed_shutdown_ = false;
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 ";
};

@ -30,7 +30,7 @@ limitations under the License.
#include "ProcessPriority.h"
#include "Settings.h"
#include "..\common\Settings.h"
#if !defined(WM_DPICHANGED)
#define WM_DPICHANGED 0x02E0
@ -73,7 +73,7 @@ TargetWindow::TargetWindow(
}
if (Settings::window.maxFps < 15 && Settings::window.maxFps > 0) {
Settings::window.maxFps = 0;
setFpsLimit(screen_refresh_rate_);
setFpsLimit(TargetWindow::calcAutoRefreshRate(screen_refresh_rate_));
} else {
setFpsLimit(Settings::window.maxFps);
}
@ -149,6 +149,41 @@ void TargetWindow::setClickThrough(bool click_through)
#endif
}
void TargetWindow::setTransparent(bool transparent) const
{
HWND hwnd = window_.getSystemHandle();
if (transparent) {
// if (windowed_) {
// DWM_BLURBEHIND bb{.dwFlags = DWM_BB_ENABLE, .fEnable = true, .hRgnBlur = nullptr};
// DwmEnableBlurBehindWindow(hwnd, &bb);
// } // semi-transparent in window mode, but deprecated api
// On Linux the window will (should) automagically be semi-transparent
// transparent windows window...
auto style = GetWindowLong(hwnd, GWL_STYLE);
style &= ~WS_OVERLAPPED;
style |= WS_POPUP;
SetWindowLong(hwnd, GWL_STYLE, style);
MARGINS margins = { -1 };
DwmExtendFrameIntoClientArea(hwnd, &margins);
spdlog::debug("Setting window to transparent");
} else {
auto style = GetWindowLong(hwnd, GWL_STYLE);
style |= WS_OVERLAPPED;
style &= ~WS_POPUP;
SetWindowLong(hwnd, GWL_STYLE, style);
MARGINS margins = {0};
DwmExtendFrameIntoClientArea(hwnd, &margins);
spdlog::debug("Setting window to opaque");
}
}
void TargetWindow::update()
{
sf::Event event{};
@ -307,6 +342,18 @@ WORD TargetWindow::GetWindowDPI(HWND hWnd)
}
#endif
unsigned int TargetWindow::calcAutoRefreshRate(unsigned int rate)
{
unsigned int auto_refresh_rate = rate;
while (auto_refresh_rate > 60) {
auto_refresh_rate /= 2;
}
if (auto_refresh_rate < 30) {
auto_refresh_rate = 30;
}
return auto_refresh_rate;
}
void TargetWindow::createWindow()
{
toggle_window_mode_after_frame_ = false;
@ -352,22 +399,7 @@ void TargetWindow::createWindow()
auto dpi = GetWindowDPI(hwnd);
spdlog::debug("Screen DPI: {}", dpi);
//if (windowed_) {
// DWM_BLURBEHIND bb{.dwFlags = DWM_BB_ENABLE, .fEnable = true, .hRgnBlur = nullptr};
// DwmEnableBlurBehindWindow(hwnd, &bb);
//} // semi-transparent in window mode, but deprecated api
// On Linux the window will (should) automagically be semi-transparent
// transparent windows window...
auto style = GetWindowLong(hwnd, GWL_STYLE);
style &= ~WS_OVERLAPPED;
style |= WS_POPUP;
SetWindowLong(hwnd, GWL_STYLE, style);
MARGINS margins;
margins.cxLeftWidth = -1;
DwmExtendFrameIntoClientArea(hwnd, &margins);
setTransparent(true);
DEVMODE dev_mode = {};
dev_mode.dmSize = sizeof(DEVMODE);
@ -379,14 +411,14 @@ void TargetWindow::createWindow()
screen_refresh_rate_ = 60;
}
else {
setFpsLimit(dev_mode.dmDisplayFrequency);
setFpsLimit(TargetWindow::calcAutoRefreshRate(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);
spdlog::debug("auto screen Scale: {}", dpi/96.f);
ImGuiIO& io = ImGui::GetIO();
io.FontGlobalScale = dpi / 96.f;
ImGui::SFML::UpdateFontTexture();

@ -39,6 +39,7 @@ class TargetWindow {
void setFpsLimit(unsigned int fps_limit);
void setClickThrough(bool click_through);
void setTransparent(bool transparent) const;
void update();
void close();
@ -79,6 +80,7 @@ class TargetWindow {
std::shared_ptr<Overlay> overlay_;
static unsigned int calcAutoRefreshRate(unsigned int rate);
void createWindow();
bool toggle_window_mode_after_frame_ = false;

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

@ -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

@ -30,7 +30,7 @@ limitations under the License.
#include "SteamTarget.h"
#include "OverlayLogSink.h"
#include "Settings.h"
#include "..\common\Settings.h"
#include <iostream>
#include "../version.hpp"
@ -71,19 +71,7 @@ LONG Win32FaultHandler(struct _EXCEPTION_POINTERS* ExInfo)
MINIDUMP_EXCEPTION_INFORMATION M;
HANDLE hDump_File;
wchar_t* localAppDataFolder;
std::filesystem::path path;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
path = std::filesystem::path(localAppDataFolder).parent_path();
}
path /= "Roaming";
path /= "GlosSI";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
auto path = util::path::getDataDirPath();
path /= "glossitarget.dmp";
M.ThreadId = GetCurrentThreadId();
@ -127,19 +115,7 @@ int main(int argc, char* argv[])
const auto console_sink = std::make_shared<spdlog::sinks::stderr_color_sink_mt>();
console_sink->set_level(spdlog::level::trace);
#ifdef _WIN32
wchar_t* localAppDataFolder;
std::filesystem::path path;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
path = std::filesystem::path(localAppDataFolder).parent_path();
}
path /= "Roaming";
path /= "GlosSI";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
auto path = util::path::getDataDirPath();
path /= "glossitarget.log";
// For "path.wstring()" to be usable here, SPDLOG_WCHAR_FILENAMES must be defined.
const auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(path.wstring(), true);

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

@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build GlosSIWatchdog (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:GlosSIWatchdog",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
},
{
"type": "shell",
"label": "Re-Build GlosSIWatchdog (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:GlosSIWatchdog:Rebuild",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
}
]
}

@ -71,10 +71,12 @@
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<IncludePath>..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;$(IncludePath)</IncludePath>
<IncludePath>..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;..\CEFInjectLib;$(IncludePath)</IncludePath>
<LibraryPath>..\x64\Debug;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;$(IncludePath)</IncludePath>
<IncludePath>..\deps\spdlog\include;..\deps\json\include;..\deps\cpp-httplib;..\CEFInjectLib;$(IncludePath)</IncludePath>
<LibraryPath>..\x64\Release;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
@ -111,11 +113,15 @@
<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;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>hid.lib;Cfgmgr32.lib;setupapi.lib;CefInjectLib.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<CustomBuildStep>
<Command>powershell.exe $(SolutionDir)version_help.ps1</Command>
@ -131,13 +137,16 @@
<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;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>hid.lib;Cfgmgr32.lib;setupapi.lib;CefInjectLib.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<CustomBuildStep>
<Outputs>Upading version based on git;%(Outputs)</Outputs>
@ -145,8 +154,8 @@
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\GlosSITarget\HidHide.cpp" />
<ClCompile Include="..\GlosSITarget\UnhookUtil.cpp" />
<ClCompile Include="..\common\HidHide.cpp" />
<ClCompile Include="..\common\UnhookUtil.cpp" />
<ClCompile Include="dllmain.cpp" />
</ItemGroup>
<ItemGroup>

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

@ -15,10 +15,9 @@ limitations under the License.
*/
#include <httplib.h>
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <ShlObj.h>
#include "../common/util.h"
#include <filesystem>
@ -28,10 +27,10 @@ limitations under the License.
#include <nlohmann/json.hpp>
#include "../version.hpp"
#include "../GlosSITarget/Settings.h"
#include "../GlosSITarget/HidHide.h"
#include "../GlosSITarget/util.h"
#include "../common/Settings.h"
#include "../common/HidHide.h"
bool IsProcessRunning(DWORD pid)
{
@ -66,20 +65,7 @@ void fetchSettings(httplib::Client& http_client, int retried_count = 0) {
DWORD WINAPI watchdog(HMODULE hModule)
{
wchar_t* localAppDataFolder;
std::filesystem::path configDirPath;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
configDirPath = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
configDirPath = std::filesystem::path(localAppDataFolder).parent_path();
}
configDirPath /= "Roaming";
configDirPath /= "GlosSI";
if (!std::filesystem::exists(configDirPath))
std::filesystem::create_directories(configDirPath);
auto configDirPath = util::path::getDataDirPath();
auto logPath = configDirPath;
logPath /= "GlosSIWatchdog.log";
const auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logPath.wstring(), true);
@ -143,7 +129,7 @@ DWORD WINAPI watchdog(HMODULE hModule)
}
if (IsProcessRunning(pid))
{
glossi_util::KillProcess(pid);
util::win::process::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.0.9.1-48-geb4ae9c"
!define VERSION "0.1.2.0-68-g0f02eca"
!define COPYRIGHT "Peter Repukat - FlatspotSoftware © 2017-2022"
!define DESCRIPTION "SteamInput compatibility tool"
!define INSTALLER_NAME "GlosSI-Installer.exe"
@ -193,3 +193,55 @@ SectionEnd

@ -0,0 +1,187 @@
module.exports = {
extends: [
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
],
env: {
browser: true,
node: false
},
plugins: [
'@typescript-eslint',
'no-null',
'prefer-arrow',
'import',
],
parser: '@typescript-eslint/parser', // Specifies the ESLint parser
parserOptions: {
ecmaVersion: 2022, // Allows for the parsing of modern ECMAScript features
sourceType: 'module',
ecmaFeatures: {
jsx: false,
},
project: ['./tsconfig.json'],
tsconfigRootDir: __dirname
},
rules: {
'@typescript-eslint/no-namespace': 'off',
'@typescript-eslint/ban-types': 'error',
'@typescript-eslint/adjacent-overload-signatures': 'error',
'@typescript-eslint/array-type': 'error',
'@typescript-eslint/consistent-type-definitions': ['error', 'interface'],
'@typescript-eslint/no-inferrable-types': 'error',
'@typescript-eslint/no-misused-new': 'error',
'@typescript-eslint/no-this-alias': 'error',
'@typescript-eslint/prefer-for-of': 'error',
'@typescript-eslint/prefer-function-type': 'error',
'@typescript-eslint/prefer-namespace-keyword': 'error',
'no-inner-declarations': 'off', // we have es6blocked scoped functions.
'@typescript-eslint/triple-slash-reference': 'error',
'@typescript-eslint/type-annotation-spacing': 'error',
'@typescript-eslint/unified-signatures': 'error',
'@typescript-eslint/no-explicit-any': 'error',
'@typescript-eslint/no-unused-vars': 'error',
'@typescript-eslint/unbound-method': 'warn',
'@typescript-eslint/semi': [
'error',
'always'
],
'@typescript-eslint/quotes': [
'warn',
'single'
],
'@typescript-eslint/member-delimiter-style': [
'error',
{
'multiline': {
'delimiter': 'semi',
'requireLast': true
},
'singleline': {
'delimiter': 'semi',
'requireLast': false
}
}
],
'@typescript-eslint/indent': [
'warn',
4,
{
'FunctionDeclaration': {
'parameters': 'first'
},
'FunctionExpression': {
'parameters': 'first'
},
'SwitchCase': 1
}
],
'@typescript-eslint/explicit-member-accessibility': [
'error',
{
'accessibility': 'explicit'
}
],
'@typescript-eslint/no-use-before-define': ['error', { 'functions': false }],
// "@typescript-eslint/naming-convention": [
// "error",
// {
// "selector": "default",
// "format": ["camelCase", "PascalCase"]
// },
// {
// "selector": "variable",
// "format": ["camelCase", "UPPER_CASE"]
// },
// {
// "selector": "parameter",
// "format": ["camelCase"],
// "leadingUnderscore": "allow"
// },
// {
// "selector": "memberLike",
// "modifiers": ["private"],
// "format": ["camelCase"],
// "leadingUnderscore": "require"
// },
// {
// "selector": "typeLike",
// "format": ["PascalCase"]
// }
// ],
'no-console': 'off',
'no-return-await': 'error',
'arrow-body-style': 'error',
'arrow-parens': [
'error',
'always'
],
'camelcase': ['warn', { "ignoreImports": true }],
'comma-dangle': [
'error',
{
'objects': 'never',
'arrays': 'never',
'functions': 'never'
}
],
'prefer-arrow/prefer-arrow-functions': 'error',
'prefer-arrow-callback': 'error',
'prefer-const': 'error',
'quote-props': [
'error',
'consistent-as-needed'
],
'no-var': 'error',
'new-parens': 'error',
'no-caller': 'error',
'no-cond-assign': 'error',
'no-debugger': 'error',
'no-empty': 'error',
'no-eval': 'error',
'no-multiple-empty-lines': 'warn',
'no-new-wrappers': 'error',
'no-redeclare': 'error',
'no-shadow': [
'error',
{
'hoist': 'all'
}
],
'no-null/no-null': 'error',
'no-throw-literal': 'error',
'no-trailing-spaces': 'error',
'no-undef-init': 'error',
'no-underscore-dangle': 'error',
'no-unsafe-finally': 'error',
'no-unused-labels': 'error',
'spaced-comment': 'error',
'use-isnan': 'error',
'max-lines': [
'error',
{
'max': 300,
'skipBlankLines': true,
'skipComments': true
}
],
'max-len': [
'warn',
{
'code': 140
}
],
'dot-notation': 'error',
'eqeqeq': 'error',
'eol-last': 'error',
'linebreak-style': ['error', 'windows'],
'block-spacing': ['error', 'always'],
'object-curly-spacing': ["error", "always"],
'import/no-deprecated': 'warn', // eslint deprecation rule sucks. just wrns on deprecated IMPORTs
},
settings: {
},
};

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

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

File diff suppressed because it is too large Load Diff

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

@ -0,0 +1,46 @@
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],
}
})
];

@ -0,0 +1,44 @@
export interface GlosSISettings {
controller: {
allowDesktopConfig: boolean;
emulateDS4: boolean;
maxControllers: number;
};
devices: {
hideDevices: boolean;
realDeviceIds: boolean;
};
extendedLogging: boolean;
globalModeGameId: string;
globalModeUseGamepadUI: boolean;
icon?: string;
ignoreEGS: boolean;
killEGS: boolean;
launch: {
closeOnExit: boolean;
ignoreLauncher: boolean;
killLauncher: boolean;
launch: boolean;
launchAppArgs?: string;
launchPath?: string;
launcherProcesses: string[];
waitForChildProcs: boolean;
};
name?: string;
snapshotNotify: boolean;
standaloneModeGameId: string;
standaloneUseGamepadUI: boolean;
minimizeSteamGamepadUI: boolean;
steamPath: string;
steamUserId: string;
steamgridApiKey: string;
version: number;
window: {
disableGlosSIOverlay: boolean;
disableOverlay: boolean;
hideAltTab: boolean;
maxFps?: number;
scale?: number;
windowMode: boolean;
};
}

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

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

@ -0,0 +1,21 @@
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));

@ -0,0 +1,33 @@
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));

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

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

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

@ -0,0 +1,12 @@
export const fetchWithTimeout = async (input: RequestInfo | URL, init?: RequestInit & { timeout: number }) => {
const { timeout = 8000 } = init || {};
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(input, {
...(init ||{}),
signal: controller.signal
});
clearTimeout(id);
return response;
};

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

@ -0,0 +1,41 @@
{
"version": "2.0.0",
"tasks": [
{
"type": "shell",
"label": "Build UWPOverlayEnablerDLL (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:UWPOverlayEnablerDLL",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
},
{
"type": "shell",
"label": "Re-Build UWPOverlayEnablerDLL (Debug)",
"command": "msbuild.exe",
"args": [
"GlosSI.sln",
"/target:UWPOverlayEnablerDLL:Rebuild",
"/p:Configuration=Debug",
"/p:Platform=x64"
],
"options": {
"cwd": "${workspaceFolder}/..",
},
"problemMatcher": ["$msCompile"],
"group": {
"kind": "build",
},
}
]
}

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

@ -44,7 +44,8 @@ There are two (known to me, at time of writing) ways to get a working overlay fo
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
#include <ShlObj.h>
#include "../common/util.h"
#define SUBHOOK_STATIC
#include <atomic>
@ -141,22 +142,7 @@ BOOL APIENTRY DllMain( HMODULE hModule,
{
if (ul_reason_for_call == DLL_PROCESS_ATTACH)
{
wchar_t* localAppDataFolder;
std::filesystem::path configDirPath;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
configDirPath = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
configDirPath = std::filesystem::path(localAppDataFolder).parent_path();
}
configDirPath /= "Roaming";
configDirPath /= "GlosSI";
if (!std::filesystem::exists(configDirPath))
std::filesystem::create_directories(configDirPath);
auto configDirPath = util::path::getDataDirPath();
auto logPath = configDirPath;
logPath /= "UWPOverlayEnabler.log";
const auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logPath.string(), true);

@ -1,8 +1,38 @@
diff --git a/src/ViGEmClient.vcxproj b/src/ViGEmClient.vcxproj
index 7c186414e62a6334fbcd518d506f55db57491dfe..6b71920a9b848e4a5a3ffb314a2e009a7a22502f 100644
--- a/src/ViGEmClient.vcxproj
+++ b/src/ViGEmClient.vcxproj
@@ -75,26 +75,26 @@
diff --git forkSrcPrefix/src/ViGEmClient.vcxproj forkDstPrefix/src/ViGEmClient.vcxproj
index 7c186414e62a6334fbcd518d506f55db57491dfe..2955df4d391b37bf0b71e17df8bd161c1014a0c4 100644
--- forkSrcPrefix/src/ViGEmClient.vcxproj
+++ forkDstPrefix/src/ViGEmClient.vcxproj
@@ -49,52 +49,52 @@
<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>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug_LIB|x64'" Label="Configuration">
<ConfigurationType>StaticLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>

@ -5,6 +5,10 @@ Remove-Item -Recurse -Force "x64\Release"
$env:_CL_="/MD"
msbuild.exe GlosSI.sln /t:Build /p:Configuration=Release /p:Platform=x64
cd ./SteamTweaks
npm i
npm run build
cd ..
cd ./x64/Release/
@ -26,6 +30,8 @@ Copy-Item "..\..\vc_redist.x64.exe" -Destination "."
Copy-Item "..\..\LICENSE" -Destination "./LICENSE"
Copy-Item "..\..\QT_License" -Destination "./QT_License"
Copy-Item "..\..\THIRD_PARTY_LICENSES.txt" -Destination "./THIRD_PARTY_LICENSES.txt"
Copy-Item "..\..\SteamTweaks\dist" -Destination "./SteamTweaks" -Recurse
#7z a GlosSI-snapshot.zip *

@ -19,6 +19,7 @@ Copy-Item "..\..\LICENSE" -Destination "./LICENSE"
Copy-Item "..\..\QT_License" -Destination "./QT_License"
Copy-Item "..\..\THIRD_PARTY_LICENSES.txt" -Destination "./THIRD_PARTY_LICENSES.txt"
Copy-Item "..\..\steamgrid.exe" -Destination "./steamgrid.exe"
Copy-Item "..\..\SteamTweaks\dist" -Destination "./SteamTweaks" -Recurse
Copy-Item "C:\Qt\Tools\OpenSSL\Win_x64\bin\libcrypto-1_1-x64.dll" -Destination "./libcrypto-1_1-x64.dll"
Copy-Item "C:\Qt\Tools\OpenSSL\Win_x64\bin\libssl-1_1-x64.dll" -Destination "./libssl-1_1-x64.dll"

@ -0,0 +1,33 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**",
"${workspaceFolder}/../deps/SFML/include",
"${workspaceFolder}/../deps/WinReg",
"${workspaceFolder}/../deps/spdlog/include",
"${workspaceFolder}/../deps/ValveFileVDF",
"${workspaceFolder}/../deps/subhook",
"${workspaceFolder}/../deps/ViGEmClient/include",
"${workspaceFolder}/../deps/imgui",
"${workspaceFolder}/../deps/imgui-sfml",
"${workspaceFolder}/../deps/json/include",
"${workspaceFolder}/../deps/traypp/tray/include",
"${workspaceFolder}/../deps/cpp-httplib",
"${workspaceFolder}/../CEFInjectLib"
],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"windowsSdkVersion": "10.0.18362.0",
"compilerPath": "C:/Program Files (x86)/Microsoft Visual Studio/2019/BuildTools/VC/Tools/MSVC/14.24.28314/bin/Hostx64/x64/cl.exe",
"cStandard": "c11",
"cppStandard": "c++20",
"intelliSenseMode": "msvc-x64"
}
],
"version": 4
}

@ -21,6 +21,8 @@ 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>
@ -29,7 +31,7 @@ limitations under the License.
#include <cfgmgr32.h>
#ifndef WATCHDOG
#include "Overlay.h"
#include "../GlosSITarget/Overlay.h"
#endif
#include "Settings.h"
@ -41,7 +43,7 @@ limitations under the License.
#include <atlbase.h>
#include "UnhookUtil.h"
#include "../common/UnhookUtil.h"
#pragma comment(lib, "Setupapi.lib")
@ -52,7 +54,7 @@ DEFINE_GUID(GUID_DEVINTERFACE_XUSB, 0xEC87F1E3, 0xC13B, 0x4100, 0xB5, 0xF7, 0x8B
// {00000000-0000-0000-FFFF-FFFFFFFFFFFF} the system container id
DEFINE_GUID(GUID_CONTAINER_ID_SYSTEM, 0x00000000, 0x0000, 0x0000, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
HidHide::HidHide(){};
HidHide::HidHide() {};
void HidHide::openCtrlDevice()
{
@ -85,7 +87,7 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path)
spdlog::info("Hiding devices is disabled; Not un-patching valve hooks, not looking for HidHide");
return;
}
spdlog::debug("Setting up device hiding...");
UnPatchValveHooks();
@ -112,12 +114,12 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path)
for (const auto& exe : whitelist_executeables_) {
auto path = std::regex_replace(steam_path_string, std::wregex(L"(.:)(\\/|\\\\)"), dos_device + L"\\");
path = std::regex_replace(path, std::wregex(L"\\/"), L"\\") + L"\\" + std::wstring{exe};
path = std::regex_replace(path, std::wregex(L"\\/"), L"\\") + L"\\" + std::wstring{ exe };
if (std::ranges::none_of(whitelist, [&path](auto ep) { // make copy!
auto p = path; // non-const(!) copy of path
std::ranges::transform(path, p.begin(), tolower);
std::ranges::transform(ep, ep.begin(), tolower);
return p == ep;
auto p = path; // non-const(!) copy of path
std::ranges::transform(path, p.begin(), tolower);
std::ranges::transform(ep, ep.begin(), tolower);
return p == ep;
})) {
whitelist.push_back(path);
}
@ -125,7 +127,7 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path)
if (Settings::common.extendedLogging) {
std::ranges::for_each(whitelist, [](const auto& exe) {
spdlog::trace(L"Whitelisted executable: {}", exe);
});
});
}
setAppWhiteList(whitelist);
@ -133,13 +135,13 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path)
if (Settings::common.extendedLogging) {
std::ranges::for_each(avail_devices_, [](const auto& dev) {
spdlog::trace(L"AvailDevice device: {}", dev.name);
});
});
}
blacklisted_devices_ = getBlackListDevices();
for (const auto& dev : avail_devices_) {
if (std::ranges::none_of(blacklisted_devices_, [&dev](const auto& blackdev) {
return blackdev == dev.device_instance_path || blackdev == dev.base_container_device_instance_path;
return blackdev == dev.device_instance_path || blackdev == dev.base_container_device_instance_path;
})) {
// Valve emulated gamepad PID/VID; mirrord by ViGEm
if (!(dev.vendor_id == 0x28de && (dev.product_id == 0x11FF || dev.product_id == 0x028E))) {
@ -160,7 +162,7 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path)
if (Settings::common.extendedLogging) {
std::ranges::for_each(blacklisted_devices_, [](const auto& dev) {
spdlog::trace(L"Blacklisted device: {}", dev);
});
});
}
}
closeCtrlDevice();
@ -199,80 +201,81 @@ void HidHide::enableOverlayElement()
{
Overlay::AddOverlayElem([this](bool window_has_focus, ImGuiID dockspace_id) {
ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
if (ImGui::Begin("Hidden Devices")) {
if (device_hiding_setup_) {
if (window_has_focus && (overlay_elem_clock_.getElapsedTime().asSeconds() > OVERLAY_ELEM_REFRESH_INTERVAL_S_)) {
// UnPatchValveHooks();
openCtrlDevice();
bool hidehide_state_store = hidhide_active_;
if (Settings::common.extendedLogging) {
spdlog::debug("Refreshing HID devices");
}
if (hidhide_active_) {
setActive(false);
}
avail_devices_ = GetHidDeviceList();
if (Settings::common.extendedLogging) {
std::ranges::for_each(avail_devices_, [](const auto& dev) {
spdlog::trace(L"AvailDevice device: {}", dev.name);
if (ImGui::Begin("Hidden Devices")) {
if (device_hiding_setup_) {
if (window_has_focus && (overlay_elem_clock_.getElapsedTime().asSeconds() > OVERLAY_ELEM_REFRESH_INTERVAL_S_)) {
// UnPatchValveHooks();
openCtrlDevice();
bool hidehide_state_store = hidhide_active_;
if (Settings::common.extendedLogging) {
spdlog::debug("Refreshing HID devices");
}
if (hidhide_active_) {
setActive(false);
}
avail_devices_ = GetHidDeviceList();
if (Settings::common.extendedLogging) {
std::ranges::for_each(avail_devices_, [](const auto& dev) {
spdlog::trace(L"AvailDevice device: {}", dev.name);
});
}
blacklisted_devices_ = getBlackListDevices();
if (hidehide_state_store && Settings::devices.hideDevices) {
setActive(true);
}
closeCtrlDevice();
overlay_elem_clock_.restart();
}
ImGui::BeginChild("Inner", {0.f, ImGui::GetItemRectSize().y - 64}, true);
std::ranges::for_each(avail_devices_, [this](const auto& device) {
std::string label = (std::string(device.name.begin(), std::ranges::find(device.name, L'\0')) + "##" + std::string(device.device_instance_path.begin(), device.device_instance_path.end()));
const auto findDeviceFn = [&device](const auto& blackdev) {
return device.device_instance_path == blackdev || device.base_container_device_instance_path == blackdev;
};
bool hidden = std::ranges::find_if(blacklisted_devices_, findDeviceFn) != blacklisted_devices_.end();
if (ImGui::Checkbox(label.data(), &hidden)) {
openCtrlDevice();
if (hidden) {
if (std::ranges::none_of(blacklisted_devices_, findDeviceFn)) {
if (!device.device_instance_path.empty()) {
blacklisted_devices_.push_back(device.device_instance_path);
}
if (!device.device_instance_path.empty()) {
blacklisted_devices_.push_back(device.base_container_device_instance_path);
}
}
}
else {
blacklisted_devices_.erase(std::ranges::remove_if(blacklisted_devices_, findDeviceFn).begin(),
blacklisted_devices_.end());
blacklisted_devices_ = getBlackListDevices();
if (hidehide_state_store && Settings::devices.hideDevices) {
setActive(true);
}
closeCtrlDevice();
overlay_elem_clock_.restart();
}
ImGui::BeginChild("Inner", { 0.f, ImGui::GetItemRectSize().y - 64 }, true);
std::ranges::for_each(avail_devices_, [this](const auto& device) {
std::string label = (std::string(device.name.begin(), std::ranges::find(device.name, L'\0')) + "##" + std::string(device.device_instance_path.begin(), device.device_instance_path.end()));
const auto findDeviceFn = [&device](const auto& blackdev) {
return device.device_instance_path == blackdev || device.base_container_device_instance_path == blackdev;
};
bool hidden = std::ranges::find_if(blacklisted_devices_, findDeviceFn) != blacklisted_devices_.end();
if (ImGui::Checkbox(label.data(), &hidden)) {
openCtrlDevice();
if (hidden) {
if (std::ranges::none_of(blacklisted_devices_, findDeviceFn)) {
if (!device.device_instance_path.empty()) {
blacklisted_devices_.push_back(device.device_instance_path);
}
setBlacklistDevices(blacklisted_devices_);
if (Settings::common.extendedLogging) {
std::ranges::for_each(blacklisted_devices_, [](const auto& dev) {
spdlog::trace(L"Blacklisted device: {}", dev);
});
if (!device.device_instance_path.empty()) {
blacklisted_devices_.push_back(device.base_container_device_instance_path);
}
closeCtrlDevice();
}
});
ImGui::EndChild();
} else {
ImGui::Text("Enable \"Hide Devices\" to see a list of gaming-devices");
}
if (ImGui::Checkbox("Hide devices", &Settings::devices.hideDevices)) {
if (!device_hiding_setup_) {
hideDevices(steam_path_);
}
if (hidhide_active_ != Settings::devices.hideDevices) {
openCtrlDevice();
setActive(Settings::devices.hideDevices);
closeCtrlDevice();
else {
blacklisted_devices_.erase(std::ranges::remove_if(blacklisted_devices_, findDeviceFn).begin(),
blacklisted_devices_.end());
}
setBlacklistDevices(blacklisted_devices_);
if (Settings::common.extendedLogging) {
std::ranges::for_each(blacklisted_devices_, [](const auto& dev) {
spdlog::trace(L"Blacklisted device: {}", dev);
});
}
closeCtrlDevice();
}
});
ImGui::EndChild();
}
else {
ImGui::Text("Enable \"Hide Devices\" to see a list of gaming-devices");
}
if (ImGui::Checkbox("Hide devices", &Settings::devices.hideDevices)) {
if (!device_hiding_setup_) {
hideDevices(steam_path_);
}
if (hidhide_active_ != Settings::devices.hideDevices) {
openCtrlDevice();
setActive(Settings::devices.hideDevices);
closeCtrlDevice();
}
}
ImGui::End();
});
}
ImGui::End();
});
}
#endif
@ -280,7 +283,7 @@ std::wstring HidHide::DosDeviceForVolume(const std::wstring& volume)
{
std::vector<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
@ -291,7 +294,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>{};
}
@ -306,7 +309,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>{};
}
@ -318,7 +321,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;
}
@ -331,7 +334,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");
}
}
@ -341,7 +344,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");
}
}
@ -350,7 +353,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;
}
@ -394,9 +397,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;
}
@ -426,7 +429,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{};
@ -443,7 +446,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;
@ -504,8 +507,8 @@ HidHide::SmallHidInfo HidHide::GetDeviceInfo(const DeviceInstancePath& instance_
std::wstring buffer;
buffer.resize(127 * sizeof WCHAR);
res.name = (HidD_GetProductString(device_object.get(), buffer.data(), static_cast<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);
@ -566,13 +569,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)
@ -580,7 +583,7 @@ HidHide::DeviceInstancePath HidHide::BaseContainerDeviceInstancePath(DeviceInsta
const GUID base_container_id(BaseContainerId(device_instance_path));
if ((GUID_NULL == base_container_id) || (GUID_CONTAINER_ID_SYSTEM == base_container_id))
return (std::wstring{});
for (auto it{device_instance_path};;) {
for (auto it{ device_instance_path };;) {
if (const auto device_instance_path_parent = DeviceInstancePathParent(it); (base_container_id == BaseContainerId(device_instance_path_parent)))
it = device_instance_path_parent;
else
@ -597,7 +600,7 @@ GUID HidHide::BaseContainerId(DeviceInstancePath const& device_instance_path)
DEVINST devInst{};
DEVPROPTYPE devPropType{};
GUID buffer{};
ULONG needed{sizeof(buffer)};
ULONG needed{ sizeof(buffer) };
if (const auto result = CM_Locate_DevNodeW(&devInst, const_cast<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 {};
@ -624,7 +627,7 @@ HidHide::DeviceInstancePath HidHide::DeviceInstancePathParent(DeviceInstancePath
DEVINST dev_inst_parent{};
std::wstring res;
res.resize(UNICODE_STRING_MAX_CHARS);
ULONG needed{static_cast<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 {};

@ -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 @@ class HidHide {
bool gaming_device = false;
};
public:
public:
HidHide();
void openCtrlDevice();
@ -72,7 +72,7 @@ class HidHide {
void disableHidHide();
// TODO: MAYBE: restore hidhide state/lists when app closes. not only disable device_hiding
private:
private:
HANDLE hidhide_handle = nullptr;
std::filesystem::path steam_path_;
@ -89,12 +89,12 @@ class HidHide {
std::vector<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);

@ -0,0 +1,379 @@
/*
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

@ -13,9 +13,13 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "UnhookUtil.h"
#include "../common/UnhookUtil.h"
#include "util.h"
#ifndef CONFIGAPP
#define SPDLOG_WCHAR_TO_UTF8_SUPPORT
#define SPDLOG_WCHAR_FILENAMES
#include <spdlog/spdlog.h>
#include "Settings.h"
@ -28,17 +32,7 @@ void UnhookUtil::UnPatchHook(const std::string& name, HMODULE module)
std::map<std::string, std::string> original_bytes_from_file;
wchar_t* localAppDataFolder;
std::filesystem::path configDirPath;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, NULL, &localAppDataFolder) != S_OK) {
configDirPath = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
configDirPath = std::filesystem::path(localAppDataFolder).parent_path();
}
configDirPath /= "Roaming";
configDirPath /= "GlosSI";
auto configDirPath = util::path::getDataDirPath();
if (std::filesystem::exists(configDirPath)) {
auto unhook_file_path = configDirPath / "unhook_bytes";
if (std::filesystem::exists(unhook_file_path)) {
@ -56,7 +50,8 @@ void UnhookUtil::UnPatchHook(const std::string& name, HMODULE module)
ifile.read(&buff, sizeof(char));
if (buff != ':') {
funcName.push_back(buff);
} else {
}
else {
char bytes[8];
ifile.read(bytes, sizeof(char) * 8);
ifile.read(&buff, sizeof(char)); // newline

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

@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.props" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.props')" />
<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>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{dfed4b7e-d04c-442b-bb48-5b6068a6b31b}</ProjectGuid>
<RootNamespace>common</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Utility</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Utility</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\json\include;..\deps\spdlog\include;..\deps\SFML\include;..\deps\imgui;..\deps\WinReg;..\deps\ValveFileVDF;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>..\deps\json\include;..\deps\spdlog\include;..\deps\SFML\include;..\deps\imgui;..\deps\WinReg;..\deps\ValveFileVDF;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</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;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</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>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</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>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="HidHide.h" />
<ClInclude Include="nlohmann_json_wstring.h" />
<ClInclude Include="Settings.h" />
<ClInclude Include="steam_util.h" />
<ClInclude Include="UnhookUtil.h" />
<ClInclude Include="util.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="HidHide.cpp" />
<ClCompile Include="UnhookUtil.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
<Import Project="..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.targets" Condition="Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.targets')" />
</ImportGroup>
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
</PropertyGroup>
<Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.props')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.props'))" />
<Error Condition="!Exists('..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\packages\Microsoft.Windows.CppWinRT.2.0.220531.1\build\native\Microsoft.Windows.CppWinRT.targets'))" />
</Target>
</Project>

@ -0,0 +1,45 @@
<?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="nlohmann_json_wstring.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="util.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UnhookUtil.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Settings.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="HidHide.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="steam_util.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="HidHide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
</Project>

@ -0,0 +1,36 @@
/*
Copyright 2021-2023 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <nlohmann/json.hpp>
#include "util.h"
namespace nlohmann {
template <>
struct adl_serializer<std::wstring> {
static void to_json(json& j, const std::wstring& str) {
j = util::string::to_string(str);
}
static void from_json(const json& j, std::wstring& str) {
str = util::string::to_wstring(j.get<std::string>());
}
};
}

@ -0,0 +1,221 @@
/*
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 <WinReg/WinReg.hpp>
#include <vdf_parser.hpp>
#include "util.h"
#include "Settings.h"
namespace util
{
namespace steam
{
static constexpr std::wstring_view user_data_path = L"/userdata/";
static constexpr std::wstring_view config_file_name = L"/config/localconfig.vdf";
static constexpr std::string_view overlay_hotkey_name = "InGameOverlayShortcutKey ";
static constexpr std::string_view screenshot_hotkey_name = "InGameOverlayScreenshotHotKey ";
inline std::filesystem::path getSteamPath()
{
#ifdef _WIN32
try {
// TODO: check if keys/value exist
// steam should always be open and have written reg values...
winreg::RegKey key{ HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam" };
const auto res = key.GetStringValue(L"SteamPath");
spdlog::info(L"Detected Steam Path: {}", res);
return res;
}
catch (const winreg::RegException& e) {
spdlog::error("Couldn't get Steam path from Registry; {}", e.what());
}
return Settings::common.steamPath;
#else
return L""; // TODO
#endif
}
inline std::wstring getSteamUserId()
{
#ifdef _WIN32
try {
// TODO: check if keys/value exist
// steam should always be open and have written reg values...
winreg::RegKey key{ HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess" };
const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser"));
spdlog::info(L"Detected Steam UserId: {}", res);
return res;
}
catch (const winreg::RegException& e) {
spdlog::error("Couldn't get Steam path from Registry; {}", e.what());
}
return Settings::common.steamUserId;
#else
return L""; // TODO
#endif
}
inline std::vector<std::string> getOverlayHotkey(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamPath())
{
const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name);
if (!std::filesystem::exists(config_path)) {
spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path);
return { "Shift", "KEY_TAB" }; // default
}
std::ifstream config_file(config_path);
auto root = tyti::vdf::read(config_file);
std::shared_ptr<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
}
auto hotkeys = children->attribs.at("InGameOverlayShortcutKey");
// has anyone more than 4 keys to open overlay?!
std::smatch m;
if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) {
spdlog::warn("Couldn't detect overlay hotkey, using default: Shift+Tab");
return { "Shift", "KEY_TAB" }; // default
}
std::vector<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;
}
inline std::vector<std::string> getScreenshotHotkey(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamPath())
{
const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name);
if (!std::filesystem::exists(config_path)) {
spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path);
return { "KEY_F12" }; // default
}
std::ifstream config_file(config_path);
auto root = tyti::vdf::read(config_file);
std::shared_ptr<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
}
auto hotkeys = children->attribs.at("InGameOverlayScreenshotHotKey");
// has anyone more than 4 keys to screenshot?!
std::smatch m;
if (!std::regex_match(hotkeys, m, std::regex(R"((\w*)\s*(\w*)\s*(\w*)\s*(\w*))"))) {
spdlog::warn("Couldn't detect overlay hotkey, using default: F12");
return { "KEY_F12" }; // default
}
std::vector<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: F12");
return { "KEY_F12" }; // default
}
spdlog::info("Detected screenshot hotkey(s): {}", std::accumulate(
res.begin() + 1, res.end(), res[0],
[](auto acc, const auto curr) { return acc += "+" + curr; }));
return res;
}
inline bool getXBCRebindingEnabled(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamPath())
{
const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name);
if (!std::filesystem::exists(config_path)) {
spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path);
return false;
}
std::ifstream config_file(config_path);
auto root = tyti::vdf::read(config_file);
if (root.attribs.empty() || !root.attribs.contains("SteamController_XBoxSupport")) {
spdlog::warn("\"Xbox Configuration Support\" is disabled in Steam. This may cause doubled Inputs!");
return false;
}
auto xbsup = root.attribs.at("SteamController_XBoxSupport");
if (xbsup != "1") {
spdlog::warn("\"Xbox Configuration Support\" is disabled in Steam. This may cause doubled Inputs!");
}
return xbsup == "1";
}
inline nlohmann::json getSteamConfig(const std::wstring& steam_path = getSteamPath(), const std::wstring& steam_user_id = getSteamUserId())
{
const auto config_path = std::wstring(steam_path) + std::wstring(user_data_path) + steam_user_id + std::wstring(config_file_name);
if (!std::filesystem::exists(config_path)) {
spdlog::warn(L"Couldn't read Steam config file: \"{}\"", config_path);
return nlohmann::json();
}
std::ifstream config_file(config_path);
auto root = tyti::vdf::read(config_file);
if (root.attribs.empty())
{
return {};
}
auto res = nlohmann::json::object();
res[root.name] = nlohmann::json::object();
for (auto& [key, value] : root.attribs)
{
res[root.name][key] = value;
}
auto parse_child = [](nlohmann::json& j, std::shared_ptr<tyti::vdf::basic_object<char>> child, auto&& recurse) -> void
{
for (auto& [key, value] : child->attribs)
{
j[key] = value;
for (auto& [childkey, childval] : child->childs)
{
j[childkey] = {};
recurse(j[childkey], childval, recurse);
}
}
};
for (auto& [key, value] : root.childs)
{
res[root.name][key] = {};
parse_child(res[root.name][key], value, parse_child);
}
return res;
}
}
}

@ -0,0 +1,173 @@
/*
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 <locale>
#include <codecvt>
#ifdef _WIN32
#define WIN32_LEAN_AND_MEAN
#define NOMINMAX
#include <Windows.h>
#include <tlhelp32.h>
#include <ShlObj.h>
#include <KnownFolders.h>
#endif
#include <filesystem>
#ifdef SPDLOG_H
#define SPDLOG_WCHAR_TO_UTF8_SUPPORT
#define SPDLOG_WCHAR_FILENAMES
#include <spdlog/spdlog.h>
#endif
namespace util {
namespace string
{
template <typename T>
inline std::wstring to_wstring(const T& t)
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.from_bytes(t);
}
template <typename T>
inline std::string to_string(const T& t)
{
std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
return converter.to_bytes(t);
}
}
namespace path
{
inline std::filesystem::path getDataDirPath()
{
wchar_t* localAppDataFolder;
std::filesystem::path path;
if (SHGetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_CREATE, nullptr, &localAppDataFolder) != S_OK) {
path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
}
else {
path = std::filesystem::path(localAppDataFolder).parent_path();
}
path /= "Roaming";
path /= "GlosSI";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
return path;
}
inline std::filesystem::path getGlosSIDir()
{
wchar_t result[MAX_PATH];
std::filesystem::path res{ std::wstring{result, GetModuleFileNameW(NULL, result, MAX_PATH)} };
return res.parent_path();
}
}
#ifdef _WIN32
namespace win
{
typedef LONG NTSTATUS, * PNTSTATUS;
#define STATUS_SUCCESS (0x00000000)
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
inline RTL_OSVERSIONINFOW GetRealOSVersion()
{
HMODULE hMod = ::GetModuleHandleW(L"ntdll.dll");
if (hMod) {
RtlGetVersionPtr fxPtr = (RtlGetVersionPtr)::GetProcAddress(hMod, "RtlGetVersion");
if (fxPtr != nullptr) {
RTL_OSVERSIONINFOW rovi = { 0 };
rovi.dwOSVersionInfoSize = sizeof(rovi);
if (STATUS_SUCCESS == fxPtr(&rovi)) {
return rovi;
}
}
}
RTL_OSVERSIONINFOW rovi = { 0 };
return rovi;
}
namespace process
{
inline DWORD PidByName(const std::wstring& name)
{
PROCESSENTRY32 entry;
entry.dwSize = sizeof(PROCESSENTRY32);
HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (Process32First(snapshot, &entry) == TRUE) {
while (Process32Next(snapshot, &entry) == TRUE) {
if (std::wstring(entry.szExeFile).find(name) != std::string::npos) {
return entry.th32ProcessID;
}
}
}
CloseHandle(snapshot);
return 0;
}
inline std::wstring GetProcName(DWORD pid)
{
PROCESSENTRY32 processInfo;
processInfo.dwSize = sizeof(processInfo);
const HANDLE processesSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL);
if (processesSnapshot == INVALID_HANDLE_VALUE) {
#ifdef SPDLOG_H
spdlog::trace("util::GetProcName: can't get a process snapshot");
#endif
return L"";
}
for (BOOL bok = Process32First(processesSnapshot, &processInfo);
bok;
bok = Process32Next(processesSnapshot, &processInfo)) {
if (pid == processInfo.th32ProcessID) {
CloseHandle(processesSnapshot);
return processInfo.szExeFile;
}
}
CloseHandle(processesSnapshot);
return L"";
}
inline bool KillProcess(DWORD pid)
{
auto res = true;
if (const auto proc = OpenProcess(PROCESS_TERMINATE, FALSE, pid)) {
#ifdef SPDLOG_H
spdlog::debug("Terminating process: {}", pid);
#endif
res = TerminateProcess(proc, 0);
if (!res) {
#ifdef SPDLOG_H
spdlog::error("Failed to terminate process: {}", pid);
#endif
}
CloseHandle(proc);
}
return res;
}
}
}
#endif
}

2
deps/ViGEmClient vendored

@ -1 +1 @@
Subproject commit 3bc2cee48ab0b10b5dd31323a621677175cfb00d
Subproject commit 20fb9cb05275acad6ab5c786160656d8f83e0c6f

1
deps/easywsclient vendored

@ -0,0 +1 @@
Subproject commit afc1d8cfc584e0f1f4a77e8c0ce3e979d9fe7ce2

@ -1,3 +1,3 @@
Invoke-WebRequest -o ViGEmBusSetup_x64.exe https://github.com/ViGEm/ViGEmBus/releases/download/v1.21.442.0/ViGEmBus_1.21.442_x64_x86_arm64.exe
Invoke-WebRequest -o HidHideSetup.exe https://github.com/ViGEm/HidHide/releases/download/v1.2.98.0/HidHide_1.2.98_x64.exe
Invoke-WebRequest -o ViGEmBusSetup_x64.exe https://github.com/nefarius/ViGEmBus/releases/download/v1.22.0/ViGEmBus_1.22.0_x64_x86_arm64.exe
Invoke-WebRequest -o HidHideSetup.exe https://github.com/nefarius/HidHide/releases/download/v1.4.192.0/HidHide_1.4.192_x64.exe
Invoke-WebRequest -o vc_redist.x64.exe https://aka.ms/vs/16/release/vc_redist.x64.exe

@ -1,15 +0,0 @@
{
"folders": [
{
"path": "GlosSIConfig"
},
{
"path": "GlosSITarget"
},
{
"name": "root",
"path": "."
}
],
"settings": {}
}

@ -0,0 +1,137 @@
{
"folders": [
{
"path": "common"
},
{
"path": "CEFInjectLib"
},
{
"path": "GlosSIConfig"
},
{
"path": "GlosSITarget"
},
{
"path": "GlosSIWatchdog"
},
{
"path": "UWPOverlayEnablerDLL"
},
{
"path": "Installer"
},
{
"path": "SteamTweaks"
},
{
"name": "root",
"path": "."
},
],
"settings": {
"files.associations": {
"algorithm": "cpp",
"chrono": "cpp",
"filesystem": "cpp",
"xstring": "cpp",
"xutility": "cpp",
"xhash": "cpp",
"xtree": "cpp",
"map": "cpp",
"any": "cpp",
"array": "cpp",
"atomic": "cpp",
"bit": "cpp",
"cctype": "cpp",
"charconv": "cpp",
"clocale": "cpp",
"cmath": "cpp",
"codecvt": "cpp",
"compare": "cpp",
"concepts": "cpp",
"condition_variable": "cpp",
"coroutine": "cpp",
"csignal": "cpp",
"cstdarg": "cpp",
"cstddef": "cpp",
"cstdint": "cpp",
"cstdio": "cpp",
"cstdlib": "cpp",
"cstring": "cpp",
"ctime": "cpp",
"cwchar": "cpp",
"deque": "cpp",
"exception": "cpp",
"resumable": "cpp",
"format": "cpp",
"forward_list": "cpp",
"fstream": "cpp",
"functional": "cpp",
"future": "cpp",
"initializer_list": "cpp",
"iomanip": "cpp",
"ios": "cpp",
"iosfwd": "cpp",
"iostream": "cpp",
"istream": "cpp",
"iterator": "cpp",
"limits": "cpp",
"list": "cpp",
"locale": "cpp",
"memory": "cpp",
"mutex": "cpp",
"new": "cpp",
"numeric": "cpp",
"optional": "cpp",
"ostream": "cpp",
"queue": "cpp",
"random": "cpp",
"ranges": "cpp",
"ratio": "cpp",
"regex": "cpp",
"set": "cpp",
"span": "cpp",
"sstream": "cpp",
"stack": "cpp",
"stdexcept": "cpp",
"stop_token": "cpp",
"streambuf": "cpp",
"string": "cpp",
"system_error": "cpp",
"thread": "cpp",
"tuple": "cpp",
"type_traits": "cpp",
"typeinfo": "cpp",
"unordered_map": "cpp",
"unordered_set": "cpp",
"utility": "cpp",
"valarray": "cpp",
"variant": "cpp",
"vector": "cpp",
"xfacet": "cpp",
"xiosbase": "cpp",
"xlocale": "cpp",
"xlocbuf": "cpp",
"xlocinfo": "cpp",
"xlocmes": "cpp",
"xlocmon": "cpp",
"xlocnum": "cpp",
"xloctime": "cpp",
"xmemory": "cpp",
"xstddef": "cpp",
"xtr1common": "cpp"
}
// uncomment if not already in user settings and wanting to debug/build c++ code (vs 2022 required!)
// "terminal.integrated.profiles.windows": {
// "Developer PowerShell for VS 2022": {
// "source": "PowerShell",
// "icon": "terminal-powershell",
// "args": [
// "-c",
// "$vsPath = & '${env:ProgramFiles(x86)}/Microsoft Visual Studio/Installer/vswhere.exe' -property installationpath; Import-Module \"$vsPath/Common7/Tools/Microsoft.VisualStudio.DevShell.dll\"; Enter-VsDevShell -VsInstallPath $vsPath -SkipAutomaticLocation; powershell.exe"
// ]
// }
// },
}
}

@ -0,0 +1 @@
Get-Content "$env:appdata\GlosSI\glossiconfig.log" -Wait

@ -0,0 +1 @@
Get-Content "$env:appdata\GlosSI\GlosSItarget.log" -Wait

@ -1,4 +1,4 @@
$tag = git describe --tags --always
$tag = git describe --tags --always $(git rev-list --all --max-count=1)
if (-Not ($tag -match ".+\..+\..+\..+")) {
$tag = "0.0.0." + $tag
}

Loading…
Cancel
Save