Merge pull request #183 from Alia5/develop

Develop
pull/184/head
Peter Repukat 2 years ago committed by GitHub
commit 4aef461a3d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

5
.gitignore vendored

@ -1,3 +1,5 @@
version.hpp
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
@ -366,4 +368,5 @@ MigrationBackup/
.ionide/
# Fody - auto-generated XML schema
FodyWeavers.xsd
FodyWeavers.xsd
.visuallint

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

@ -1,7 +1,7 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31729.503
# Visual Studio Version 17
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}"
EndProject
@ -9,6 +9,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GlosSIConfig", "GlosSIConfi
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
@ -39,12 +41,20 @@ Global
{50212575-87E2-40AB-87EE-EAED19C8EBB2}.Release|x64.Build.0 = Release|x64
{50212575-87E2-40AB-87EE-EAED19C8EBB2}.Release|x86.ActiveCfg = Release|Win32
{50212575-87E2-40AB-87EE-EAED19C8EBB2}.Release|x86.Build.0 = Release|Win32
{BF273B90-CB69-43C8-9AF6-F3256DAFD41E}.Debug|x64.ActiveCfg = Debug|x64
{BF273B90-CB69-43C8-9AF6-F3256DAFD41E}.Debug|x64.Build.0 = Debug|x64
{BF273B90-CB69-43C8-9AF6-F3256DAFD41E}.Debug|x86.ActiveCfg = Debug|Win32
{BF273B90-CB69-43C8-9AF6-F3256DAFD41E}.Debug|x86.Build.0 = Debug|Win32
{BF273B90-CB69-43C8-9AF6-F3256DAFD41E}.Release|x64.ActiveCfg = Release|x64
{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
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {789386B6-7D1E-4F9C-BF2E-9B5EDC3BB7C8}
Qt5Version = 6.2.2_msvc2019_64
SolutionGuid = {789386B6-7D1E-4F9C-BF2E-9B5EDC3BB7C8}
EndGlobalSection
EndGlobal

@ -4,5 +4,5 @@ IndentWidth: 4
BreakBeforeBraces: "Stroustrup"
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
ColumnLimit: 0
ColumnLimit: 120
PointerAlignment: "Left"

Binary file not shown.

Before

Width:  |  Height:  |  Size: 54 KiB

@ -57,9 +57,11 @@
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<CustomBuildBeforeTargets>ResourceCompile</CustomBuildBeforeTargets>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<CustomBuildBeforeTargets>ResourceCompile</CustomBuildBeforeTargets>
<LibraryPath>$(LibraryPath)</LibraryPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
@ -81,6 +83,7 @@
</CustomBuildStep>
<Link>
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
@ -99,6 +102,7 @@
<Link>
<UACExecutionLevel>AsInvoker</UACExecutionLevel>
<GenerateWindowsMetadata>true</GenerateWindowsMetadata>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<CustomBuildStep>
<Command>powershell.exe $(SolutionDir)version_help.ps1</Command>
@ -137,7 +141,9 @@
<ClCompile Include="UIModel.cpp" />
<None Include=".clang-format" />
<None Include="GetAUMIDs.ps1" />
<None Include="qml\AdvancedTargetSettings.qml" />
<None Include="qml\CollapsiblePane.qml" />
<None Include="qml\GlobalConf.qml" />
<None Include="qml\SteamInputXboxDisabledDialog.qml" />
<None Include="qml\AddSelectTypeDialog.qml" />
<None Include="qml\FluentTextInput.qml" />
@ -166,7 +172,7 @@
<ResourceCompile Include="Resource.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="$(SolutionDir)\GloSC_Icon.ico" />
<Image Include="..\GlosSI_Icon.ico" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')">

@ -74,6 +74,12 @@
<None Include="qml\CollapsiblePane.qml">
<Filter>qml</Filter>
</None>
<None Include="qml\AdvancedTargetSettings.qml">
<Filter>qml</Filter>
</None>
<None Include="qml\GlobalConf.qml">
<Filter>qml</Filter>
</None>
</ItemGroup>
<ItemGroup>
<QtMoc Include="UIModel.h">
@ -105,7 +111,7 @@
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Image Include="$(SolutionDir)\GloSC_Icon.ico">
<Image Include="..\GlosSI_Icon.ico">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

Binary file not shown.

@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,0,8,1023005406006
PRODUCTVERSION 0,0,8,1023005406006
FILEVERSION 0,0,9,1037005000102
PRODUCTVERSION 0,0,9,1037005000102
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -69,12 +69,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Peter Repukat - FlatspotSoftware"
VALUE "FileDescription", "GlosSI - Config"
VALUE "FileVersion", "0.0.8.1-23-g54e6bf6"
VALUE "FileVersion", "0.0.9.1-37-g5ebe102"
VALUE "InternalName", "GlosSIConfig"
VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware"
VALUE "OriginalFilename", "GlosSIConfig.exe"
VALUE "ProductName", "GlosSI"
VALUE "ProductVersion", "0.0.8.1-23-g54e6bf6"
VALUE "ProductVersion", "0.0.9.1-37-g5ebe102"
END
END
BLOCK "VarFileInfo"
@ -91,7 +91,7 @@ END
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1 ICON "..\GloSC_Icon.ico"
IDI_ICON1 ICON "..\GlosSI_Icon.ico"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////

@ -18,26 +18,31 @@ limitations under the License.
#include <QDir>
#include <QGuiApplication>
#include <QJsonDocument>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <WinReg/WinReg.hpp>
#include <ranges>
#ifdef _WIN32
#include "UWPFetch.h"
#include <Windows.h>
#endif
#include "../version.hpp"
UIModel::UIModel() : QObject(nullptr)
{
auto path = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
auto path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
path /= "Roaming";
path /= "GlosSI";
if (!std::filesystem::exists(path))
std::filesystem::create_directories(path);
qDebug() << "Version: " << getVersionString();
config_path_ = path;
config_dir_name_ = QString::fromStdWString((path /= "Targets").wstring());
@ -46,15 +51,14 @@ UIModel::UIModel() : QObject(nullptr)
parseShortcutVDF();
readTargetConfigs();
updateCheck();
}
void UIModel::readTargetConfigs()
{
QDir dir(config_dir_name_);
auto entries = dir.entryList(QDir::Files, QDir::SortFlag::Name);
entries.removeIf([](const auto& entry) {
return !entry.endsWith(".json");
});
entries.removeIf([](const auto& entry) { return !entry.endsWith(".json"); });
std::for_each(entries.begin(), entries.end(), [this](const auto& name) {
auto path = config_path_;
@ -70,9 +74,8 @@ void UIModel::readTargetConfigs()
const auto jsondoc = QJsonDocument::fromJson(data);
auto filejson = jsondoc.object();
filejson["name"] = filejson.contains("name")
? filejson["name"].toString()
: QString(name).replace(QRegularExpression("\\.json"), "");
filejson["name"] = filejson.contains("name") ? filejson["name"].toString()
: QString(name).replace(QRegularExpression("\\.json"), "");
targets_.append(filejson.toVariantMap());
});
@ -80,10 +83,7 @@ void UIModel::readTargetConfigs()
emit targetListChanged();
}
QVariantList UIModel::getTargetList() const
{
return targets_;
}
QVariantList UIModel::getTargetList() const { return targets_; }
void UIModel::addTarget(QVariant shortcut)
{
@ -99,7 +99,8 @@ void UIModel::updateTarget(int index, QVariant shortcut)
const auto map = shortcut.toMap();
const auto json = QJsonObject::fromVariantMap(map);
auto oldName = targets_[index].toMap()["name"].toString().replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json";
auto oldName =
targets_[index].toMap()["name"].toString().replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json";
auto path = config_path_;
path /= config_dir_name_.toStdString();
path /= (oldName).toStdString();
@ -113,7 +114,8 @@ void UIModel::updateTarget(int index, QVariant shortcut)
void UIModel::deleteTarget(int index)
{
auto oldName = targets_[index].toMap()["name"].toString().replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json";
auto oldName =
targets_[index].toMap()["name"].toString().replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json";
auto path = config_path_;
path /= config_dir_name_.toStdString();
path /= (oldName).toStdString();
@ -147,11 +149,13 @@ bool UIModel::addToSteam(QVariant shortcut, const QString& shortcutspath, bool f
VDFParser::Shortcut vdfshortcut;
vdfshortcut.appname = name.toStdString();
vdfshortcut.exe = ("\"" + appDir.absolutePath() + "/GlosSITarget.exe" + "\"").toStdString();
vdfshortcut.StartDir = (launch && !maybeLaunchPath.isEmpty()
? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"")
: ("\"" + appDir.absolutePath() + "\"").toStdString());
vdfshortcut.StartDir =
(launch && !maybeLaunchPath.isEmpty()
? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"")
: ("\"" + appDir.absolutePath() + "\"").toStdString());
// ShortcutPath; default
vdfshortcut.LaunchOptions = (QString(name).replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json").toStdString();
vdfshortcut.LaunchOptions =
(QString(name).replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json").toStdString();
// IsHidden; default
// AllowDesktopConfig; default
// AllowOverlay; default
@ -164,11 +168,16 @@ bool UIModel::addToSteam(QVariant shortcut, const QString& shortcutspath, bool f
if (maybeIcon.isEmpty()) {
if (launch && !maybeLaunchPath.isEmpty())
vdfshortcut.icon =
"\"" + (is_windows_ ? QString(maybeLaunchPath).replace(QRegularExpression("\\/"), "\\").toStdString() : maybeLaunchPath.toStdString()) + "\"";
"\"" +
(is_windows_ ? QString(maybeLaunchPath).replace(QRegularExpression("\\/"), "\\").toStdString()
: maybeLaunchPath.toStdString()) +
"\"";
}
else {
vdfshortcut.icon =
"\"" + (is_windows_ ? QString(maybeIcon).replace(QRegularExpression("\\/"), "\\").toStdString() : maybeIcon.toStdString()) + "\"";
vdfshortcut.icon = "\"" +
(is_windows_ ? QString(maybeIcon).replace(QRegularExpression("\\/"), "\\").toStdString()
: maybeIcon.toStdString()) +
"\"";
}
// Add installed locally and GlosSI tag
vdfshortcut.tags.push_back("Installed locally");
@ -195,10 +204,11 @@ bool UIModel::addToSteam(const QString& name, const QString& shortcutspath, bool
bool UIModel::removeFromSteam(const QString& name, const QString& shortcutspath, bool from_cmd)
{
qDebug() << "trying to remove " << name << " from steam";
shortcuts_vdf_.erase(std::ranges::remove_if(shortcuts_vdf_, [&name](const auto& shortcut) {
return shortcut.appname == name.toStdString();
}).begin(),
shortcuts_vdf_.end());
shortcuts_vdf_.erase(
std::ranges::remove_if(shortcuts_vdf_,
[&name](const auto& shortcut) { return shortcut.appname == name.toStdString(); })
.begin(),
shortcuts_vdf_.end());
return writeShortcutsVDF(L"remove", name.toStdWString(), shortcutspath.toStdWString(), from_cmd);
}
@ -214,17 +224,21 @@ QVariantMap UIModel::manualProps(QVariant shortcut)
res.insert("name", name);
res.insert("config", name + ".json");
res.insert("launch", ("\"" + appDir.absolutePath() + "/GlosSITarget.exe" + "\""));
res.insert("launchDir", (
launch && !maybeLaunchPath.isEmpty()
? (QString("\"") + QString::fromStdString(std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string()) + "\"")
: ("\"" + appDir.absolutePath() + "\"")));
res.insert(
"launchDir",
(launch && !maybeLaunchPath.isEmpty()
? (QString("\"") +
QString::fromStdString(std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string()) +
"\"")
: ("\"" + appDir.absolutePath() + "\"")));
return res;
}
void UIModel::enableSteamInputXboxSupport()
{
if (foundSteam()) {
const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + user_config_file_.toStdWString();
const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() +
getSteamUserId() + user_config_file_.toStdWString();
if (!std::filesystem::exists(config_path)) {
qDebug() << "localconfig.vdf does not exist.";
}
@ -261,19 +275,130 @@ void UIModel::enableSteamInputXboxSupport()
}
}
#ifdef _WIN32
QVariantList UIModel::uwpApps()
bool UIModel::restartSteam()
{
const auto path = getSteamPath();
if (QProcess::execute("taskkill.exe", {"/im", steam_executable_name_, "/f"}) != 0) {
return false;
}
QProcess::startDetached(QString::fromStdWString(path) + "/" + steam_executable_name_);
return true;
}
void UIModel::updateCheck()
{
auto manager = new QNetworkAccessManager();
QNetworkRequest request;
QNetworkReply* reply = NULL;
QSslConfiguration config = QSslConfiguration::defaultConfiguration();
config.setProtocol(QSsl::TlsV1_2);
request.setSslConfiguration(config);
request.setUrl(QUrl("https://glossi.1-3-3-7.dev/api/availFiles"));
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
reply = manager->get(request);
// connect(
// manager, &QNetworkAccessManager::finished, this, [](QNetworkReply* rep) {
// qDebug() << rep->readAll();
// });
connect(manager, &QNetworkAccessManager::finished, this, &UIModel::onAvailFilesResponse);
}
QVariantMap UIModel::getDefaultConf() const
{
auto path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
path /= "Roaming";
path /= "GlosSI";
path /= "default.json";
if (std::filesystem::exists(path)) {
QFile file(QString::fromStdWString(path));
if (file.open(QIODevice::ReadOnly)) {
const auto data = file.readAll();
file.close();
return QJsonDocument::fromJson(data).object().toVariantMap();
}
}
QJsonObject obj = {
{"icon", QJsonValue::Null},
{"name", QJsonValue::Null},
{"version", 1},
{"extendedLogging", false},
{"snapshotNotify", false},
{
"controller",
QJsonObject{
{"maxControllers", 1},
{"emulateDS4", false},
{"allowDesktopConfig", false}
}
},
{
"devices",
QJsonObject{
{"hideDevices", true},
{"realDeviceIds", false},
}
},
{
"launch",
QJsonObject{
{"closeOnExit", true},
{"launch", false},
{"launchAppArgs", QJsonValue::Null},
{"launchPath", QJsonValue::Null},
{"waitForChildProcs", true},
}
},
{
"window",
QJsonObject{
{"disableOverlay", false},
{"maxFps", QJsonValue::Null},
{"scale", QJsonValue::Null},
{"windowMode", false},
}
},
};
saveDefaultConf(obj.toVariantMap());
return getDefaultConf();
}
void UIModel::saveDefaultConf(QVariantMap conf) const
{
return UWPFetch::UWPAppList();
auto path = std::filesystem::temp_directory_path().parent_path().parent_path().parent_path();
path /= "Roaming";
path /= "GlosSI";
path /= "default.json";
QFile file(path);
if (!file.open(QIODevice::Text | QIODevice::ReadWrite)) {
qDebug() << "Couldn't open file for writing: " << path;
return;
}
file.write(QString(QJsonDocument::fromVariant(conf).toJson(QJsonDocument::Indented)).toStdString().data());
file.close();
}
#ifdef _WIN32
QVariantList UIModel::uwpApps() { return UWPFetch::UWPAppList(); }
#endif
bool UIModel::writeShortcutsVDF(const std::wstring& mode, const std::wstring& name, const std::wstring& shortcutspath, bool is_admin_try) const
bool UIModel::writeShortcutsVDF(const std::wstring& mode, const std::wstring& name, const std::wstring& shortcutspath,
bool is_admin_try) const
{
#ifdef _WIN32
const std::filesystem::path config_path = is_admin_try
? shortcutspath
: std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + shortcutsfile_.toStdWString();
: std::wstring(getSteamPath()) + user_data_path_.toStdWString() +
getSteamUserId() + shortcutsfile_.toStdWString();
qDebug() << "Steam config Path: " << config_path;
qDebug() << "Trying to write config as admin: " << is_admin_try;
@ -327,15 +452,9 @@ bool UIModel::writeShortcutsVDF(const std::wstring& mode, const std::wstring& na
#endif
}
bool UIModel::getIsWindows() const
{
return is_windows_;
}
bool UIModel::getIsWindows() const { return is_windows_; }
bool UIModel::hasAcrylicEffect() const
{
return has_acrylic_affect_;
}
bool UIModel::hasAcrylicEffect() const { return has_acrylic_affect_; }
void UIModel::setAcrylicEffect(bool has_acrylic_affect)
{
@ -343,6 +462,63 @@ void UIModel::setAcrylicEffect(bool has_acrylic_affect)
emit acrylicChanged();
}
void UIModel::onAvailFilesResponse(QNetworkReply* reply)
{
const QVariant status_code = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
qDebug() << "http status: " << status_code;
if (status_code.isValid() && status_code.toInt() == 200) {
const auto respStr = reply->readAll();
qDebug() << "AvailFiles response: " << respStr;
QJsonObject json = QJsonDocument::fromJson(respStr).object();
const auto defaultConf = getDefaultConf();
bool snapshotNotify =
defaultConf.contains("snapshotNotify")
? defaultConf["snapshotNotify"].toJsonValue().toBool()
: false;
struct VersionInfo {
int major;
int minor;
int patch;
int revision;
int commits_since_last;
};
std::vector<std::pair<QString, VersionInfo>> new_versions;
for (const auto& info :
json.keys() | std::ranges::views::filter([this, &json, snapshotNotify](const auto& key) {
return notify_on_snapshots_ ? true
: json[key].toObject().value("type") == (snapshotNotify ? "snapshot" : "release");
}) | std::ranges::views::transform([&json](const auto& key) -> std::pair<QString, VersionInfo> {
const auto versionString = json[key].toObject().value("version").toString();
const auto cleanVersion = versionString.split("-")[0];
const auto versionSplits = cleanVersion.split(".");
return {key,
{versionSplits[0].toInt(), versionSplits[1].toInt(), versionSplits[2].toInt(),
versionSplits[3].toInt(),
versionString.count('-') == 2 ? versionString.split("-")[1].toInt() : 0}};
}) | std::views::filter([](const auto& info) {
return info.second.major > version::VERSION_MAJOR || info.second.minor > version::VERSION_MINOR ||
info.second.patch > version::VERSION_PATCH ||
info.second.revision > version::VERSION_REVISION ||
info.second.commits_since_last > (QString(version::VERSION_STR).count('-') == 2
? QString(version::VERSION_STR).split("-")[1].toInt()
: 0);
}) | std::ranges::views::all) {
new_versions.push_back(info);
}
std::ranges::sort(new_versions, [](const auto& a, const auto& b) { return a.first > b.first; });
if (!new_versions.empty()) {
qDebug() << "New version available: " << new_versions[0].first;
new_version_name_ = new_versions[0].first;
emit newVersionAvailable();
}
}
}
void UIModel::writeTarget(const QJsonObject& json, const QString& name) const
{
auto path = config_path_;
@ -354,14 +530,14 @@ void UIModel::writeTarget(const QJsonObject& json, const QString& name) const
return;
}
file.write(
QString(QJsonDocument(json).toJson(QJsonDocument::Indented))
.toStdString()
.data()
);
file.write(QString(QJsonDocument(json).toJson(QJsonDocument::Indented)).toStdString().data());
file.close();
}
QString UIModel::getVersionString() const { return QString(version::VERSION_STR); }
QString UIModel::getNewVersionName() const { return new_version_name_; }
std::filesystem::path UIModel::getSteamPath() const
{
try {
@ -379,7 +555,7 @@ std::filesystem::path UIModel::getSteamPath() const
return "";
}
#else
return L""; // TODO LINUX
return L""; // TODO LINUX
#endif
}
@ -398,11 +574,12 @@ std::wstring UIModel::getSteamUserId() const
qDebug() << "Steam not open?";
}
return res;
} catch(...) {
}
catch (...) {
return L"0";
}
#else
return L""; // TODO LINUX
return L""; // TODO LINUX
#endif
}
@ -411,7 +588,8 @@ bool UIModel::foundSteam() const
if (getSteamPath() == "" || getSteamUserId() == L"0") {
return false;
}
const std::filesystem::path user_config_dir = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId();
const std::filesystem::path user_config_dir =
std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId();
if (!std::filesystem::exists(user_config_dir)) {
return false;
}
@ -420,7 +598,8 @@ bool UIModel::foundSteam() const
void UIModel::parseShortcutVDF()
{
const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + shortcutsfile_.toStdWString();
const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() +
getSteamUserId() + shortcutsfile_.toStdWString();
if (!std::filesystem::exists(config_path)) {
qDebug() << "Shortcuts file does not exist.";
return;
@ -438,7 +617,8 @@ bool UIModel::isSteamInputXboxSupportEnabled() const
{
// return true as default to not bug the user in error cases.
if (foundSteam()) {
const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + user_config_file_.toStdWString();
const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() +
getSteamUserId() + user_config_file_.toStdWString();
if (!std::filesystem::exists(config_path)) {
qDebug() << "localconfig.vdf does not exist.";
return true;

@ -14,11 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <shortcuts_vdf.hpp>
#include <QJsonObject>
#include <QObject>
#include <QVariant>
#include <filesystem>
#include <shortcuts_vdf.hpp>
class QNetworkReply;
class UIModel : public QObject {
Q_OBJECT
@ -30,6 +32,9 @@ class UIModel : public QObject {
Q_PROPERTY(bool foundSteam READ foundSteam CONSTANT)
Q_PROPERTY(bool steamInputXboxSupportEnabled READ isSteamInputXboxSupportEnabled CONSTANT)
Q_PROPERTY(QString versionString READ getVersionString CONSTANT)
Q_PROPERTY(QString newVersionName READ getNewVersionName NOTIFY newVersionAvailable)
public:
UIModel();
@ -44,16 +49,20 @@ class UIModel : public QObject {
Q_INVOKABLE bool removeFromSteam(const QString& name, const QString& shortcutspath, bool from_cmd = false);
Q_INVOKABLE QVariantMap manualProps(QVariant shortcut);
Q_INVOKABLE void enableSteamInputXboxSupport();
Q_INVOKABLE bool restartSteam();
Q_INVOKABLE void updateCheck();
Q_INVOKABLE QVariantMap getDefaultConf() const;
Q_INVOKABLE void saveDefaultConf(QVariantMap conf) const;
#ifdef _WIN32
Q_INVOKABLE QVariantList uwpApps();
#endif
[[nodiscard]] bool writeShortcutsVDF(
const std::wstring& mode,
const std::wstring& name,
const std::wstring& shortcutspath,
bool is_admin_try = false
) const;
[[nodiscard]] bool writeShortcutsVDF(const std::wstring& mode, const std::wstring& name,
const std::wstring& shortcutspath, bool is_admin_try = false) const;
bool getIsWindows() const;
[[nodiscard]] bool hasAcrylicEffect() const;
@ -62,6 +71,10 @@ class UIModel : public QObject {
signals:
void acrylicChanged();
void targetListChanged();
void newVersionAvailable();
public slots:
void onAvailFilesResponse(QNetworkReply* reply);
private:
#ifdef _WIN32
@ -77,13 +90,20 @@ class UIModel : public QObject {
QString shortcutsfile_ = "/config/shortcuts.vdf";
QString user_config_file_ = "/config/localconfig.vdf";
QString user_data_path_ = "/userdata/";
QString steam_executable_name_ = "steam.exe";
QVariantList targets_;
QString new_version_name_;
bool notify_on_snapshots_ = false;
std::vector<VDFParser::Shortcut> shortcuts_vdf_;
void writeTarget(const QJsonObject& json, const QString& name) const;
QString getVersionString() const;
QString getNewVersionName() const;
std::filesystem::path getSteamPath() const;
std::wstring getSteamUserId() const;
bool foundSteam() const;

@ -154,7 +154,7 @@ int main(int argc, char* argv[])
}
}
QGuiApplication::setWindowIcon(QIcon(":/GloSC_Icon_small.png"));
QGuiApplication::setWindowIcon(QIcon(":/GlosSI_Logo_512.png"));
#ifdef _WIN32

@ -13,11 +13,14 @@
<file>qml/InfoDialog.qml</file>
<file>steamscreener.png</file>
<file>noise.png</file>
<file>GloSC_Icon_small.png</file>
<file>svg/help_outline_white_24dp.svg</file>
<file>qml/SteamNotFoundDialog.qml</file>
<file>qml/SteamInputXboxDisabledDialog.qml</file>
<file>qml/CollapsiblePane.qml</file>
<file>svg/expand_more_white_24dp.svg</file>
<file>GlosSI_Logo_512.png</file>
<file>qml/AdvancedTargetSettings.qml</file>
<file>qml/GlobalConf.qml</file>
<file>svg/settings_fill_white_24dp.svg</file>
</qresource>
</RCC>

@ -0,0 +1,475 @@
/*
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import QtQuick 6.2
import QtQuick.Controls 6.2
import QtQuick.Layouts 6.2
import QtQuick.Controls.Material 6.2
import QtQuick.Dialogs 6.2
CollapsiblePane {
radius: 4
Material.elevation: 32
bgOpacity: 0.97
title: qsTr("Advanced ⚙️")
property string subTitle: ""
property var shortcutInfo: ({})
content:
Column {
spacing: 16
Label {
id: subTitleLabel
width: parent.width
font.pixelSize: 16
text: subTitle
height: text == "" ? 0 : 24
}
RPane {
width: parent.width
radius: 4
Material.elevation: 32
bgOpacity: 0.97
height: advancedLaunchCol.height + 24
Column {
id: advancedLaunchCol
spacing: 4
height: advancedLaunchedRow.height
Row {
id: advancedLaunchedRow
spacing: 32
width: parent.width
height: closeOnExitCol.height
Column {
id: closeOnExitCol
spacing: 2
CheckBox {
id: closeOnExit
text: qsTr("Close when launched app quits")
checked: shortcutInfo.launch.closeOnExit
onCheckedChanged: function() {
shortcutInfo.launch.closeOnExit = checked
if (checked) {
waitForChildren.enabled = true;
} else {
waitForChildren.enabled = false;
}
}
}
Label {
text: qsTr("Recommended to disable for launcher-games")
wrapMode: Text.WordWrap
width: parent.width
leftPadding: 32
topPadding: -8
}
CheckBox {
id: waitForChildren
text: qsTr("Wait for child processes")
checked: shortcutInfo.launch.waitForChildProcs
onCheckedChanged: function(){
shortcutInfo.launch.waitForChildProcs = checked
}
}
}
Column {
spacing: 2
CheckBox {
id: allowDesktopConfig
text: qsTr("Allow desktop-config")
checked: shortcutInfo.controller.allowDesktopConfig
onCheckedChanged: function(){
shortcutInfo.controller.allowDesktopConfig = checked
}
}
Label {
text: qsTr("Allow desktop-config if launched application is not focused")
leftPadding: 32
topPadding: -8
}
}
}
}
}
Row {
spacing: 16
width: parent.width
RPane {
width: parent.width / 2 - 8
height: 248
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
spacing: 0
width: parent.width
Row {
CheckBox {
id: hideDevices
text: qsTr("Hide (Real) Controllers")
checked: shortcutInfo.devices.hideDevices
onCheckedChanged: shortcutInfo.devices.hideDevices = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Hide (Real) Controllers")
helpInfoDialog.text =
qsTr("Hides real game controllers from the system\nThis may prevent doubled inputs")
+ "\n"
+ qsTr("You can change this setting and which devices are hidden in the GlosSI overlay")
+ "\n"
+ qsTr("If ONLY using a SteamController or SteamDeck this settings is not required, but won't hurt")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
CheckBox {
id: realDeviceIds
text: qsTr("Use real device (USB)-IDs")
checked: shortcutInfo.devices.realDeviceIds
onCheckedChanged: shortcutInfo.devices.realDeviceIds = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Use real device (USB)-IDs")
helpInfoDialog.text =
qsTr("Only enable if input's are not recognized by the game")
+ "\n"
+ qsTr("If enabled, device-hiding won't work.\nUse the \"Max. Controller count\" setting!")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
CheckBox {
id: emulateDS4
text: qsTr("Emulate DS4")
checked: shortcutInfo.controller.emulateDS4 || false
onCheckedChanged: shortcutInfo.controller.emulateDS4 = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Emulate DS4")
helpInfoDialog.text =
qsTr("Emulates a DS4 instead of X360 Pad")
+ "\n"
qsTr("for usage with, for example, PSNow")
+ "\n"
+ qsTr("If enabled you have to disable \"Playstation Configuration support\" in Steam")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
leftPadding: 16
Label {
text: qsTr("Max. emulated controllers")
topPadding: 16
}
SpinBox {
id: maxControllersSpinBox
width: 128
editable: true
value: shortcutInfo.controller.maxControllers
from: 0
to: 4
onValueChanged: shortcutInfo.controller.maxControllers = value
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Max. emulated controllers")
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\" ")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
}
}
RPane {
width: parent.width / 2 - 8
height: 248
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
spacing: 0
width: parent.width
Row {
CheckBox {
id: windowMode
text: qsTr("Steam/GlosSI overlay as separate window")
checked: shortcutInfo.window.windowMode
onCheckedChanged: shortcutInfo.window.windowMode = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Steam/GlosSI overlay as separate window")
helpInfoDialog.text =
qsTr("Doesn't show overlay on top, but as separate window")
+ "\n"
+ qsTr("Use if blackscreen-issues are encountered.")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
CheckBox {
id: disableOverlayCheckbox
text: qsTr("Disable Steam/GlosSI overlay")
checked: shortcutInfo.window.disableOverlay
onCheckedChanged: shortcutInfo.window.disableOverlay = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Disable Steam/GlosSI overlay")
helpInfoDialog.text =
qsTr("Only controller emulation - No extra window")
+ "\n"
+ qsTr("Might help with Steam remote play.")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
leftPadding: 16
Label {
text: qsTr("GlosSI-Overlay scale")
topPadding: 16
}
SpinBox {
id: scaleSpinBox
width: 172
from: -100
value: shortcutInfo.window.scale * 100 || 0
to: 350
stepSize: 10
editable: true
property int decimals: 2
property real realValue: value / 100
validator: DoubleValidator {
bottom: Math.min(scaleSpinBox.from, scaleSpinBox.to)
top: Math.max(scaleSpinBox.from, scaleSpinBox.to)
}
textFromValue: function(value, locale) {
return Number(value / 100).toLocaleString(locale, 'f', scaleSpinBox.decimals)
}
valueFromText: function(text, locale) {
return Number.fromLocaleString(locale, text) * 100
}
onValueChanged: function() {
if (value <= 0) {
shortcutInfo.window.scale = null
return
}
shortcutInfo.window.scale = value / 100
}
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("GloSI-Overlay scaling")
helpInfoDialog.text =
qsTr("Scales the elements of the GlosSI-Overlay (not Steam Overlay)")
+ "\n"
+ qsTr(" <= 0.0 to use auto-detection")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
leftPadding: 16
Label {
text: qsTr("Max. Overlay FPS")
topPadding: 16
}
SpinBox {
id: maxFPSSpinBox
width: 172
from: -1
value: shortcutInfo.window.maxFps || 0
to: 244
stepSize: 5
editable: true
onValueChanged: function() {
if (value <= 0) {
shortcutInfo.window.maxFps = null
return
}
shortcutInfo.window.maxFps = value
}
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Max. Overlay FPS")
helpInfoDialog.text =
qsTr("Restricts the FPS of the overlay to the given value")
+ "\n"
+ qsTr(" <= 0.0 to use screen refresh rate")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
}
}
}
RPane {
width: parent.width
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
spacing: 4
Row {
Row {
CheckBox {
id: extendedLogging
text: qsTr("Extended Logging")
checked: shortcutInfo.extendedLogging
onCheckedChanged: shortcutInfo.extendedLogging = checked
}
}
}
}
}
}
}

@ -0,0 +1,130 @@
/*
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
import QtQuick 6.2
import QtQuick.Controls 6.2
import QtQuick.Layouts 6.2
import QtQuick.Controls.Material 6.2
import QtQuick.Dialogs 6.2
Item {
id: globalConfContent
anchors.fill: parent
signal cancel()
signal done()
property var config: ({})
Component.onCompleted: function() {
config = uiModel.getDefaultConf()
}
Flickable {
id: flickable
anchors.margins: 0
anchors.top: parent.top
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
clip: true
ScrollBar.vertical: ScrollBar {
}
contentWidth: parent.width
contentHeight: confColumn.height
flickableDirection: Flickable.VerticalFlick
Column {
id: confColumn
anchors.left: parent.left
anchors.right: parent.right
anchors.leftMargin: 32
anchors.rightMargin: 32
spacing: 4
Item {
width: 1
height: 32
}
RPane {
width: parent.width
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
width: parent.width
height: parent.height
spacing: 4
Row {
spacing: 32
width: parent.width
CheckBox {
id: launchApp
text: qsTr("Notify me about Snapshots")
checked: config ? config.snapshotNotify : false
onCheckedChanged: function() {
config.snapshotNotify = checked
}
}
}
}
}
Item {
width: 1
height: 4
}
AdvancedTargetSettings {
id: advancedTargetSettings
shortcutInfo: config
title: qsTr("Advanced default target settings ⚙️")
subTitle: qsTr(
"Default settings when creating new shortcuts\n"
+ "as well as settings applied when launching GlosSITarget without config")
}
Item {
width: 1
height: 32
}
}
}
Row {
spacing: 8
anchors.bottom: parent.bottom
anchors.right: parent.right
anchors.margins: 24
anchors.bottomMargin: 16
Button {
text: qsTr("Cancel")
onClicked: function() {
cancel()
}
}
Button {
text: qsTr("💾 Save")
highlighted: true
onClicked: function() {
uiModel.saveDefaultConf(config)
done()
}
}
}
}

@ -22,6 +22,7 @@ Dialog {
property var confirmedParam: null
signal confirmed(var param)
signal confirmedExtra(var param)
property alias buttonText: okbutton.text
visible: false
modal: true

@ -32,34 +32,7 @@ Item {
property var shortcutInfo: ({})
function resetInfo() {
shortcutInfo = ({
"controller": {
"maxControllers": 1,
"emulateDS4": false,
"allowDesktopConfig": false
},
"devices": {
"hideDevices": true,
"realDeviceIds": false
},
"icon": null,
"launch": {
"closeOnExit": true,
"launch": false,
"launchAppArgs": null,
"launchPath": null,
"waitForChildProcs": true
},
"name": null,
"version": 1,
"window": {
"disableOverlay": false,
"maxFps": null,
"scale": null,
"windowMode": false
},
"extendedLogging": false
})
shortcutInfo = uiModel.getDefaultConf()
}
Component.onCompleted: function() {
@ -67,24 +40,18 @@ Item {
}
onShortcutInfoChanged: function() {
nameInput.text = shortcutInfo.name || ""
if (extendedLogging) {
extendedLogging.checked = shortcutInfo.extendedLogging || false
if (!shortcutInfo) {
return;
}
if (nameInput) { // basic info (not in collapsible container)
nameInput.text = shortcutInfo.name || ""
launchApp.checked = shortcutInfo.launch.launch
pathInput.text = shortcutInfo.launch.launchPath || ""
argsInput.text = shortcutInfo.launch.launchAppArgs || ""
}
if (advancedTargetSettings) { // advanced settings (collapsible container)
advancedTargetSettings.shortcutInfo = shortcutInfo;
}
launchApp.checked = shortcutInfo.launch.launch
pathInput.text = shortcutInfo.launch.launchPath || ""
argsInput.text = shortcutInfo.launch.launchAppArgs || ""
closeOnExit.checked = shortcutInfo.launch.closeOnExit
waitForChildren.checked = shortcutInfo.launch.waitForChildProcs
hideDevices.checked = shortcutInfo.devices.hideDevices
realDeviceIds.checked = shortcutInfo.devices.realDeviceIds
windowMode.checked = shortcutInfo.window.windowMode
disableOverlayCheckbox.checked = shortcutInfo.window.disableOverlay
scaleSpinBox.value = shortcutInfo.window.scale
maxFPSSpinBox.value = shortcutInfo.window.maxFps
maxControllersSpinBox.value = shortcutInfo.controller.maxControllers
allowDesktopConfig.checked = shortcutInfo.controller.allowDesktopConfig
emulateDS4.checked = shortcutInfo.controller.emulateDS4
}
Flickable {
@ -157,7 +124,7 @@ Item {
CheckBox {
id: launchApp
text: qsTr("Launch app")
checked: shortcutInfo.launch.launch
checked: shortcutInfo ? shortcutInfo.launch.launch : false
onCheckedChanged: function() {
shortcutInfo.launch.launch = checked
if (checked) {
@ -271,464 +238,9 @@ Item {
height: 8
}
CollapsiblePane {
radius: 4
Material.elevation: 32
bgOpacity: 0.97
title: qsTr("Advanced")
content:
Column {
spacing: 16
RPane {
width: parent.width
radius: 4
Material.elevation: 32
bgOpacity: 0.97
height: advancedLaunchCol.height + 24
Column {
id: advancedLaunchCol
spacing: 4
height: advancedLaunchedRow.height
Row {
id: advancedLaunchedRow
spacing: 32
width: parent.width
height: closeOnExitCol.height
Column {
id: closeOnExitCol
spacing: 2
CheckBox {
id: closeOnExit
text: qsTr("Close when launched app quits")
checked: shortcutInfo.launch.closeOnExit
onCheckedChanged: function() {
shortcutInfo.launch.closeOnExit = checked
if (checked) {
waitForChildren.enabled = true;
} else {
waitForChildren.enabled = false;
}
}
}
Label {
text: qsTr("Recommended to disable for launcher-games")
wrapMode: Text.WordWrap
width: parent.width
leftPadding: 32
topPadding: -8
}
CheckBox {
id: waitForChildren
text: qsTr("Wait for child processes")
checked: shortcutInfo.launch.waitForChildProcs
onCheckedChanged: function(){
shortcutInfo.launch.waitForChildProcs = checked
}
}
}
Column {
spacing: 2
CheckBox {
id: allowDesktopConfig
text: qsTr("Allow desktop-config")
checked: shortcutInfo.controller.allowDesktopConfig
onCheckedChanged: function(){
shortcutInfo.controller.allowDesktopConfig = checked
}
}
Label {
text: qsTr("Allow desktop-config if launched application is not focused")
leftPadding: 32
topPadding: -8
}
}
}
}
}
Row {
spacing: 16
width: parent.width
RPane {
width: parent.width / 2 - 8
height: 248
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
spacing: 0
width: parent.width
Row {
CheckBox {
id: hideDevices
text: qsTr("Hide (Real) Controllers")
checked: shortcutInfo.devices.hideDevices
onCheckedChanged: shortcutInfo.devices.hideDevices = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Hide (Real) Controllers")
helpInfoDialog.text =
qsTr("Hides real game controllers from the system\nThis may prevent doubled inputs")
+ "\n"
+ qsTr("You can change this setting and which devices are hidden in the GlosSI overlay")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
CheckBox {
id: realDeviceIds
text: qsTr("Use real device (USB)-IDs")
checked: shortcutInfo.devices.realDeviceIds
onCheckedChanged: shortcutInfo.devices.realDeviceIds = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Use real device (USB)-IDs")
helpInfoDialog.text =
qsTr("Only enable if input's are not recognized by the game")
+ "\n"
+ qsTr("If enabled, device-hiding won't work.\nUse the \"Max. Controller count\" setting!")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
CheckBox {
id: emulateDS4
text: qsTr("Emulate DS4")
checked: shortcutInfo.controller.emulateDS4 || false
onCheckedChanged: shortcutInfo.controller.emulateDS4 = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Emulate DS4")
helpInfoDialog.text =
qsTr("Emulates a DS4 instead of X360 Pad")
+ "\n"
qsTr("for usage with, for example, PSNow")
+ "\n"
+ qsTr("If enabled you have to disable \"Playstation Configuration support\" in Steam")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
leftPadding: 16
Label {
text: qsTr("Max. emulated controllers")
topPadding: 16
}
SpinBox {
id: maxControllersSpinBox
width: 128
editable: true
value: shortcutInfo.controller.maxControllers
from: 0
to: 4
onValueChanged: shortcutInfo.controller.maxControllers = value
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Max. emulated controllers")
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\" ")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
}
}
RPane {
width: parent.width / 2 - 8
height: 248
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
spacing: 0
width: parent.width
Row {
CheckBox {
id: windowMode
text: qsTr("Steam/GlosSI overlay as separate window")
checked: shortcutInfo.window.windowMode
onCheckedChanged: shortcutInfo.window.windowMode = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Steam/GlosSI overlay as separate window")
helpInfoDialog.text =
qsTr("Doesn't show overlay on top, but as separate window")
+ "\n"
+ qsTr("Use if blackscreen-issues are encountered.")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
CheckBox {
id: disableOverlayCheckbox
text: qsTr("Disable Steam/GlosSI overlay")
checked: shortcutInfo.window.disableOverlay
onCheckedChanged: shortcutInfo.window.disableOverlay = checked
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Disable Steam/GlosSI overlay")
helpInfoDialog.text =
qsTr("Only controller emulation - No extra window")
+ "\n"
+ qsTr("Might help with Steam remote play.")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
leftPadding: 16
Label {
text: qsTr("GlosSI-Overlay scale")
topPadding: 16
}
SpinBox {
id: scaleSpinBox
width: 172
from: -100
value: shortcutInfo.window.scale * 100 || 0
to: 350
stepSize: 10
editable: true
property int decimals: 2
property real realValue: value / 100
validator: DoubleValidator {
bottom: Math.min(scaleSpinBox.from, scaleSpinBox.to)
top: Math.max(scaleSpinBox.from, scaleSpinBox.to)
}
textFromValue: function(value, locale) {
return Number(value / 100).toLocaleString(locale, 'f', scaleSpinBox.decimals)
}
valueFromText: function(text, locale) {
return Number.fromLocaleString(locale, text) * 100
}
onValueChanged: function() {
if (value <= 0) {
shortcutInfo.window.scale = null
return
}
shortcutInfo.window.scale = value / 100
}
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("GloSI-Overlay scaling")
helpInfoDialog.text =
qsTr("Scales the elements of the GlosSI-Overlay (not Steam Overlay)")
+ "\n"
+ qsTr(" <= 0.0 to use auto-detection")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
Item {
width: 1
height: 4
}
Row {
leftPadding: 16
Label {
text: qsTr("Max. Overlay FPS")
topPadding: 16
}
SpinBox {
id: maxFPSSpinBox
width: 172
from: -1
value: shortcutInfo.window.maxFps || 0
to: 244
stepSize: 5
editable: true
onValueChanged: function() {
if (value <= 0) {
shortcutInfo.window.maxFps = null
return
}
shortcutInfo.window.maxFps = value
}
}
RoundButton {
onClicked: () => {
helpInfoDialog.titleText = qsTr("Max. Overlay FPS")
helpInfoDialog.text =
qsTr("Restricts the FPS of the overlay to the given value")
+ "\n"
+ qsTr(" <= 0.0 to use screen refresh rate")
helpInfoDialog.open()
}
width: 48
height: 48
Material.elevation: 0
anchors.topMargin: 16
Image {
anchors.centerIn: parent
source: "qrc:/svg/help_outline_white_24dp.svg"
width: 24
height: 24
}
}
}
}
}
}
RPane {
width: parent.width
radius: 4
Material.elevation: 32
bgOpacity: 0.97
Column {
spacing: 4
Row {
Row {
CheckBox {
id: extendedLogging
text: qsTr("Extended Logging")
checked: shortcutInfo.extendedLogging
onCheckedChanged: shortcutInfo.extendedLogging = checked
}
// RoundButton {
// onClicked: () => {
// helpInfoDialog.titleText = qsTr("Hide (Real) Controllers")
// helpInfoDialog.text =
// qsTr("Hides real game controllers from the system\nThis may prevent doubled inputs")
// + "\n"
// + qsTr("You can change this setting and which devices are hidden in the GlosSI overlay")
// helpInfoDialog.open()
// }
// width: 48
// height: 48
// Material.elevation: 0
// anchors.topMargin: 16
// Image {
// anchors.centerIn: parent
// source: "qrc:/svg/help_outline_white_24dp.svg"
// width: 24
// height: 24
// }
// }
}
}
}
}
}
AdvancedTargetSettings {
id: advancedTargetSettings
shortcutInfo: shortcutInfo
}
Item {
@ -748,11 +260,12 @@ Item {
Button {
text: qsTr("Cancel")
onClicked: function() {
resetInfo();
cancel()
}
}
Button {
text: qsTr("Save")
text: qsTr("💾 Save")
highlighted: true
enabled: nameInput.acceptableInput
onClicked: function() {

@ -76,12 +76,53 @@ Window {
InfoDialog {
id: steamChangedDialog
titleText: qsTr("Steam shortcuts changed!")
text: qsTr("Please restart Steam to reload your changes")
text: qsTr("You have to restart Steam before your changes become visible")
onConfirmed: function (callback) {
callback();
}
}
InfoDialog {
id: steamChangedOnCloseDialog
titleText: qsTr("Steam shortcuts changed!")
text: qsTr("Please restart Steam to reload your changes\nRestart Steam now?")
onConfirmed: function (closeWindow) {
if (uiModel.restartSteam()) {
closeWindow();
} else {
// meh I really should write a dialogUtil or some shit...
failedRestartingSteamDialog.confirmedParam = closeWindow;
failedRestartingSteamDialog.open();
}
}
buttonText: qsTr("Yes")
extraButton: true
extraButtonText: qsTr("No")
}
InfoDialog {
id: failedRestartingSteamDialog
titleText: qsTr("Failed restarting Steam!")
text: qsTr("You have to restart Steam before your changes become visible")
onConfirmed: function (callback) {
callback();
}
}
InfoDialog {
id: newVersionDialog
titleText: qsTr("New version available!")
text: uiModel.newVersionName + "\n\n" + qsTr("Would you like to visit the download page now?")
onConfirmed: function (callback) {
callback();
Qt.openUrlExternally(`https://glossi.flatspot.pictures/#downloads-${uiModel.newVersionName}`);
}
buttonText: qsTr("Yes")
extraButton: true
extraButtonText: qsTr("Remind me later")
visible: !!uiModel.newVersionName
}
Rectangle {
id: titleBar
visible: uiModel.isWindows
@ -124,10 +165,10 @@ Window {
text: "🗙"
onClicked: steamShortcutsChanged
? (function(){
steamChangedDialog.confirmedParam = () => {
steamChangedOnCloseDialog.confirmedParam = () => {
window.close()
}
steamChangedDialog.open()
steamChangedOnCloseDialog.open()
})()
: window.close()
background: Rectangle {
@ -193,24 +234,46 @@ Window {
windowContent.editedIndex = index;
}
}
RoundButton {
id: addBtn
Column {
spacing: 8
anchors.right: parent.right
anchors.bottom: parent.bottom
anchors.margins: 24
width: 64
height: 64
text: "+"
contentItem: Label {
anchors.centerIn: parent
text: addBtn.text
font.pixelSize: 32
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
RoundButton {
id: optionsBtn
width: 64
height: 64
text: ""
contentItem: Item {
Image {
anchors.centerIn: parent
source: "qrc:/svg/settings_fill_white_24dp.svg"
width: 24
height: 24
}
}
highlighted: true
onClicked: function() {
globalConf.opacity = 1;
homeContent.opacity = 0;
}
}
RoundButton {
id: addBtn
width: 64
height: 64
text: "+"
contentItem: Label {
anchors.centerIn: parent
text: addBtn.text
font.pixelSize: 32
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
elide: Text.ElideRight
}
highlighted: true
onClicked: selectTypeDialog.open()
}
highlighted: true
onClicked: selectTypeDialog.open()
}
}
@ -258,6 +321,55 @@ Window {
}
}
Item {
id: globalConf
height: parent.height
width: parent.width
opacity: 0
property real animMarg: opacity == 0 ? parent.height : 0
y: animMarg
visible: opacity === 0 ? false : true
Behavior on opacity {
ParallelAnimation {
NumberAnimation {
duration: 300
property: "opacity"
easing.type: opacity === 0 ? Easing.OutQuad : Easing.InOutQuad
}
PropertyAnimation {
duration: 300
target: globalConf
property: "animMarg";
from: globalConf.animMarg
to: globalConf.animMarg > 0 ? 0 : globalConf.parent.height;
easing.type: opacity === 0 ? Easing.OutQuad : Easing.InOutQuad
}
}
}
GlobalConf {
id: glConf
anchors.fill: parent
onCancel: function() {
globalConf.opacity = 0;
homeContent.opacity = 1;
}
onDone: function() {
globalConf.opacity = 0;
homeContent.opacity = 1;
}
}
}
Label {
id: versionInfo
anchors.bottom: parent.bottom
anchors.left: parent.left
anchors.margins: 8
opacity: 0.5
text: uiModel.versionString
}
AddSelectTypeDialog {
id: selectTypeDialog
visible: false

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" height="48" width="48" fill="#FFFFFF"><path d="m19.4 44-1-6.3q-.95-.35-2-.95t-1.85-1.25l-5.9 2.7L4 30l5.4-3.95q-.1-.45-.125-1.025Q9.25 24.45 9.25 24q0-.45.025-1.025T9.4 21.95L4 18l4.65-8.2 5.9 2.7q.8-.65 1.85-1.25t2-.9l1-6.35h9.2l1 6.3q.95.35 2.025.925Q32.7 11.8 33.45 12.5l5.9-2.7L44 18l-5.4 3.85q.1.5.125 1.075.025.575.025 1.075t-.025 1.05q-.025.55-.125 1.05L44 30l-4.65 8.2-5.9-2.7q-.8.65-1.825 1.275-1.025.625-2.025.925l-1 6.3ZM24 30.5q2.7 0 4.6-1.9 1.9-1.9 1.9-4.6 0-2.7-1.9-4.6-1.9-1.9-4.6-1.9-2.7 0-4.6 1.9-1.9 1.9-1.9 4.6 0 2.7 1.9 4.6 1.9 1.9 4.6 1.9Z"/></svg>

After

Width:  |  Height:  |  Size: 609 B

@ -30,12 +30,18 @@ limitations under the License.
#include <regex>
#include "UnhookUtil.h"
AppLauncher::AppLauncher(
std::vector<HWND>& process_hwnds,
std::function<void()> shutdown) : process_hwnds_(process_hwnds), shutdown_(std::move(shutdown))
std::function<void()> shutdown) : shutdown_(std::move(shutdown)), process_hwnds_(process_hwnds)
{
#ifdef _WIN32
spdlog::debug("Unpatching Valve CreateProcess Hooks");
UnPatchValveHooks();
if (Settings::launch.launch) {
spdlog::debug("App launch requested");
}
#endif
};
@ -106,6 +112,29 @@ void AppLauncher::close()
#endif
}
void AppLauncher::launchWatchdog()
{
// wchar_t buff[MAX_PATH];
// GetModuleFileName(GetModuleHandle(NULL), buff, MAX_PATH);
// const std::wstring glossipath(buff);
// GetWindowsDirectory(buff, MAX_PATH);
// const std::wstring winpath(buff);
// launchWin32App(
// (glossipath.substr(0, 1 + glossipath.find_last_of(L'\\')) + L"GlosSIWatchdog.exe"),
// L"",
// true);
spdlog::debug("Launching GlosSIWatchdog");
char buff[MAX_PATH];
GetModuleFileNameA(GetModuleHandle(NULL), buff, MAX_PATH);
const std::string glossipath(buff);
// hack to start a TRULY detached process...
system(("start " + (glossipath.substr(0, 1 + glossipath.find_last_of(L'\\')) + "GlosSIWatchdog.exe")).data());
}
#ifdef _WIN32
bool AppLauncher::IsProcessRunning(DWORD pid)
{
@ -125,7 +154,12 @@ void AppLauncher::getChildPids(DWORD parent_pid)
if (Process32First(hp, &pe)) {
do {
if (pe.th32ParentProcessID == parent_pid) {
pids_.push_back(pe.th32ProcessID);
if (std::ranges::find(pids_, pe.th32ProcessID) == pids_.end()) {
if (Settings::extendedLogging) {
spdlog::info("Found new child process with PID \"{}\"", pe.th32ProcessID);
}
pids_.push_back(pe.th32ProcessID);
}
}
} while (Process32Next(hp, &pe));
}
@ -167,66 +201,46 @@ void AppLauncher::getProcessHwnds()
#ifdef _WIN32
void AppLauncher::UnPatchValveHooks()
{
// TODO: move and re-use reusable unhook util from HidHide.cpp
spdlog::debug("Unpatching Valve CreateProcess hook...");
// need to load addresses that way.. Otherwise we may land before some jumps...
auto kernel32dll = GetModuleHandle(L"kernel32.dll");
if (kernel32dll) {
BYTE* address = reinterpret_cast<BYTE*>(GetProcAddress(kernel32dll, "CreateProcessW"));
if (address) {
DWORD dw_old_protect, dw_bkup;
const auto len = CREATE_PROC_ORIG_BYTES.size();
VirtualProtect(address, len, PAGE_EXECUTE_READWRITE, &dw_old_protect); //Change permissions of memory..
const auto opcode = *(address);
if (opcode != 0xE9 && opcode != 0xE8 && opcode != 0xEB && opcode != 0xEA && opcode != 0xFF) {
spdlog::debug("\"CreateProcessW\" Doesn't appear to be hooked, skipping!");
VirtualProtect(address, len, dw_old_protect, &dw_bkup); // Revert permission change...
} else {
for (DWORD i = 0; i < len; i++) // unpatch Valve's hook
{
*(address + i) = CREATE_PROC_ORIG_BYTES[i];
}
VirtualProtect(address, len, dw_old_protect, &dw_bkup); // Revert permission change...
spdlog::trace("Unpatched CreateProcessW");
}
}
else {
spdlog::error("failed to unpatch CreateProcessW");
}
UnhookUtil::UnPatchHook("CreateProcessW", kernel32dll);
}
else {
spdlog::error("kernel32.dll not found... sure...");
}
}
void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& args)
void AppLauncher::launchWin32App(const std::wstring& path, const std::wstring& args, bool watchdog)
{
const auto native_seps_path = std::regex_replace(path, std::wregex(L"(\\/|\\\\)"), L"\\");
//std::wstring launch_dir;
//std::wsmatch m;
//if (!std::regex_search(native_seps_path, m, std::wregex(L"(.*?\\\\)*"))) {
// spdlog::warn("Couldn't detect launch application directory"); // Shouldn't ever happen...
//} else {
// launch_dir = m[0];
//}
// std::wstring launch_dir;
// std::wsmatch m;
// if (!std::regex_search(native_seps_path, m, std::wregex(L"(.*?\\\\)*"))) {
// spdlog::warn("Couldn't detect launch application directory"); // Shouldn't ever happen...
// } else {
// launch_dir = m[0];
// }
std::wstring args_cpy(args);
spdlog::debug(L"Launching Win32App app \"{}\"; args \"{}\"", native_seps_path, args_cpy);
if (CreateProcessW(native_seps_path.data(),
args_cpy.data(),
nullptr,
nullptr,
TRUE,
0,
watchdog ? FALSE : TRUE,
watchdog ? DETACHED_PROCESS : 0,
nullptr,
nullptr, //launch_dir.empty() ? nullptr : launch_dir.data(),
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: \"{}\"", native_seps_path);
pids_.push_back(process_info.dwProcessId);
// 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) {
pids_.push_back(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: \"{}\" in directory: \"{}\"", native_seps_path, launch_dir);
spdlog::error(L"Couldn't start program: \"{}\"", native_seps_path);
}
}
@ -265,7 +279,7 @@ void AppLauncher::launchUWPApp(const LPCWSTR package_full_name, const std::wstri
pids_.clear();
}
else {
spdlog::info(L"Launched UWP Package \"{}\"", package_full_name);
spdlog::info(L"Launched UWP Package \"{}\"; PID: {}", package_full_name, pids_[0]);
}
}
else {
@ -308,6 +322,7 @@ void AppLauncher::launchURL(const std::wstring& url, const std::wstring& args, c
if (execute_info.hProcess != nullptr) {
if (const auto pid = GetProcessId(execute_info.hProcess); pid > 0) {
pids_.push_back(pid);
spdlog::trace("Launched URL; PID: {}", pid);
return;
}
}

@ -34,6 +34,8 @@ class AppLauncher {
void update();
void close();
void launchWatchdog();
private:
std::function<void()> shutdown_;
sf::Clock process_check_clock_;
@ -45,12 +47,9 @@ class AppLauncher {
std::vector<HWND>& process_hwnds_;
std::wstring launched_uwp_path_;
// Valve also hooks "CreateProcess"
// Unpatch that so that launched programs don't also get hooked...
static inline const std::string CREATE_PROC_ORIG_BYTES = "\x4C\x8B\xDC\x48\x83";
static void UnPatchValveHooks();
void launchWin32App(const std::wstring& path, const std::wstring& args = L"");
void launchWin32App(const std::wstring& path, const std::wstring& args = L"", bool watchdog = false);
void launchUWPApp(LPCWSTR package_full_name, const std::wstring& args = L"");
void launchURL(const std::wstring& url, const std::wstring& args = L"", const std::wstring& verb = L"open");
STARTUPINFO info{sizeof(info)};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

@ -194,6 +194,7 @@
<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" />
@ -201,6 +202,7 @@
<ClInclude Include="..\deps\subhook\subhook.h" />
<ClInclude Include="AppLauncher.h" />
<ClInclude Include="DllInjector.h" />
<ClInclude Include="GlosSI_logo.h" />
<ClInclude Include="HidHide.h" />
<ClInclude Include="imconfig.h" />
<ClInclude Include="InputRedirector.h" />
@ -214,6 +216,7 @@
<ClInclude Include="SteamTarget.h" />
<ClInclude Include="steam_sf_keymap.h" />
<ClInclude Include="TargetWindow.h" />
<ClInclude Include="UnhookUtil.h" />
<ClInclude Include="UWPOverlayEnabler.h" />
</ItemGroup>
<ItemGroup>
@ -242,16 +245,12 @@
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</DeploymentContent>
</None>
<None Include=".clang-format" />
<None Include="example_conf.json">
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</DeploymentContent>
<DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Release|x64'">true</DeploymentContent>
</None>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="Resource.rc" />
</ItemGroup>
<ItemGroup>
<Image Include="$(SolutionDir)\GloSC_Icon.ico" />
<Image Include="..\GlosSI_Icon.ico" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">

@ -114,6 +114,9 @@
<ClCompile Include="..\deps\traypp\tray\src\components\toggle.cpp">
<Filter>Source Files\tray</Filter>
</ClCompile>
<ClCompile Include="UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="SteamTarget.h">
@ -173,15 +176,18 @@
<ClInclude Include="ProcessPriority.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="GlosSI_logo.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UnhookUtil.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="..\deps\SFML\out\Debug\lib\Debug\sfml-system-d-2.dll" />
<None Include="..\deps\SFML\out\Debug\lib\Debug\sfml-window-d-2.dll" />
<None Include="..\deps\SFML\out\Debug\lib\Debug\sfml-graphics-d-2.dll" />
<None Include=".clang-format" />
<None Include="example_conf.json">
<Filter>Source Files</Filter>
</None>
<None Include="..\deps\SFML\out\Release\lib\RelWithDebInfo\sfml-graphics-2.dll" />
<None Include="..\deps\SFML\out\Release\lib\RelWithDebInfo\sfml-system-2.dll" />
<None Include="..\deps\SFML\out\Release\lib\RelWithDebInfo\sfml-window-2.dll" />
@ -192,7 +198,7 @@
</ResourceCompile>
</ItemGroup>
<ItemGroup>
<Image Include="$(SolutionDir)\GloSC_Icon.ico">
<Image Include="..\GlosSI_Icon.ico">
<Filter>Resource Files</Filter>
</Image>
</ItemGroup>

File diff suppressed because it is too large Load Diff

@ -29,13 +29,18 @@ limitations under the License.
#include <initguid.h>
//
#ifndef WATCHDOG
#include "Overlay.h"
#endif
#include "Settings.h"
#include <devguid.h>
#include <devpkey.h>
#include <regex>
#include "UnhookUtil.h"
// {D61CA365-5AF4-4486-998B-9DB4734C6CA3}add the XUSB class GUID as it is missing in the public interfaces
DEFINE_GUID(GUID_DEVCLASS_XUSBCLASS, 0xD61CA365, 0x5AF4, 0x4486, 0x99, 0x8B, 0x9D, 0xB4, 0x73, 0x4C, 0x6C, 0xA3);
// {EC87F1E3-C13B-4100-B5F7-8B84D54260CB} add the XUSB interface class GUID as it is missing in the public interfaces
@ -68,10 +73,17 @@ void HidHide::closeCtrlDevice()
void HidHide::hideDevices(const std::filesystem::path& steam_path)
{
steam_path_ = steam_path;
#ifndef WATCHDOG
enableOverlayElement();
#endif
if (!Settings::devices.hideDevices) {
spdlog::info("Hiding devices is disabled; Not un-patching valve hooks, not looking for HidHide");
return;
}
spdlog::debug("Setting up device hiding...");
UnPatchValveHooks();
openCtrlDevice();
@ -146,9 +158,9 @@ void HidHide::hideDevices(const std::filesystem::path& steam_path)
spdlog::trace(L"Blacklisted device: {}", dev);
});
}
enableOverlayElement();
}
closeCtrlDevice();
device_hiding_setup_ = true;
}
void HidHide::disableHidHide()
@ -166,119 +178,99 @@ void HidHide::UnPatchValveHooks()
spdlog::debug("Unpatching Valve HID hooks...");
// need to load addresses that way.. Otherwise we land before some jumps...
if (const auto setupapidll = GetModuleHandle(L"setupapi.dll")) {
UnPatchHook("SetupDiEnumDeviceInfo", setupapidll);
UnPatchHook("SetupDiGetClassDevsW", setupapidll);
UnhookUtil::UnPatchHook("SetupDiEnumDeviceInfo", setupapidll);
UnhookUtil::UnPatchHook("SetupDiGetClassDevsW", setupapidll);
}
if (const auto hiddll = GetModuleHandle(L"hid.dll")) {
for (const auto& name : ORIGINAL_BYTES | std::views::keys) {
for (const auto& name : UnhookUtil::UNHOOK_BYTES_ORIGINAL_22000 | std::views::keys) {
if (name.starts_with("Hid")) {
UnPatchHook(name, hiddll);
UnhookUtil::UnPatchHook(name, hiddll);
}
}
}
}
void HidHide::UnPatchHook(const std::string& name, HMODULE module)
{
spdlog::trace("Patching \"{}\"...", name);
BYTE* address = reinterpret_cast<BYTE*>(GetProcAddress(module, name.c_str()));
if (!address) {
spdlog::error("failed to unpatch \"{}\"", name);
}
std::string bytes;
if (Settings::isWin10 && ORIGINAL_BYTES_WIN10.contains(name)) {
bytes = ORIGINAL_BYTES_WIN10.at(name);
} else {
bytes = ORIGINAL_BYTES.at(name);
}
DWORD dw_old_protect, dw_bkup;
const auto len = bytes.size();
VirtualProtect(address, len, PAGE_EXECUTE_READWRITE, &dw_old_protect); // Change permissions of memory..
const auto opcode = *(address);
if (!std::ranges::any_of(JUMP_INSTR_OPCODES, [&opcode](const auto& op) { return op == opcode; })) {
spdlog::debug("\"{}\" Doesn't appear to be hooked, skipping!", name);
VirtualProtect(address, len, dw_old_protect, &dw_bkup); // Revert permission change...
return;
}
for (DWORD i = 0; i < len; i++) // unpatch Valve's hook
{
*(address + i) = bytes[i];
}
VirtualProtect(address, len, dw_old_protect, &dw_bkup); // Revert permission change...
spdlog::trace("Unpatched \"{}\"", name);
}
#ifndef WATCHDOG
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 (window_has_focus && (overlay_elem_clock_.getElapsedTime().asSeconds() > OVERLAY_ELEM_REFRESH_INTERVAL_S_)) {
// UnPatchValveHooks();
openCtrlDevice();
bool hidehide_state_store = hidhide_active_;
if (Settings::extendedLogging) {
spdlog::debug("Refreshing HID devices");
}
if (hidhide_active_) {
setActive(false);
}
avail_devices_ = GetHidDeviceList();
if (Settings::extendedLogging) {
std::ranges::for_each(avail_devices_, [](const auto& dev) {
spdlog::trace(L"AvailDevice device: {}", dev.name);
});
}
blacklisted_devices_ = getBlackListDevices();
if (hidehide_state_store) {
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)) {
if (device_hiding_setup_) {
if (window_has_focus && (overlay_elem_clock_.getElapsedTime().asSeconds() > OVERLAY_ELEM_REFRESH_INTERVAL_S_)) {
// UnPatchValveHooks();
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);
}
}
bool hidehide_state_store = hidhide_active_;
if (Settings::extendedLogging) {
spdlog::debug("Refreshing HID devices");
}
else {
blacklisted_devices_.erase(std::ranges::remove_if(blacklisted_devices_, findDeviceFn).begin(),
blacklisted_devices_.end());
if (hidhide_active_) {
setActive(false);
}
setBlacklistDevices(blacklisted_devices_);
avail_devices_ = GetHidDeviceList();
if (Settings::extendedLogging) {
std::ranges::for_each(blacklisted_devices_, [](const auto& dev) {
spdlog::trace(L"Blacklisted device: {}", dev);
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());
}
setBlacklistDevices(blacklisted_devices_);
if (Settings::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::EndChild();
if (ImGui::Checkbox("Devices Hidden", &hidhide_active_)) {
openCtrlDevice();
setActive(hidhide_active_);
closeCtrlDevice();
}
}
ImGui::End();
});
}
#endif
std::wstring HidHide::DosDeviceForVolume(const std::wstring& volume)
{

@ -25,7 +25,9 @@ limitations under the License.
#include <map>
#include <string>
#include <vector>
#ifndef WATCHDOG
#include <SFML/System/Clock.hpp>
#endif
class HidHide {
private:
@ -69,40 +71,16 @@ class HidHide {
private:
HANDLE hidhide_handle = nullptr;
// Valve Hooks various functions and hides Gaming devices like this.
// To be able to query them, unpatch the hook with the original bytes...
static inline const std::map<std::string, std::string> ORIGINAL_BYTES = {
{"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"},
};
static inline const std::map<std::string, std::string> ORIGINAL_BYTES_WIN10 = {
{"SetupDiEnumDeviceInfo", "\x40\x53\x56\x57\x41\x54\x41\x55"},
{"SetupDiGetClassDevsW", "\x48\x8B\xC4\x48\x89\x58\x08"},
};
static inline const std::vector<uint8_t> JUMP_INSTR_OPCODES = {
0xE9,
0xE8,
0xEB,
0xEA,
0xFF
};
std::filesystem::path steam_path_;
bool device_hiding_setup_ = false;
static void UnPatchValveHooks();
static void UnPatchHook(const std::string& name, HMODULE module);
#ifndef WATCHDOG
void enableOverlayElement();
sf::Clock overlay_elem_clock_;
#endif
std::vector<std::wstring> blacklisted_devices_;
std::vector<SmallHidInfo> avail_devices_;

@ -50,14 +50,13 @@ void InputRedirector::run()
{
run_ = vigem_connected_;
controller_thread_ = std::thread(&InputRedirector::runLoop, this);
max_controller_count_ = Settings::controller.maxControllers;
use_real_vid_pid_ = Settings::devices.realDeviceIds;
#ifdef _WIN32
Overlay::AddOverlayElem([this](bool window_has_focus, ImGuiID dockspace_id) {
ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
ImGui::Begin("Controller Emulation");
int countcopy = max_controller_count_;
int countcopy = Settings::controller.maxControllers;
ImGui::Text("Max. controller count");
ImGui::SameLine();
ImGui::InputInt("##Max. controller count", &countcopy, 1, 1);
if (countcopy > XUSER_MAX_COUNT) {
countcopy = XUSER_MAX_COUNT;
@ -65,10 +64,27 @@ void InputRedirector::run()
if (countcopy < 0) {
countcopy = 0;
}
max_controller_count_ = countcopy;
Settings::controller.maxControllers = countcopy;
if (ImGui::Checkbox("Emulate DS4 (instead of Xbox360 controller)", &Settings::controller.emulateDS4)) {
controller_settings_changed_ = true;
}
ImGui::Spacing();
if (Settings::launch.launch) {
ImGui::Checkbox("Allow desktop config", &Settings::controller.allowDesktopConfig);
ImGui::Text("Allows desktop config if the launched application is not focused");
ImGui::Spacing();
}
bool enable_rumbe_copy = enable_rumble_;
ImGui::Checkbox("Enable Rumble", &enable_rumbe_copy);
enable_rumble_ = enable_rumbe_copy;
ImGui::Spacing();
if (ImGui::CollapsingHeader("Advanced", ImGuiTreeNodeFlags_DefaultOpen)) {
ImGui::Text("GlosSI uses different USB-IDs for it's emulated controllers");
ImGui::Text("This can cause issues with some games");
@ -76,12 +92,12 @@ void InputRedirector::run()
ImGui::Text("to prevent having multiple mirrored controllers plugged in");
ImGui::Spacing();
ImGui::Text("If enabled, Device hiding won't work! Use \"Max. Controller count\"-setting.");
bool use_real_copy = use_real_vid_pid_;
bool use_real_copy = Settings::devices.realDeviceIds;
ImGui::Checkbox("Use real USB-IDs", &use_real_copy);
if (use_real_vid_pid_ != use_real_copy) {
use_real_vid_pid_changed_ = true;
if (Settings::devices.realDeviceIds != use_real_copy) {
controller_settings_changed_ = true;
}
use_real_vid_pid_ = use_real_copy;
Settings::devices.realDeviceIds = use_real_copy;
}
ImGui::End();
});
@ -108,19 +124,19 @@ void InputRedirector::runLoop()
}
while (run_) {
#ifdef _WIN32
if (use_real_vid_pid_changed_) {
if (controller_settings_changed_) {
// unplug all.
use_real_vid_pid_changed_ = false;
controller_settings_changed_ = false;
for (int i = 0; i < XUSER_MAX_COUNT; i++) {
unplugVigemPad(i);
}
}
if (max_controller_count_ < XUSER_MAX_COUNT) {
for (int i = max_controller_count_; i < XUSER_MAX_COUNT; i++) {
if (Settings::controller.maxControllers < XUSER_MAX_COUNT) {
for (int i = Settings::controller.maxControllers; i < XUSER_MAX_COUNT; i++) {
unplugVigemPad(i);
}
}
for (int i = 0; i < XUSER_MAX_COUNT && i < max_controller_count_; i++) {
for (int i = 0; i < XUSER_MAX_COUNT && i < Settings::controller.maxControllers; i++) {
XINPUT_STATE state{};
if (XInputGetState(i, &state) == ERROR_SUCCESS) {
if (vt_pad_[i] != nullptr) {
@ -160,16 +176,28 @@ void InputRedirector::runLoop()
// Otherwise, this application (GloSC/GlosSI) will pickup the emulated controller as well!
// This however is configurable withon GlosSI overlay;
// Multiple controllers can be worked around with by setting max count.
if (!use_real_vid_pid_) {
vigem_target_set_vid(vt_pad_[i], 0x28de); //VALVE_DIRECTINPUT_GAMEPAD_VID
//vigem_target_set_pid(vt_pad_[i], 0x11FF); //VALVE_DIRECTINPUT_GAMEPAD_PID
vigem_target_set_pid(vt_pad_[i], 0x028E); // XBOX 360 Controller
} else {
vigem_target_set_vid(vt_pad_[i], 0x045E); // MICROSOFT
vigem_target_set_pid(vt_pad_[i], 0x028E); // XBOX 360 Controller
if (!Settings::devices.realDeviceIds) {
vigem_target_set_vid(vt_pad_[i], 0x28de); // VALVE_DIRECTINPUT_GAMEPAD_VID
// vigem_target_set_pid(vt_pad_[i], 0x11FF); //VALVE_DIRECTINPUT_GAMEPAD_PID
if (Settings::controller.emulateDS4) {
vigem_target_set_pid(vt_pad_[i], 0x05C4); // DS4 Controller
}
else {
vigem_target_set_pid(vt_pad_[i], 0x028E); // XBOX 360 Controller
}
}
else {
if (Settings::controller.emulateDS4) {
vigem_target_set_vid(vt_pad_[i], 0x054C); // Sony Corp.
vigem_target_set_pid(vt_pad_[i], 0x05C4); // DS4 Controller
}
else {
vigem_target_set_vid(vt_pad_[i], 0x045E); // MICROSOFT
vigem_target_set_pid(vt_pad_[i], 0x028E); // XBOX 360 Controller
}
}
// TODO: MAYBE!: In a future version, use something like OpenXInput
//and filter out emulated controllers to support a greater amount of controllers simultaneously
// and filter out emulated controllers to support a greater amount of controllers simultaneously
const int target_add_res = vigem_target_add(driver_, vt_pad_[i]);
if (target_add_res == VIGEM_ERROR_TARGET_UNINITIALIZED) {
@ -182,10 +210,10 @@ void InputRedirector::runLoop()
}
if (target_add_res == VIGEM_ERROR_NONE) {
spdlog::info("Plugged in controller {}, {}; VID: {:x}; PID: {:x}",
i,
vigem_target_get_index(vt_pad_[i]),
vigem_target_get_vid(vt_pad_[i]),
vigem_target_get_pid(vt_pad_[i]));
i,
vigem_target_get_index(vt_pad_[i]),
vigem_target_get_vid(vt_pad_[i]),
vigem_target_get_pid(vt_pad_[i]));
if (Settings::controller.emulateDS4) {
const auto callback_register_res = vigem_target_ds4_register_notification(

@ -41,10 +41,8 @@ class InputRedirector {
PVIGEM_CLIENT driver_;
// variables for overlay element; run in different thread
std::atomic<int> max_controller_count_ = XUSER_MAX_COUNT;
static inline std::atomic<bool> enable_rumble_ = true;
static inline std::atomic<bool> use_real_vid_pid_ = false;
static inline std::atomic<bool> use_real_vid_pid_changed_ = false;
static inline std::atomic<bool> controller_settings_changed_ = false;
bool vigem_connected_;

@ -19,9 +19,13 @@ limitations under the License.
#include <utility>
#include <locale>
#include <codecvt>
#include <regex>
#include "Roboto.h"
#include "Settings.h"
#include "GlosSI_logo.h"
#include "../version.hpp"
Overlay::Overlay(
sf::RenderWindow& window,
@ -40,7 +44,6 @@ Overlay::Overlay(
io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad;
io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;
io.Fonts->Clear(); // clear fonts if you loaded some before (even if only default one was loaded)
auto fontconf = ImFontConfig{};
fontconf.FontDataOwnedByAtlas = false;
@ -64,9 +67,17 @@ Overlay::Overlay(
io.IniFilename = config_file_name_.data();
#endif
if (!logo_texture_.loadFromMemory(GLOSSI_LOGO.data(), GLOSSI_LOGO.size())) {
spdlog::trace("Failed to load logo texture");
}
if (logo_texture_.getSize().x > 0) {
logo_sprite_.setTexture(logo_texture_);
logo_sprite_.setScale(0.5f, 0.5f);
}
window.resetGLStates();
//Hack: Trick ImGui::SFML into thinking we already have focus (otherwise only notices after focus-lost)
// Hack: Trick ImGui::SFML into thinking we already have focus (otherwise only notices after focus-lost)
const sf::Event ev(sf::Event::GainedFocus);
ProcessEvent(ev);
@ -155,31 +166,46 @@ void Overlay::update()
{
ImGui::SFML::Update(window_, update_clock_.restart());
if (!enabled_ && !force_enable_ && time_since_start_clock_.getElapsedTime().asSeconds() < SPLASH_DURATION_S_) {
const auto millis = time_since_start_clock_.getElapsedTime().asMilliseconds();
const auto fade_millis = 1000;
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 {
showSplash(128);
}
}
showLogs(0);
if (enabled_ || force_enable_) {
showSplash();
// Create a DockSpace node where any window can be docked
ImGui::SetNextWindowSize({ImGui::GetMainViewport()->Size.x * 0.6f, ImGui::GetMainViewport()->Size.y * 0.7f}, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos({ImGui::GetMainViewport()->Size.x * 0.25f, 100 }, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos({ImGui::GetMainViewport()->Size.x * 0.25f, 100}, ImGuiCond_FirstUseEver);
ImGui::Begin("GlosSI Settings");
ImGui::Text("Version: %s", version::VERSION_STR);
if (Settings::settings_path_ != "") {
if (ImGui::Button("Save shortcut settings", {256, 32})) {
ImGui::SameLine();
if (ImGui::Button("Save shortcut settings", {256 * ImGui::GetIO().FontGlobalScale, 32 * ImGui::GetIO().FontGlobalScale})) {
Settings::StoreSettings();
}
}
ImGui::Checkbox("Extended logging", &Settings::extendedLogging);
ImGuiID dockspace_id = ImGui::GetID("GlosSI-DockSpace");
ImGui::DockSpace(dockspace_id);
window_.clear(sf::Color(0, 0, 0, 128)); // make window slightly dim screen with overlay
std::ranges::for_each(OVERLAY_ELEMS_, [this, &dockspace_id](const auto& elem) {
elem.second(window_.hasFocus(), dockspace_id);
});
ImGui::End();
// ImGui::ShowDemoWindow();
if (closeButton()) {
@ -235,13 +261,11 @@ void Overlay::showLogs(ImGuiID dockspace_id)
std::ranges::copy_if(LOG_MSGS_,
std::back_inserter(logs),
[&logs_contain_warn_or_worse](const auto& log) {
const auto res = (
log.time.time_since_epoch() + std::chrono::seconds(
LOG_RETENTION_TIME_) >
std::chrono::system_clock::now().time_since_epoch())
const auto res = (log.time.time_since_epoch() + std::chrono::seconds(
LOG_RETENTION_TIME_) >
std::chrono::system_clock::now().time_since_epoch())
#ifdef NDEBUG
&& (log.level > spdlog::level::debug)
&& (log.level > spdlog::level::debug)
#endif
;
if (res && log.level > spdlog::level::warn) {
@ -250,7 +274,7 @@ void Overlay::showLogs(ImGuiID dockspace_id)
return res;
});
}
if (logs.empty() || ( !enabled_ && !force_enable_ && !logs_contain_warn_or_worse && time_since_start_clock_.getElapsedTime().asSeconds() > HIDE_NORMAL_LOGS_AFTER_S))
if (logs.empty() || (!enabled_ && !force_enable_ && !logs_contain_warn_or_worse && time_since_start_clock_.getElapsedTime().asSeconds() > HIDE_NORMAL_LOGS_AFTER_S))
return;
ImGui::SetNextWindowSizeConstraints({150, 150}, {1000, window_.getSize().y - 250.f});
if (!enabled_) {
@ -259,7 +283,7 @@ void Overlay::showLogs(ImGuiID dockspace_id)
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoTitleBar);
}
else {
//ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
// ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowSize({ImGui::GetMainViewport()->Size.x * 0.2f, ImGui::GetMainViewport()->Size.y * 0.7f}, ImGuiCond_FirstUseEver);
ImGui::SetNextWindowPos({ImGui::GetMainViewport()->Size.x * 0.05f, 100}, ImGuiCond_FirstUseEver);
log_expanded_ = ImGui::Begin("Log");
@ -297,8 +321,8 @@ bool Overlay::closeOverlayButton() const
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.f, 0.f, 0.0f));
ImGui::Begin("##CloseOverlayButton", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
ImGui::SetWindowPos({(window_.getSize().x - ImGui::GetWindowWidth()) / 2, 32});
ImGui::SetWindowSize({256, 32});
if (ImGui::Button("Return to Game", {256, 32})) {
ImGui::SetWindowSize({256 * ImGui::GetIO().FontGlobalScale, 32 * ImGui::GetIO().FontGlobalScale});
if (ImGui::Button("Return to Game", {256 * ImGui::GetIO().FontGlobalScale, 32 * ImGui::GetIO().FontGlobalScale})) {
trigger_state_change_();
ImGui::End();
ImGui::PopStyleColor();
@ -318,9 +342,9 @@ bool Overlay::closeButton() const
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.6f, 0.f, 0.f, 0.9f));
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(1.f, 0.16f, 0.16f, 1.00f));
ImGui::Begin("##CloseButton", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize);
ImGui::SetWindowSize({56 + 32, 32 + 32});
ImGui::SetWindowSize({(56 + 32) * ImGui::GetIO().FontGlobalScale, (32 + 32) * ImGui::GetIO().FontGlobalScale});
ImGui::SetWindowPos({window_.getSize().x - ImGui::GetWindowWidth() + 32, -32});
if (ImGui::Button("X##Close", {56, 32})) {
if (ImGui::Button("X##Close", {56 * ImGui::GetIO().FontGlobalScale, 32 * ImGui::GetIO().FontGlobalScale})) {
ImGui::End();
ImGui::PopStyleColor();
ImGui::PopStyleColor();
@ -336,3 +360,20 @@ bool Overlay::closeButton() const
ImGui::PopStyleVar();
return false;
}
void Overlay::showSplash(uint8_t alpha)
{
if (logo_texture_.getSize().x > 0) {
ImGui::SetNextWindowPos(
{ImGui::GetMainViewport()->Size.x * 0.5f - static_cast<float>(logo_texture_.getSize().x) * 0.5f * logo_sprite_.getScale().x,
ImGui::GetMainViewport()->Size.y * 0.5f - static_cast<float>(logo_texture_.getSize().y) * 0.5f * logo_sprite_.getScale().y});
ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, {0, 0});
ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0.0f, 0.f, 0.f, 0.0f));
ImGui::Begin("##Splash", nullptr, ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoInputs | ImGuiWindowFlags_NoTitleBar);
ImGui::Image(logo_sprite_, {255, 255, 255, alpha});
ImGui::End();
ImGui::PopStyleColor();
ImGui::PopStyleVar();
}
}

@ -50,10 +50,14 @@ class Overlay {
void showLogs(ImGuiID dockspace_id);
bool closeOverlayButton() const;
[[nodiscard]] bool closeButton() const;
void showSplash(uint8_t alpha = 128);
bool force_enable_ = false;
bool log_expanded_ = true;
sf::Clock time_since_start_clock_;
sf::Texture logo_texture_;
sf::Sprite logo_sprite_;
struct Log {
std::chrono::system_clock::time_point time;
spdlog::level::level_enum level;
@ -62,6 +66,7 @@ class Overlay {
static inline std::vector<Log> LOG_MSGS_;
static constexpr int LOG_RETENTION_TIME_ = 5;
static constexpr int HIDE_NORMAL_LOGS_AFTER_S = 20;
static constexpr int SPLASH_DURATION_S_ = 3;
static inline int overlay_element_id_ = 0;
static inline std::map<int, std::function<void(bool window_has_focus, ImGuiID dockspace_id)>> OVERLAY_ELEMS_;

@ -51,8 +51,8 @@ END
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 0,0,8,1031000051035
PRODUCTVERSION 0,0,8,1031000051035
FILEVERSION 0,0,9,1040000008386
PRODUCTVERSION 0,0,9,1040000008386
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
@ -69,12 +69,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Peter Repukat - FlatspotSoftware"
VALUE "FileDescription", "GlosSI - SteamTarget"
VALUE "FileVersion", "0.0.8.1-31-gda51d35"
VALUE "FileVersion", "0.0.9.1-40-gdae8386"
VALUE "InternalName", "GlosSITarget"
VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware"
VALUE "LegalCopyright", "Copyright (C) 2021-2022 Peter Repukat - FlatspotSoftware"
VALUE "OriginalFilename", "GlosSITarget.exe"
VALUE "ProductName", "GlosSI"
VALUE "ProductVersion", "0.0.8.1-31-gda51d35"
VALUE "ProductVersion", "0.0.9.1-40-gdae8386"
END
END
BLOCK "VarFileInfo"
@ -91,7 +91,7 @@ END
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_ICON1 ICON "GloSC_Icon.ico"
IDI_ICON1 ICON "..\\GlosSI_Icon.ico"
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
@ -160,242 +160,6 @@ IDI_ICON1 ICON "GloSC_Icon.ico"

@ -28,7 +28,6 @@ limitations under the License.
#include <Windows.h>
#endif
namespace Settings {
inline struct Launch {
@ -62,14 +61,13 @@ inline bool extendedLogging = 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,3}:"))) {
if (!std::regex_search(launch_path, m, std::wregex(L"^.{1,5}:"))) {
return true;
}
return false;
@ -78,7 +76,7 @@ inline bool checkIsUwp(const std::wstring& launch_path)
#ifdef WIN32
inline bool isWin10 = false;
typedef LONG NTSTATUS, *PNTSTATUS;
typedef LONG NTSTATUS, *PNTSTATUS;
#define STATUS_SUCCESS (0x00000000)
typedef NTSTATUS(WINAPI* RtlGetVersionPtr)(PRTL_OSVERSIONINFOW);
@ -106,40 +104,50 @@ inline void checkWinVer()
isWin10 = VN.dwBuildNumber < 22000;
if (isWin10) {
spdlog::info("Running on Windows 10");
} else {
spdlog::info("Running on Windows 11");
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(std::wstring arg1)
{
if (!arg1.ends_with(L".json")) {
arg1 += L".json";
const auto config_specified = !std::views::filter(arg1, [](const auto& ch) {
return ch != ' ';
}).empty();
if (config_specified) {
if (!arg1.ends_with(L".json")) {
arg1 += L".json";
}
}
std::filesystem::path path(arg1);
if (path.has_extension() && !std::filesystem::exists(path)) {
path = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
path /= "Roaming";
path /= "GlosSI";
std::filesystem::path path = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
path /= "Roaming";
path /= "GlosSI";
if (config_specified) {
path /= "Targets";
path /= arg1;
}
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;
auto safeParseValue = [](const auto& object, const auto& key, auto& value) {
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;
@ -147,7 +155,9 @@ inline void Parse(std::wstring arg1)
value = object[key];
}
catch (const nlohmann::json::exception& e) {
spdlog::warn("Err parsing \"{}\"; {}", key, e.what());
e.id == 403
? spdlog::trace("Err parsing \"{}\"; {}", key, e.what())
: spdlog::warn("Err parsing \"{}\"; {}", key, e.what());
}
catch (const std::exception& e) {
spdlog::warn("Err parsing \"{}\"; {}", key, e.what());
@ -165,37 +175,44 @@ inline void Parse(std::wstring arg1)
const auto json = nlohmann::json::parse(json_file);
int version;
safeParseValue(json, "version" ,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);
}
if (auto launchconf = json["launch"]; 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);
}
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 devconf = json["devices"]; devconf.is_object()) {
safeParseValue(devconf, "hideDevices", devices.hideDevices);
safeParseValue(devconf, "realDeviceIds", devices.realDeviceIds);
}
if (auto winconf = json["window"]; !winconf.is_null() && !winconf.empty() && winconf.is_object()) {
safeParseValue(winconf, "windowMode", window.windowMode);
safeParseValue(winconf, "maxFps", window.maxFps);
safeParseValue(winconf, "scale", window.scale);
safeParseValue(winconf, "disableOverlay", window.disableOverlay);
}
if (auto winconf = json["window"]; winconf.is_object()) {
safeParseValue(winconf, "windowMode", window.windowMode);
safeParseValue(winconf, "maxFps", window.maxFps);
safeParseValue(winconf, "scale", window.scale);
safeParseValue(winconf, "disableOverlay", window.disableOverlay);
if (auto controllerConf = json["controller"]; !controllerConf.is_null() && !controllerConf.empty() && controllerConf.is_object()) {
safeParseValue(controllerConf, "maxControllers", controller.maxControllers);
safeParseValue(controllerConf, "allowDesktopConfig", controller.allowDesktopConfig);
safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4);
}
}
if (auto controllerConf = json["controller"]; controllerConf.is_object()) {
safeParseValue(controllerConf, "maxControllers", controller.maxControllers);
safeParseValue(controllerConf, "allowDesktopConfig", controller.allowDesktopConfig);
safeParseValue(controllerConf, "emulateDS4", controller.emulateDS4);
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());
}
safeParseValue(json, "extendedLogging", extendedLogging);

@ -74,6 +74,7 @@ Application will not function!");
if (!overlay_.expired())
overlay_.lock()->setEnabled(false);
steam_overlay_present_ = true;
launcher_.launchWatchdog();
}
getXBCRebindingEnabled();

@ -48,15 +48,55 @@ TargetWindow::TargetWindow(
screenshot_keys_(std::move(screenshot_hotkey)),
on_window_changed_(std::move(on_window_changed))
{
createWindow(Settings::window.windowMode);
createWindow();
Overlay::AddOverlayElem([this](bool window_has_focus, ImGuiID dockspace_id) {
ImGui::SetNextWindowDockID(dockspace_id, ImGuiCond_FirstUseEver);
bool windowed_copy = windowed_;
ImGui::Begin("Window mode");
if (ImGui::Checkbox("Window mode", &windowed_copy)) {
ImGui::Begin("Window");
if (ImGui::Checkbox("Window mode", &Settings::window.windowMode)) {
toggle_window_mode_after_frame_ = true;
}
ImGui::Text("Max. FPS");
ImGui::SameLine();
int max_fps_copy = Settings::window.maxFps;
ImGui::InputInt("##max_fps", &max_fps_copy, 20, 20);
ImGui::Text("Values smaller than 15 set the limit to the screen refresh rate.");
if (max_fps_copy != Settings::window.maxFps) {
Settings::window.maxFps = max_fps_copy;
if (Settings::window.maxFps > 240) {
Settings::window.maxFps = 240;
}
if (Settings::window.maxFps < 15 && Settings::window.maxFps > 0) {
Settings::window.maxFps = 0;
setFpsLimit(screen_refresh_rate_);
} else {
setFpsLimit(Settings::window.maxFps);
}
}
ImGui::Spacing();
ImGui::Text("Overlay scale");
ImGui::SameLine();
float scale_copy = Settings::window.scale;
ImGui::DragFloat("##UISCale", &scale_copy, 0.1f, 0.0f, 6.f);
ImGui::Text("Values smaller than 0.3 reset to 1");
if (scale_copy > Settings::window.scale + 0.01f || scale_copy < Settings::window.scale - 0.01f) {
Settings::window.scale = scale_copy;
if (Settings::window.scale < 0.3f) {
spdlog::trace("Scale to small! Scaling overlay to 1");
Settings::window.scale = 0.0f;
ImGuiIO& io = ImGui::GetIO();
io.FontGlobalScale = 1;
ImGui::SFML::UpdateFontTexture();
} else {
spdlog::trace("Scaling overlay: {}", Settings::window.scale);
ImGuiIO& io = ImGui::GetIO();
io.FontGlobalScale = Settings::window.scale;
ImGui::SFML::UpdateFontTexture();
}
}
ImGui::End();
});
@ -65,6 +105,7 @@ TargetWindow::TargetWindow(
void TargetWindow::setFpsLimit(unsigned int fps_limit)
{
spdlog::trace("Limiting FPS to {}", fps_limit);
window_.setFramerateLimit(fps_limit);
}
@ -102,7 +143,7 @@ void TargetWindow::update()
overlay_->update();
window_.display();
if (toggle_window_mode_after_frame_) {
createWindow(!windowed_);
createWindow();
}
// As SFML screws us out of most windows-events, just poll resolution every once in a while
// If changed, recreate window.
@ -110,7 +151,7 @@ void TargetWindow::update()
// (WHY?!)
if (check_resolution_clock_.getElapsedTime().asSeconds() > RES_CHECK_SECONDS) {
if (sf::VideoMode::getDesktopMode().width != old_desktop_mode_.width) {
createWindow(windowed_);
createWindow();
}
check_resolution_clock_.restart();
}
@ -238,17 +279,16 @@ WORD TargetWindow::GetWindowDPI(HWND hWnd)
}
#endif
void TargetWindow::createWindow(bool window_mode)
void TargetWindow::createWindow()
{
toggle_window_mode_after_frame_ = false;
auto desktop_mode = sf::VideoMode::getDesktopMode();
spdlog::info("Detected resolution: {}x{}", desktop_mode.width, desktop_mode.height);
old_desktop_mode_ = desktop_mode;
if (window_mode) {
if (Settings::window.windowMode) {
spdlog::info("Creating Overlay window...");
window_.create(sf::VideoMode(desktop_mode.width * 0.75, desktop_mode.height * 0.75, 32), "GlosSITarget");
windowed_ = true;
}
else {
#ifdef _WIN32
@ -258,7 +298,7 @@ void TargetWindow::createWindow(bool window_mode)
// Due to some other issue, the (Steam) overlay might get blurred when doing this
// as a workaround, start in full size, and scale down later...
spdlog::info("Creating Overlay window (Borderless Fullscreen)...");
window_.create(sf::VideoMode(desktop_mode.width -1, desktop_mode.height -1, 32), "GlosSITarget", sf::Style::None);
window_.create(sf::VideoMode(desktop_mode.width, desktop_mode.height, 32), "GlosSITarget", sf::Style::None);
// get size of all monitors combined
const auto screenWidth = GetSystemMetrics(SM_CXVIRTUALSCREEN);
@ -270,11 +310,15 @@ void TargetWindow::createWindow(bool window_mode)
#else
window_.create(desktop_mode, "GlosSITarget", sf::Style::None);
#endif
windowed_ = false;
}
window_.setActive(true);
spdlog::debug("Window position: {}x{}", window_.getPosition().x, window_.getPosition().y);
if (!Settings::window.windowMode) {
spdlog::info("Resizing window to 1px smaller than fullscreen...");
window_.setSize(sf::Vector2u(desktop_mode.width - 1, desktop_mode.height - 1));
}
#ifdef _WIN32
HWND hwnd = window_.getSystemHandle();
auto dpi = GetWindowDPI(hwnd);
@ -303,14 +347,15 @@ void TargetWindow::createWindow(bool window_mode)
if (EnumDisplaySettings(nullptr, ENUM_CURRENT_SETTINGS, &dev_mode) == 0) {
setFpsLimit(60);
spdlog::warn("Couldn't detect screen refresh rate; Limiting overlay to 60");
screen_refresh_rate_ = 60;
}
else {
setFpsLimit(dev_mode.dmDisplayFrequency);
spdlog::debug("Limiting overlay to FPS to {}", dev_mode.dmDisplayFrequency);
screen_refresh_rate_ = dev_mode.dmDisplayFrequency;
}
overlay_ = std::make_shared<Overlay>(
window_, [this]() { close(); }, toggle_overlay_state_, windowed_);
window_, [this]() { close(); }, toggle_overlay_state_, Settings::window.windowMode);
ImGuiIO& io = ImGui::GetIO();
io.FontGlobalScale = dpi / 96.f;
@ -321,8 +366,8 @@ void TargetWindow::createWindow(bool window_mode)
#endif
if (Settings::window.maxFps > 0) {
spdlog::debug("Config file fps limit seems sane...");
setFpsLimit(Settings::window.maxFps);
spdlog::debug("Limiting overlay to FPS from config-file to {}", Settings::window.maxFps);
}
if (Settings::window.scale > 0.3f) { // Now that's just getting ridicoulus
ImGuiIO& io = ImGui::GetIO();

@ -74,11 +74,12 @@ class TargetWindow {
sf::Clock check_resolution_clock_;
static constexpr int RES_CHECK_SECONDS = 1;
unsigned int screen_refresh_rate_ = 0;
std::shared_ptr<Overlay> overlay_;
void createWindow(bool window_mode);
void createWindow();
bool windowed_ = false;
bool toggle_window_mode_after_frame_ = false;
};

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

@ -0,0 +1,61 @@
/*
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#define NOMINMAX
#include <map>
#include <Windows.h>
#include <string>
#include <vector>
namespace UnhookUtil {
void UnPatchHook(const std::string& name, HMODULE module);
static inline const std::vector<uint8_t> JUMP_INSTR_OPCODES = {
0xE9,
0xE8,
0xEB,
0xEA,
0xFF};
// Valve Hooks various functions and hides Gaming devices like this.
// To be able to query them, unpatch the hook with the original bytes...
// 22000 ^= Windows build number
static inline const std::map<std::string, std::string> UNHOOK_BYTES_ORIGINAL_22000 = {
{"SetupDiEnumDeviceInfo", "\x48\x89\x5C\x24\x08"},
{"SetupDiGetClassDevsW", "\x48\x89\x5C\x24\x08"},
{"HidD_GetPreparsedData", "\x48\x89\x5C\x24\x18"},
{"HidP_GetCaps", "\x4C\x8B\xD1\x48\x85\xC9"},
{"HidD_GetAttributes", "\x40\x53\x48\x83\xEC"},
{"HidD_GetProductString", "\x48\x83\xEC\x48\x48"},
{"HidP_GetUsages", "\x4C\x89\x4C\x24\x20"},
{"HidP_GetData", "\x4C\x89\x44\x24\x18"},
{"HidP_GetValueCaps", "\x48\x83\xEC\x48\x49"},
{"HidP_GetUsageValue", "\x40\x53\x55\x56\x48"},
{"HidP_GetButtonCaps", "\x48\x83\xEC\x48\x49"},
// Valve hooks "CreateProcess" to detect child-processes
{"CreateProcessW", "\x4C\x8B\xDC\x48\x83"},
};
// SetupApi.dll is different on Win10 than on Win11
static inline const std::map<std::string, std::string> UNHOOK_BYTES_ORIGINAL_WIN10 = {
{"SetupDiEnumDeviceInfo", "\x40\x53\x56\x57\x41\x54\x41\x55"},
{"SetupDiGetClassDevsW", "\x48\x8B\xC4\x48\x89\x58\x08"},
};
} // namespace UnhookUtil

@ -1,18 +0,0 @@
{
"version": 1,
"launch": {
"launch": false,
"launchPath": "C:\\Users\\Alia5\\AppData\\Local\\Programs\\Microsoft VS Code\\Code.exe",
"launchAppArgs": "--new-window",
"closeOnExit": true
},
"devices": {
"hideDevices": true
},
"window": {
"windowMode": false,
"maxFps": null,
"scale": null
},
"icon": null
}

@ -29,6 +29,8 @@ limitations under the License.
#include "Settings.h"
#include <iostream>
#include "../version.hpp"
#ifdef _WIN32
// default to high performance GPU
@ -147,6 +149,7 @@ int main(int argc, char* argv[])
logger->flush_on(spdlog::level::trace);
spdlog::set_default_logger(logger);
SetUnhandledExceptionFilter(static_cast<LPTOP_LEVEL_EXCEPTION_FILTER>(Win32FaultHandler));
spdlog::info("GlosSITarget version: {}", version::VERSION_STR);
auto exit = 1;
try {
#ifdef _WIN32

@ -0,0 +1,100 @@
// Microsoft Visual C++ generated resource script.
//
#include "resource.h"
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "winres.h"
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
// English (United States) resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
#pragma code_page(1252)
#ifdef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//
1 TEXTINCLUDE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE
BEGIN
"#include ""winres.h""\r\n"
"\0"
END
3 TEXTINCLUDE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
VS_VERSION_INFO VERSIONINFO
FILEVERSION 1,0,0,1
PRODUCTVERSION 1,0,0,1
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
#else
FILEFLAGS 0x0L
#endif
FILEOS 0x40004L
FILETYPE 0x1L
FILESUBTYPE 0x0L
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040704b0"
BEGIN
VALUE "CompanyName", "Peter Repukat - FlatspotSoftware"
VALUE "FileDescription", "GlosSI - SteamTarget Watchdog"
VALUE "FileVersion", "1.0.0.1"
VALUE "InternalName", "GlosSIWatchdog"
VALUE "LegalCopyright", "Copyright (C) 2021-2022 Peter Repukat - FlatspotSoftware"
VALUE "OriginalFilename", "GlosSIWatchdog.exe"
VALUE "ProductName", "GlosSI"
VALUE "ProductVersion", "1.0.0.1"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x407, 1200
END
END
#endif // English (United States) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

@ -0,0 +1,161 @@
<?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>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{bf273b90-cb69-43c8-9af6-f3256dafd41e}</ProjectGuid>
<RootNamespace>GlosSIWatchdog</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>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</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\spdlog\include;..\deps\json\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<IncludePath>..\deps\spdlog\include;..\deps\json\include;$(IncludePath)</IncludePath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</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;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Windows</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;_WINDOWS;%(PreprocessorDefinitions);WATCHDOG;SPDLOG_WCHAR_TO_UTF8_SUPPORT;SPDLOG_WCHAR_FILENAMES;_SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<LanguageStandard>stdcpp20</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>hid.lib;Cfgmgr32.lib;setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<CustomBuildStep>
<Command>powershell.exe $(SolutionDir)version_help.ps1</Command>
<Outputs>Upading version based on git;%(Outputs)</Outputs>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<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>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>hid.lib;Cfgmgr32.lib;setupapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<CustomBuildStep>
<Outputs>Upading version based on git;%(Outputs)</Outputs>
<Command>powershell.exe $(SolutionDir)version_help.ps1</Command>
</CustomBuildStep>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="..\GlosSITarget\HidHide.cpp" />
<ClCompile Include="..\GlosSITarget\UnhookUtil.cpp" />
<ClCompile Include="main.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="GlosSIWatchdog.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

@ -0,0 +1,38 @@
<?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>
<ClCompile Include="main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\GlosSITarget\HidHide.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="..\GlosSITarget\UnhookUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="resource.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="GlosSIWatchdog.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

@ -0,0 +1,77 @@
/*
Copyright 2021-2022 Peter Repukat - FlatspotSoftware
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#define NOMINMAX
#include <Windows.h>
#include <filesystem>
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
#include "../version.hpp"
#include "../GlosSITarget/HidHide.h"
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPWSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
auto configDirPath = std::filesystem::temp_directory_path()
.parent_path()
.parent_path()
.parent_path();
configDirPath /= "Roaming";
configDirPath /= "GlosSI";
if (!std::filesystem::exists(configDirPath))
std::filesystem::create_directories(configDirPath);
auto logPath = configDirPath;
logPath /= "GlosSIWatchdog.log";
const auto file_sink = std::make_shared<spdlog::sinks::basic_file_sink_mt>(logPath.wstring(), true);
std::vector<spdlog::sink_ptr> sinks{ file_sink };
auto logger = std::make_shared<spdlog::logger>("log", sinks.begin(), sinks.end());
logger->set_level(spdlog::level::trace);
logger->flush_on(spdlog::level::trace);
spdlog::set_default_logger(logger);
spdlog::info("GlosSIWatchdog loaded");
spdlog::info("Version: {}", version::VERSION_STR);
auto glossi_hwnd = FindWindowA(nullptr, "GlosSITarget");
if (!glossi_hwnd)
{
spdlog::error("Couldn't find GlosSITarget window. Exiting...");
return 1;
}
spdlog::debug("Found GlosSITarget window; Starting watch loop");
while (glossi_hwnd)
{
glossi_hwnd = FindWindowA(nullptr, "GlosSITarget");
Sleep(1337);
}
spdlog::info("GlosSITarget was closed. Cleaning up...");
HidHide hidhide;
hidhide.disableHidHide();
return 0;
}

@ -0,0 +1,14 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by GlosSIWatchdog.rc
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 978 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

@ -18,5 +18,7 @@ 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 "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"
7z a GlosSI-snapshot.zip *

@ -8,4 +8,10 @@ cd deps/traypp
git apply ../../traypp_unicode.patch
cd ../../
cd ../../
cd .\GlosSIConfig\
..\version_help.ps1
cd ../

@ -10,6 +10,29 @@ $commatag = $commatag -replace "[A-z]","0"
((Get-Content -path ./Resource.rc -Raw) -replace '"FileVersion", ".*"', ('"FileVersion", "' + $tag + '"')) | Set-Content -Path ./Resource.rc
((Get-Content -path ./Resource.rc -Raw) -replace '"ProductVersion", ".*"', ('"ProductVersion", "' + $tag + '"')) | Set-Content -Path ./Resource.rc
$cleanTag = git describe --tags --abbrev=0
$versionFileText = "
/* Autogenerated version info file */
#pragma once
namespace version {
inline const auto VERSION_STR = ""$tag"";
inline const auto IS_SNAPSHOT = $( If ($tag.Length -gt 10) {'true'} Else {'false'});
inline const auto VERSION_MAJOR = $($cleanTag.Split('.')[0]);
inline const auto VERSION_MINOR = $($cleanTag.Split('.')[1]);
inline const auto VERSION_PATCH = $($cleanTag.Split('.')[2]);
inline const auto VERSION_REVISION = $($cleanTag.Split('.')[3]);
}
"
if (!(Test-Path '../version.hpp')) {
New-Item -Path ".." -Name "version.hpp" -ItemType "file" -Value $versionFileText
} else {
Set-Content -Path "../version.hpp" -Value $versionFileText
}
if ((Test-Path '../Installer/Installer.nsi')) {
((Get-Content -path '../Installer/Installer.nsi' -Raw) -replace '!define VERSION ".*"', ('!define VERSION "' + $tag + '"')) | Set-Content -Path '../Installer/Installer.nsi'
}
Loading…
Cancel
Save