main
Peter Repukat 3 years ago
parent b0e2aa4dc1
commit 3f250bdc22

@ -0,0 +1,7 @@
UseTab: false
IndentWidth: 4
BreakBeforeBraces: "Stroustrup"
AllowShortIfStatementsOnASingleLine: false
IndentCaseLabels: false
ColumnLimit: 0
PointerAlignment: "Left"

@ -123,8 +123,10 @@
<ItemGroup>
<ClCompile Include="main.cpp" />
<ClCompile Include="UIModel.cpp" />
<None Include=".clang-format" />
<None Include="qml\AddSelectTypeDialog.qml" />
<None Include="qml\FluentTextInput.qml" />
<None Include="qml\InfoDialog.qml" />
<None Include="qml\RPane.qml" />
<None Include="qml\ShortcutCards.qml" />
<None Include="qml\ShortcutProps.qml" />
@ -136,6 +138,7 @@
<QtMoc Include="UIModel.h" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="UWPFetch.h" />
<ClInclude Include="VDFParser.h" />
<ClInclude Include="WinEventFilter.h" />
</ItemGroup>

@ -51,12 +51,16 @@
<None Include="qml\ShortcutProps.qml">
<Filter>qml</Filter>
</None>
<None Include="qml\FluentTextInput.qml">
<Filter>Source Files</Filter>
</None>
<None Include="qml\UWPSelectDialog.qml">
<Filter>qml</Filter>
</None>
<None Include="qml\InfoDialog.qml">
<Filter>qml</Filter>
</None>
<None Include="qml\FluentTextInput.qml">
<Filter>qml</Filter>
</None>
<None Include=".clang-format" />
</ItemGroup>
<ItemGroup>
<QtMoc Include="UIModel.h">
@ -70,6 +74,9 @@
<ClInclude Include="VDFParser.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="UWPFetch.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<Xml Include="manifest.xml">

@ -16,27 +16,13 @@ limitations under the License.
#include "UIModel.h"
#include <QDir>
#include <QJsonDocument>
#ifdef _WIN32
#include <Windows.h>
#include <VersionHelpers.h>
#include <collection.h>
#include <appmodel.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <windows.h>
#include <appxpackaging.h>
#pragma comment(lib, "Shlwapi.lib")
using namespace Windows::Management::Deployment;
using namespace Windows::Foundation::Collections;
#include <QGuiApplication>
#include <QJsonDocument>
#ifdef _WIN32
#include <WinReg/WinReg.hpp>
#endif
#ifdef _WIN32
#include "UWPFetch.h"
#endif
UIModel::UIModel() : QObject(nullptr)
@ -59,7 +45,6 @@ UIModel::UIModel() : QObject(nullptr)
parseShortcutVDF();
readConfigs();
}
void UIModel::readConfigs()
@ -70,14 +55,12 @@ void UIModel::readConfigs()
return !entry.endsWith(".json");
});
std::for_each(entries.begin(), entries.end(), [this](const auto& name)
{
std::for_each(entries.begin(), entries.end(), [this](const auto& name) {
auto path = config_path_;
path /= config_dir_name_.toStdString();
path /= name.toStdString();
QFile file(path);
if (!file.open(QIODevice::Text | QIODevice::ReadOnly))
{
if (!file.open(QIODevice::Text | QIODevice::ReadOnly)) {
// meh
return;
}
@ -103,7 +86,6 @@ void UIModel::readConfigs()
targets_.append(json.toVariantMap());
});
emit targetListChanged();
}
@ -152,12 +134,9 @@ void UIModel::deleteTarget(int index)
bool UIModel::isInSteam(QVariant shortcut)
{
const auto map = shortcut.toMap();
for (auto& steam_shortcut : shortcuts_vdf_.shortcuts)
{
if (map["name"].toString() == QString::fromStdString(steam_shortcut.appName.value))
{
if (QString::fromStdString(steam_shortcut.exe.value).toLower().contains("glossitarget.exe"))
{
for (auto& steam_shortcut : shortcuts_vdf_.shortcuts) {
if (map["name"].toString() == QString::fromStdString(steam_shortcut.appName.value)) {
if (QString::fromStdString(steam_shortcut.exe.value).toLower().contains("glossitarget.exe")) {
return true;
}
}
@ -178,11 +157,9 @@ bool UIModel::addToSteam(QVariant shortcut)
vdfshortcut.idx = shortcuts_vdf_.shortcuts.size();
vdfshortcut.appName.value = name.toStdString();
vdfshortcut.exe.value = ("\"" + appDir.absolutePath() + "/GlosSITarget.exe" + "\"").toStdString();
vdfshortcut.StartDir.value = (
launch && !maybeLaunchPath.isEmpty()
vdfshortcut.StartDir.value = (launch && !maybeLaunchPath.isEmpty()
? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"")
: ("\"" + appDir.absolutePath() + "\"").toStdString()
);
: ("\"" + appDir.absolutePath() + "\"").toStdString());
vdfshortcut.appId.value = VDFParser::Parser::calculateAppId(vdfshortcut);
// ShortcutPath; default
vdfshortcut.LaunchOptions.value = (name + ".json").toStdString();
@ -195,11 +172,11 @@ bool UIModel::addToSteam(QVariant shortcut)
// DevkitOverrideAppID; default
// LastPlayTime; default
auto maybeIcon = map["icon"].toString();
if (maybeIcon.isEmpty())
{
if (maybeIcon.isEmpty()) {
if (launch && !maybeLaunchPath.isEmpty())
vdfshortcut.icon.value = maybeLaunchPath.toStdString();
} else {
}
else {
vdfshortcut.icon.value = maybeIcon.toStdString();
}
// Add installed locally and GlosSI tag
@ -217,19 +194,16 @@ bool UIModel::addToSteam(QVariant shortcut)
const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + shortcutsfile_.toStdWString();
return VDFParser::Parser::writeShortcuts(config_path, shortcuts_vdf_);
}
bool UIModel::removeFromSteam(const QString& name)
{
auto& scuts = shortcuts_vdf_.shortcuts;
scuts.erase(std::remove_if(scuts.begin(), scuts.end(), [&name](const auto& shortcut)
{
scuts.erase(std::remove_if(scuts.begin(), scuts.end(), [&name](const auto& shortcut) {
return shortcut.appName.value == name.toStdString();
}), scuts.end());
for (int i = 0; i < scuts.size(); i++)
{
if (scuts[i].idx != i)
{
}),
scuts.end());
for (int i = 0; i < scuts.size(); i++) {
if (scuts[i].idx != i) {
scuts[i].idx = i;
}
}
@ -240,232 +214,7 @@ bool UIModel::removeFromSteam(const QString& name)
#ifdef _WIN32
QVariantList UIModel::uwpApps()
{
// TODO really should do this async, and notify gui when complete...
if (!IsWindows10OrGreater())
{
return QVariantList();
}
std::vector<std::wstring> logoNames{
L"Square150x150Logo",
L"Square310x310Logo",
L"Square44x44Logo",
L"Square71x71Logo",
L"Square70x70Logo",
L"Logo",
L"SmallLogo",
L"Square30x30Logo",
};
QVariantList pairs;
// is it considered stealing when you take code that was pull-requested by someone else into your own repo?
// Anyway... Stolen from: https://github.com/Thracky/GloSC/commit/3cd92e058498e3ab9d73ced140bbd7e490f639a7
// https://github.com/Alia5/GloSC/commit/3cd92e058498e3ab9d73ced140bbd7e490f639a7
// TODO: only return apps for current user.
// TODO: I have no clue how this WinRT shit works; HELP MEH!
PackageManager^ packageManager = ref new PackageManager();
IIterable<Windows::ApplicationModel::Package^>^ packages = packageManager->FindPackages();
int packageCount = 0;
// Only way to get the count of packages is to iterate through the whole collection first
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
[&](Windows::ApplicationModel::Package^ package)
{
packageCount += 1;
});
int currPackage = 0;
// Iterate through all the packages
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
[&](Windows::ApplicationModel::Package^ package)
{
QGuiApplication::processEvents();
HRESULT hr = S_OK;
IStream* inputStream = NULL;
UINT32 pathLen = 0;
IAppxManifestReader* manifestReader = NULL;
IAppxFactory* appxFactory = NULL;
LPWSTR appId = NULL;
LPWSTR manifestAppName = NULL;
LPWSTR iconName = NULL;
// Get the package path on disk so we can load the manifest XML and get the PRAID
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, NULL);
if (pathLen > 0) {
// Length of the path + "\\AppxManifest.xml" that we'll be appending
UINT32 manifestLen = pathLen + 20;
PWSTR pathBuf = (PWSTR)malloc(manifestLen * sizeof(wchar_t));
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, pathBuf);
PWSTR manifest_xml = L"\\AppxManifest.xml";
hr = StringCchCatW(pathBuf, manifestLen, manifest_xml);
// Let's ignore a bunch of built in apps and such
if (wcsstr(pathBuf, L"SystemApps")) {
hr = E_FAIL;
}
else if (wcsstr(pathBuf, L".NET.Native."))
hr = E_FAIL;
else if (wcsstr(pathBuf, L".VCLibs."))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.UI"))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.Advertising"))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.Services.Store"))
hr = E_FAIL;
BOOL hasCurrent = FALSE;
// Open the manifest XML
if (SUCCEEDED(hr)) {
hr = SHCreateStreamOnFileEx(
pathBuf,
STGM_READ | STGM_SHARE_EXCLUSIVE,
0, // default file attributes
FALSE, // do not create new file
NULL, // no template
&inputStream);
}
if (SUCCEEDED(hr)) {
hr = CoCreateInstance(
__uuidof(AppxFactory),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IAppxFactory),
(LPVOID*)(&appxFactory));
}
if (SUCCEEDED(hr)) {
hr = appxFactory->CreateManifestReader(inputStream, &manifestReader);
}
// Grab application ID (PRAID) and DisplayName from the XML
if (SUCCEEDED(hr)) {
IAppxManifestApplicationsEnumerator* applications = NULL;
manifestReader->GetApplications(&applications);
if (SUCCEEDED(hr)) {
hr = applications->GetHasCurrent(&hasCurrent);
if (hasCurrent) {
IAppxManifestApplication* application = NULL;
hr = applications->GetCurrent(&application);
if (SUCCEEDED(hr)) {
application->GetStringValue(L"Id", &appId);
application->GetStringValue(L"DisplayName", &manifestAppName);
for (auto& logoNameStr : logoNames)
{
application->GetStringValue(logoNameStr.c_str(), &iconName);
if (!std::wstring(iconName).empty())
{
break;
}
}
application->Release();
}
}
else {
hr = S_FALSE;
}
applications->Release();
}
manifestReader->Release();
inputStream->Release();
}
if (SUCCEEDED(hr)) {
PWSTR appNameBuf;
QString AppUMId = QString::fromWCharArray(package->Id->FamilyName->Data());
QString AppName;
QString Path = QString::fromWCharArray(package->EffectivePath->Data());
// QString thumbToken = QString::fromWCharArray(package->GetThumbnailToken()->Data());
if (manifestAppName != NULL)
{
// If the display name is an indirect string, we'll try and load it using SHLoadIndirectString
if (wcsstr(manifestAppName, L"ms-resource:"))
{
PWSTR res_name = wcsdup(&manifestAppName[12]);
appNameBuf = (PWSTR)malloc(1026);
LPCWSTR resource_str = L"@{";
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/resources/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
// Try several resource paths
if (!SUCCEEDED(hr)) {
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/Resources/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
// If the third one doesn't work, we give up and use the package name from PackageManager
if (!SUCCEEDED(hr)) {
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
}
}
if (!SUCCEEDED(hr))
AppName = QString::fromWCharArray(package->DisplayName->Data());
else
AppName = QString::fromWCharArray(appNameBuf);
free(appNameBuf);
}
else
{
appNameBuf = manifestAppName;
AppName = QString::fromWCharArray(appNameBuf);
}
}
else {
AppName = QString::fromWCharArray(package->DisplayName->Data());
}
QString PRAID = QString::fromWCharArray(appId);
CoTaskMemFree(appId);
if (!PRAID.isEmpty()) {
AppUMId = AppUMId.append("!");
AppUMId = AppUMId.append(PRAID);
}
QVariantMap uwpPair;
uwpPair.insert("AppName", AppName);
uwpPair.insert("AppUMId", AppUMId);
uwpPair.insert("Path", Path);
QString icoFName = Path + "/" + QString::fromWCharArray(iconName);
std::filesystem::path icoPath(icoFName.toStdString());
std::vector<QString> possibleextensions = { ".scale-100", ".scale-125", ".scale-150", ".scale-200" };
if (!std::filesystem::exists(icoPath))
{
for (const auto& ext : possibleextensions)
{
QString maybeFname = QString(icoFName).replace(".png", ext + ".png");
std::filesystem::path maybePath(maybeFname.toStdString());
if (std::filesystem::exists(maybePath))
{
icoPath = maybePath;
break;
}
}
}
uwpPair.insert("IconPath", QString::fromStdString(icoPath.string()));
free(pathBuf);
pairs.push_back(uwpPair);
}
}
currPackage += 1;
});
return pairs;
return UWPFetch::UWPAppList();
}
#endif
@ -491,8 +240,7 @@ void UIModel::writeTarget(const QJsonObject& json, const QString& name)
path /= config_dir_name_.toStdString();
path /= (name + ".json").toStdString();
QFile file(path);
if (!file.open(QIODevice::Text | QIODevice::ReadWrite))
{
if (!file.open(QIODevice::Text | QIODevice::ReadWrite)) {
// meh
return;
}

@ -14,15 +14,13 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <filesystem>
#include "VDFParser.h"
#include <QJsonObject>
#include <QObject>
#include <QVariant>
#include <QJsonObject>
#include "VDFParser.h"
#include <filesystem>
class UIModel : public QObject
{
class UIModel : public QObject {
Q_OBJECT
Q_PROPERTY(bool isWindows READ getIsWindows CONSTANT)
@ -59,7 +57,6 @@ private:
void writeTarget(const QJsonObject& json, const QString& name);
std::filesystem::path getSteamPath() const;
std::wstring getSteamUserId() const;
void parseShortcutVDF();

@ -0,0 +1,237 @@
#pragma once
#include <QVariant>
#include <VersionHelpers.h>
#include <Windows.h>
#include <appmodel.h>
#include <appxpackaging.h>
#include <collection.h>
#include <shlwapi.h>
#include <strsafe.h>
#include <windows.h>
#pragma comment(lib, "Shlwapi.lib")
using namespace Windows::Management::Deployment;
using namespace Windows::Foundation::Collections;
namespace UWPFetch {
QVariantList UWPAppList()
{
// TODO really should do this async, and notify gui when complete...
if (!IsWindows10OrGreater()) {
return QVariantList();
}
std::vector<std::wstring> logoNames{
L"Square150x150Logo",
L"Square310x310Logo",
L"Square44x44Logo",
L"Square71x71Logo",
L"Square70x70Logo",
L"Logo",
L"SmallLogo",
L"Square30x30Logo",
};
QVariantList pairs;
// is it considered stealing when you take code that was pull-requested by someone else into your own repo?
// Anyway... Stolen from: https://github.com/Thracky/GloSC/commit/3cd92e058498e3ab9d73ced140bbd7e490f639a7
// https://github.com/Alia5/GloSC/commit/3cd92e058498e3ab9d73ced140bbd7e490f639a7
// TODO: only return apps for current user.
// TODO: I have no clue how this WinRT shit works; HELP MEH!
// BTW: This config app needs to run as admin, in order to modify steams shortcuts.vdf
// This won't hopefully be a problem fetching UWP apps for a specific user?
PackageManager ^ packageManager = ref new PackageManager();
IIterable<Windows::ApplicationModel::Package ^> ^ packages = packageManager->FindPackages();
int packageCount = 0;
// Only way to get the count of packages is to iterate through the whole collection first
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
[&](Windows::ApplicationModel::Package ^ package) {
packageCount += 1;
});
int currPackage = 0;
// Iterate through all the packages
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
[&](Windows::ApplicationModel::Package ^ package) {
QGuiApplication::processEvents();
HRESULT hr = S_OK;
IStream* inputStream = NULL;
UINT32 pathLen = 0;
IAppxManifestReader* manifestReader = NULL;
IAppxFactory* appxFactory = NULL;
LPWSTR appId = NULL;
LPWSTR manifestAppName = NULL;
LPWSTR iconName = NULL;
// Get the package path on disk so we can load the manifest XML and get the PRAID
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, NULL);
if (pathLen > 0) {
// Length of the path + "\\AppxManifest.xml" that we'll be appending
UINT32 manifestLen = pathLen + 20;
PWSTR pathBuf = (PWSTR)malloc(manifestLen * sizeof(wchar_t));
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, pathBuf);
PWSTR manifest_xml = L"\\AppxManifest.xml";
hr = StringCchCatW(pathBuf, manifestLen, manifest_xml);
// Let's ignore a bunch of built in apps and such
if (wcsstr(pathBuf, L"SystemApps")) {
hr = E_FAIL;
}
else if (wcsstr(pathBuf, L".NET.Native."))
hr = E_FAIL;
else if (wcsstr(pathBuf, L".VCLibs."))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.UI"))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.Advertising"))
hr = E_FAIL;
else if (wcsstr(pathBuf, L"Microsoft.Services.Store"))
hr = E_FAIL;
BOOL hasCurrent = FALSE;
// Open the manifest XML
if (SUCCEEDED(hr)) {
hr = SHCreateStreamOnFileEx(
pathBuf,
STGM_READ | STGM_SHARE_EXCLUSIVE,
0, // default file attributes
FALSE, // do not create new file
NULL, // no template
&inputStream);
}
if (SUCCEEDED(hr)) {
hr = CoCreateInstance(
__uuidof(AppxFactory),
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IAppxFactory),
(LPVOID*)(&appxFactory));
}
if (SUCCEEDED(hr)) {
hr = appxFactory->CreateManifestReader(inputStream, &manifestReader);
}
// Grab application ID (PRAID) and DisplayName from the XML
if (SUCCEEDED(hr)) {
IAppxManifestApplicationsEnumerator* applications = NULL;
manifestReader->GetApplications(&applications);
if (SUCCEEDED(hr)) {
hr = applications->GetHasCurrent(&hasCurrent);
if (hasCurrent) {
IAppxManifestApplication* application = NULL;
hr = applications->GetCurrent(&application);
if (SUCCEEDED(hr)) {
application->GetStringValue(L"Id", &appId);
application->GetStringValue(L"DisplayName", &manifestAppName);
for (auto& logoNameStr : logoNames) {
application->GetStringValue(logoNameStr.c_str(), &iconName);
if (!std::wstring(iconName).empty()) {
break;
}
}
application->Release();
}
}
else {
hr = S_FALSE;
}
applications->Release();
}
manifestReader->Release();
inputStream->Release();
}
if (SUCCEEDED(hr)) {
PWSTR appNameBuf;
QString AppUMId = QString::fromWCharArray(package->Id->FamilyName->Data());
QString AppName;
QString Path = QString::fromWCharArray(package->EffectivePath->Data());
// QString thumbToken = QString::fromWCharArray(package->GetThumbnailToken()->Data());
if (manifestAppName != NULL) {
// If the display name is an indirect string, we'll try and load it using SHLoadIndirectString
if (wcsstr(manifestAppName, L"ms-resource:")) {
PWSTR res_name = wcsdup(&manifestAppName[12]);
appNameBuf = (PWSTR)malloc(1026);
LPCWSTR resource_str = L"@{";
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/resources/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
// Try several resource paths
if (!SUCCEEDED(hr)) {
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/Resources/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
// If the third one doesn't work, we give up and use the package name from PackageManager
if (!SUCCEEDED(hr)) {
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/" + res_name + L"}";
PCWSTR res_str = reslookup.c_str();
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
}
}
if (!SUCCEEDED(hr))
AppName = QString::fromWCharArray(package->DisplayName->Data());
else
AppName = QString::fromWCharArray(appNameBuf);
free(appNameBuf);
}
else {
appNameBuf = manifestAppName;
AppName = QString::fromWCharArray(appNameBuf);
}
}
else {
AppName = QString::fromWCharArray(package->DisplayName->Data());
}
QString PRAID = QString::fromWCharArray(appId);
CoTaskMemFree(appId);
if (!PRAID.isEmpty()) {
AppUMId = AppUMId.append("!");
AppUMId = AppUMId.append(PRAID);
}
QVariantMap uwpPair;
uwpPair.insert("AppName", AppName);
uwpPair.insert("AppUMId", AppUMId);
uwpPair.insert("Path", Path);
QString icoFName = Path + "/" + QString::fromWCharArray(iconName);
std::filesystem::path icoPath(icoFName.toStdString());
std::vector<QString> possibleextensions = {".scale-100", ".scale-125", ".scale-150", ".scale-200"};
if (!std::filesystem::exists(icoPath)) {
for (const auto& ext : possibleextensions) {
QString maybeFname = QString(icoFName).replace(".png", ext + ".png");
std::filesystem::path maybePath(maybeFname.toStdString());
if (std::filesystem::exists(maybePath)) {
icoPath = maybePath;
break;
}
}
}
uwpPair.insert("IconPath", QString::fromStdString(icoPath.string()));
free(pathBuf);
pairs.push_back(uwpPair);
}
}
currPackage += 1;
});
return pairs;
}
} // namespace UWPFetch

@ -17,17 +17,15 @@ limitations under the License.
// "Shitty shortcuts.vdf Parser"<22>
#pragma once
#include <charconv>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <charconv>
namespace VDFParser
{
namespace crc
{
namespace VDFParser {
namespace crc {
template <typename CONT>
uint32_t calculate_crc(CONT container)
{
@ -38,7 +36,8 @@ namespace VDFParser
for (size_t j = 0; j < 8; j++) {
uint32_t b = (ch ^ crc) & 1;
crc >>= 1;
if (b) crc = crc ^ 0xEDB88320;
if (b)
crc = crc ^ 0xEDB88320;
ch >>= 1;
}
crc32_table[i] = crc;
@ -51,7 +50,7 @@ namespace VDFParser
}
return ~crc;
}
}
} // namespace crc
static constexpr const char k_appid[] = {"appid"};
static constexpr const char k_appname[] = {"appname"};
@ -70,16 +69,14 @@ namespace VDFParser
static constexpr const char k_LastPlayTime[] = {"LastPlayTime"};
static constexpr const char k_tags[] = {"tags"};
enum VDFTypeId
{
enum VDFTypeId {
StringList = 0,
String,
Number,
};
template <const char* const keyname, typename type, const uint8_t const _type_id>
struct VDFKeyPair
{
struct VDFKeyPair {
VDFKeyPair() {}
explicit VDFKeyPair(type _value) : value(_value) {}
static constexpr uint8_t _TID = _type_id;
@ -108,8 +105,7 @@ namespace VDFParser
};
template <uint8_t initialByte>
struct VDFIdx
{
struct VDFIdx {
VDFIdx(){};
VDFIdx(const VDFIdx& other)
{
@ -123,26 +119,23 @@ namespace VDFParser
};
VDFIdx(int idx)
{
if (idx > 99)
{
if (idx > 99) {
data[0] = initialByte;
data[1] = 0;
}
else if (idx < 10)
{
else if (idx < 10) {
data[1] = std::to_string(idx).c_str()[0];
}
else
{
else {
auto meh = std::to_string(idx).c_str();
data[0] = meh[0] + initialByte;
data[1] = meh[1];
}
}
char data[2] = {initialByte, 0x0};
operator int() const {
if (data[0] == initialByte)
operator int() const
{
if (data[0] == initialByte) {
int res = 0;
std::from_chars(&data[1], &data[1], res);
return res;
@ -167,8 +160,7 @@ namespace VDFParser
}
};
struct ShortcutTag
{
struct ShortcutTag {
ShortcutTag(){};
ShortcutTag(const ShortcutTag& other)
{
@ -194,8 +186,7 @@ namespace VDFParser
}
};
struct Shortcut
{
struct Shortcut {
VDFIdx<0x00> idx;
VDFKeyPair<k_appid, uint32_t, VDFTypeId::Number> appId{0x000000};
VDFKeyPair<k_appname, std::string, VDFTypeId::String> appName{""};
@ -237,8 +228,7 @@ namespace VDFParser
}
};
struct VDFFile
{
struct VDFFile {
VDFFile(){};
VDFFile(const VDFFile& other)
{
@ -264,8 +254,7 @@ namespace VDFParser
}
};
class Parser
{
class Parser {
private:
static inline std::ifstream ifile;
static inline std::ofstream ofile;
@ -273,8 +262,7 @@ namespace VDFParser
template <typename typ, typename size>
static inline auto readVDFBuffer(typ& buff, size sz)
{
if (ifile.eof())
{
if (ifile.eof()) {
return;
}
@ -293,10 +281,8 @@ namespace VDFParser
{
std::string str;
char ch = '\x0';
do
{
if (ifile.eof())
{
do {
if (ifile.eof()) {
return str;
}
ifile.read(&ch, sizeof(char));
@ -319,169 +305,140 @@ namespace VDFParser
VDFFile vdffile;
ifile.open(path.string(), std::ios::binary | std::ios::in);
if (!ifile.is_open())
{
if (!ifile.is_open()) {
return {};
}
auto firsty = readVDFValue<uint8_t>();
if (vdffile.first_byte != firsty)
{
if (vdffile.first_byte != firsty) {
// TODO: invalid
ifile.close();
return vdffile;
}
auto headername = readVDFString();
if (vdffile.identifier != headername)
{
if (vdffile.identifier != headername) {
// TODO: invalid
ifile.close();
return vdffile;
}
char buff[3];
while(true)
{
if (ifile.eof())
{
while (true) {
if (ifile.eof()) {
break;
}
readVDFBuffer(buff, 3); // 2 bytes idx, 0x00 delmiter
if (buff[0] == 0x08 && buff[1] == 0x08)
{
if (buff[0] == 0x08 && buff[1] == 0x08) {
break;
}
Shortcut shortcut;
shortcut.idx.data[0] = buff[0]; shortcut.idx.data[1] = buff[1];
shortcut.idx.data[0] = buff[0];
shortcut.idx.data[1] = buff[1];
while (true) // TODO;
{
if (ifile.eof())
{
if (ifile.eof()) {
break;
}
VDFTypeId tid = static_cast<VDFTypeId>(readVDFValue<uint8_t>());
if (tid == 0x08)
{
if (tid == 0x08) {
auto nextbyte = readVDFValue<uint8_t>();
if (nextbyte == 0x08)
{
if (nextbyte == 0x08) {
break;
} else {
}
else {
// WTF?!
// TODO:
throw std::exception("WTF");
}
}
auto key = readVDFString();
if ((tid == 0x08 && key[0] == 0x08) || key == "\x08\x08")
{
if ((tid == 0x08 && key[0] == 0x08) || key == "\x08\x08") {
break;
}
if (key == shortcut.appId.key)
{
if (key == shortcut.appId.key) {
shortcut.appId.value = readVDFValue<uint32_t>();
continue;
}
if (key == shortcut.appName.key)
{
if (key == shortcut.appName.key) {
shortcut.appName.value = readVDFString();
continue;
}
if (key == shortcut.exe.key)
{
if (key == shortcut.exe.key) {
shortcut.exe.value = readVDFString();
continue;
}
if (key == shortcut.StartDir.key)
{
if (key == shortcut.StartDir.key) {
shortcut.StartDir.value = readVDFString();
continue;
}
if (key == shortcut.icon.key)
{
if (key == shortcut.icon.key) {
shortcut.icon.value = readVDFString();
continue;
}
if (key == shortcut.ShortcutPath.key)
{
if (key == shortcut.ShortcutPath.key) {
shortcut.ShortcutPath.value = readVDFString();
continue;
}
if (key == shortcut.LaunchOptions.key)
{
if (key == shortcut.LaunchOptions.key) {
shortcut.LaunchOptions.value = readVDFString();
continue;
}
if (key == shortcut.IsHidden.key)
{
if (key == shortcut.IsHidden.key) {
shortcut.IsHidden.value = readVDFValue<uint32_t>();
continue;
}
if (key == shortcut.AllowDesktopConfig.key)
{
if (key == shortcut.AllowDesktopConfig.key) {
shortcut.AllowDesktopConfig.value = readVDFValue<uint32_t>();
continue;
}
if (key == shortcut.AllowOverlay.key)
{
if (key == shortcut.AllowOverlay.key) {
shortcut.AllowOverlay.value = readVDFValue<uint32_t>();
continue;
}
if (key == shortcut.openvr.key)
{
if (key == shortcut.openvr.key) {
shortcut.openvr.value = readVDFValue<uint32_t>();
continue;
}
if (key == shortcut.Devkit.key)
{
if (key == shortcut.Devkit.key) {
shortcut.Devkit.value = readVDFValue<uint32_t>();
continue;
}
if (key == shortcut.DevkitGameID.key)
{
if (key == shortcut.DevkitGameID.key) {
shortcut.DevkitGameID.value = readVDFString();
continue;
}
if (key == shortcut.DevkitOverrideAppID.key)
{
if (key == shortcut.DevkitOverrideAppID.key) {
shortcut.DevkitOverrideAppID.value = readVDFValue<uint32_t>();
continue;
}
if (key == shortcut.LastPlayTime.key)
{
if (key == shortcut.LastPlayTime.key) {
shortcut.LastPlayTime.value = readVDFValue<uint32_t>();
continue;
}
if (key == shortcut.tags.key)
{
if (key == shortcut.tags.key) {
// TODO: read tags
ShortcutTag tag;
while (true)
{
if (ifile.eof())
{
while (true) {
if (ifile.eof()) {
break;
}
char tbuff[2];
readVDFBuffer(tbuff, 2); // 2 bytes idx
if (tbuff[0] == 0x08 && tbuff[1] == 0x08)
{
if (tbuff[0] == 0x08 && tbuff[1] == 0x08) {
ifile.seekg(-2, std::ios_base::cur);
break;
}
tag.idx.data[0] = tbuff[0]; tag.idx.data[1] = tbuff[1];
tag.idx.data[0] = tbuff[0];
tag.idx.data[1] = tbuff[1];
ifile.seekg(1, std::ios_base::cur);
tag.value = readVDFString();
shortcut.tags.value.push_back(tag);
}
continue;
}
}
if (!(shortcut.idx.data[0] == 0x0 && shortcut.idx.data[1] == 0x0))
{
if (!(shortcut.idx.data[0] == 0x0 && shortcut.idx.data[1] == 0x0)) {
vdffile.shortcuts.push_back(shortcut);
}
}
@ -494,15 +451,13 @@ namespace VDFParser
static inline bool writeShortcuts(std::filesystem::path path, const VDFFile& vdffile)
{
ofile.open(path.string(), std::ios::binary | std::ios::out);
if (!ofile.is_open())
{
if (!ofile.is_open()) {
return false;
}
ofile.write((char*)&vdffile.first_byte, 1);
ofile.write(vdffile.identifier.data(), vdffile.identifier.length());
ofile.write("\x00", 1);
for (auto& shortcut : vdffile.shortcuts)
{
for (auto& shortcut : vdffile.shortcuts) {
ofile.write(shortcut.idx.data, 2);
ofile.write("\x00", 1);
//
@ -587,12 +542,10 @@ namespace VDFParser
ofile.write(shortcut.LastPlayTime.key, 13);
ofile.write((char*)&shortcut.LastPlayTime.value, 4);
//
ofile.write((char*)&shortcut.tags.type_id, 1);
ofile.write(shortcut.tags.key, 5);
for (auto& tag : shortcut.tags.value)
{
for (auto& tag : shortcut.tags.value) {
ofile.write(tag.idx.data, 2);
ofile.write("\x00", 1);
ofile.write(tag.value.data(), tag.value.length());
@ -604,6 +557,5 @@ namespace VDFParser
ofile.close();
return true;
}
};
}
} // namespace VDFParser

@ -19,8 +19,7 @@ limitations under the License.
#define NOMINMAX
#include <Windows.h>
class WinEventFilter : public QAbstractNativeEventFilter
{
class WinEventFilter : public QAbstractNativeEventFilter {
public:
WinEventFilter() = default;
@ -37,8 +36,7 @@ public:
auto msg = static_cast<MSG*>(message)->message;
auto lParam = static_cast<MSG*>(message)->lParam;
auto hwnd = static_cast<MSG*>(message)->hwnd;
if (msg == WM_NCCALCSIZE)
{
if (msg == WM_NCCALCSIZE) {
auto sz = reinterpret_cast<LPNCCALCSIZE_PARAMS>(lParam);
sz->rgrc[0].top -= 6;
}

@ -19,9 +19,9 @@ limitations under the License.
#include <QWindow>
#ifdef _WIN32
#include <VersionHelpers.h>
#include <Windows.h>
#include <dwmapi.h>
#include <VersionHelpers.h>
#pragma comment(lib, "Dwmapi.lib")
#endif
@ -30,8 +30,7 @@ limitations under the License.
#ifdef _WIN32
// Some undocument stuff to enable aero on win10 or higher...
enum AccentState
{
enum AccentState {
ACCENT_DISABLED = 0,
ACCENT_ENABLE_GRADIENT = 1,
ACCENT_ENABLE_TRANSPARENTGRADIENT = 2,
@ -40,8 +39,7 @@ enum AccentState
ACCENT_ENABLE_HOSTBACKDROP = 5, // RS5 1809
ACCENT_INVALID_STATE = 6
};
struct AccentPolicy
{
struct AccentPolicy {
AccentState AccentState;
int AccentFlags;
@ -49,14 +47,12 @@ struct AccentPolicy
int AnimationId;
};
enum WindowCompositionAttribute
{
enum WindowCompositionAttribute {
// ...
WCA_ACCENT_POLICY = 19
// ...
};
struct WindowCompositionAttributeData
{
struct WindowCompositionAttributeData {
WindowCompositionAttribute Attribute;
void* Data;
int SizeOfData;
@ -66,7 +62,6 @@ typedef HRESULT(__stdcall* PSetWindowCompositionAttribute)(HWND hwnd, WindowComp
#endif
int main(int argc, char* argv[])
{
#if defined(Q_OS_WIN)
@ -98,16 +93,18 @@ int main(int argc, char* argv[])
// Enable blurbehind (not needed?) anyway gives nice background on win7 and 8
DWM_BLURBEHIND bb{};
bb.dwFlags = DWM_BB_ENABLE; bb.fEnable = true; bb.hRgnBlur = nullptr;
bb.dwFlags = DWM_BB_ENABLE;
bb.fEnable = true;
bb.hRgnBlur = nullptr;
DwmEnableBlurBehindWindow(hwnd, &bb);
if (IsWindows10OrGreater())
{
if (IsWindows10OrGreater()) {
// undoc stuff for aero >= Win10
int color = (0 << 24) + (0x21 << 16) + (0x11 << 8) + (0x11);
AccentPolicy accPol{};
accPol.AccentState = ACCENT_ENABLE_ACRYLICBLURBEHIND;
accPol.AccentFlags = 2; accPol.GradientColor = color;
accPol.AccentFlags = 2;
accPol.GradientColor = color;
accPol.AnimationId = 0;
WindowCompositionAttributeData data{};
data.Attribute = WindowCompositionAttribute::WCA_ACCENT_POLICY;
@ -115,14 +112,10 @@ int main(int argc, char* argv[])
data.SizeOfData = sizeof(accPol);
auto user32dll = GetModuleHandle(L"user32.dll");
if (user32dll) {
PSetWindowCompositionAttribute SetWindowCompositionAttribute = (
reinterpret_cast<PSetWindowCompositionAttribute>(GetProcAddress(user32dll, "SetWindowCompositionAttribute"))
);
if (SetWindowCompositionAttribute)
{
PSetWindowCompositionAttribute SetWindowCompositionAttribute = (reinterpret_cast<PSetWindowCompositionAttribute>(GetProcAddress(user32dll, "SetWindowCompositionAttribute")));
if (SetWindowCompositionAttribute) {
auto res = SetWindowCompositionAttribute(hwnd, &data);
if (SUCCEEDED(res))
{
if (SUCCEEDED(res)) {
uimodel.setAcrylicEffect(true);
}
}

@ -10,5 +10,6 @@
<file>svg/edit_white_24dp.svg</file>
<file>svg/steam.svg</file>
<file>qml/UWPSelectDialog.qml</file>
<file>qml/InfoDialog.qml</file>
</qresource>
</RCC>

@ -0,0 +1,98 @@
/*
Copyright 2021 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
Dialog {
id: dlg
anchors.centerIn: parent
property var confirmedParam: null
signal confirmed(var param)
visible: false
modal: true
dim: true
parent: Overlay.overlay
Overlay.modal: Rectangle {
color: Qt.rgba(0,0,0,0.4)
opacity: backdropOpacity
Behavior on opacity {
NumberAnimation {
duration: 300
}
}
}
property real backdropOpacity: 1.0
property alias titleText: title.text
property alias text: text.text
enter: Transition {
NumberAnimation{target: content; property: "y"; from: parent.height; to: 16; duration: 300; easing.type: Easing.OutQuad }
NumberAnimation{target: background; property: "y"; from: parent.height; to: 0; duration: 300; easing.type: Easing.OutQuad }
NumberAnimation{target: dlg; property: "backdropOpacity"; from: 0; to: 1; duration: 300; easing.type: Easing.OutQuad }
}
exit: Transition {
NumberAnimation{target: content; property: "y"; from: 16; to: parent.height; duration: 300; easing.type: Easing.InQuad }
NumberAnimation{target: background; property: "y"; from: 0; to: parent.height; duration: 300; easing.type: Easing.InQuad }
NumberAnimation{target: dlg; property: "backdropOpacity"; from: 1; to: 0; duration: 300; easing.type: Easing.InQuad }
}
background: RPane {
id: background
radius: 4
Material.elevation: 64
bgOpacity: 0.97
}
contentItem: Item {
id: content
readonly property real spacing: 16
implicitWidth: Math.max(row.width, col.width)
implicitHeight: title.height + row.height + spacing + col.height
Label {
id: title
anchors.top: parent.top
anchors.left: parent.left
text: dlg.title
font.bold: true
}
Column {
id: col
anchors.top: title.bottom
anchors.topMargin: parent.spacing
spacing: 16
Label {
id: text
}
}
Row {
id: row
anchors.top: col.bottom
anchors.topMargin: parent.spacing
spacing: 16
Button {
text: qsTr("OK")
onClicked: function(){
close()
confirmed(confirmedParam)
}
}
anchors.right: parent.right
}
}
}

@ -17,6 +17,7 @@ import QtQuick 6.2
import QtQuick.Layouts 6.2
import QtQuick.Controls 6.2
import QtQuick.Controls.Material 6.2
import QtQuick.Dialogs 6.2
import Qt5Compat.GraphicalEffects
GridView {
@ -64,6 +65,12 @@ GridView {
NumberAnimation { properties: "x,y"; duration: 300; easing.type: Easing.InQuad }
}
InfoDialog {
id: writeErrorDialog
titleText: qsTr("Error")
text: qsTr("Error writing shortcuts.vdf...")
}
delegate: RPane {
id: delegateRoot
color: Qt.lighter(Material.background, 1.6)
@ -90,7 +97,7 @@ GridView {
anchors.left: parent.left
anchors.bottom: buttonrow.top
anchors.margins: 12
spacing: 4
spacing: 8
Row {
spacing: 8
visible: modelData.launchPath && modelData.launchPath.length > 0
@ -112,6 +119,37 @@ GridView {
elide: Text.ElideRight
}
}
Row {
visible: false // TODO: dunno about this...
spacing: 4
Label {
text: qsTr("Is in")
font.bold: true
}
Image {
anchors.verticalCenter: parent.verticalCenter
source: "qrc:/svg/steam.svg"
width: 16
height: 16
smooth: true
mipmap: true
ColorOverlay {
anchors.fill: parent
source: parent
color: "white"
}
}
Item {
width: 4
height: 1
}
Label {
anchors.verticalCenter: parent.verticalCenter
text: delegateRoot.isInSteam ? qsTr("Yes") : qsTr("No")
width: 292 - typeLabel.width - 72
elide: Text.ElideRight
}
}
}
Image {
@ -134,19 +172,20 @@ GridView {
onClicked: function(){
if (delegateRoot.isInSteam) {
if (!uiModel.removeFromSteam(modelData.name)) {
// TODO: show error
writeErrorDialog.open();
return;
}
} else {
if (!uiModel.addToSteam(modelData)) {
// TODO: show error
writeErrorDialog.open();
return;
}
}
delegateRoot.isInSteam = uiModel.isInSteam(modelData)
steamShortcutsChanged = true
}
highlighted: delegateRoot.isInSteam
Material.accent: Material.color(Material.Red, Material.Shade400)
Material.accent: Material.color(Material.Red, Material.Shade800)
Row {
anchors.centerIn: parent
spacing: 8

@ -43,6 +43,17 @@ Window {
return Qt.rgba(color.r, color.g, color.b, alpha);
}
property bool steamShortcutsChanged: false
InfoDialog {
id: steamChangedDialog
titleText: qsTr("Attention!")
text: qsTr("Please restart Steam to reload your changes!")
onConfirmed: function (callback) {
callback();
}
}
Rectangle {
id: titleBar
visible: uiModel.isWindows
@ -83,7 +94,14 @@ Window {
ToolButton {
id: closbtn
text: "🗙"
onClicked: window.close()
onClicked: steamShortcutsChanged
? (function(){
steamChangedDialog.confirmedParam = () => {
window.close()
}
steamChangedDialog.open()
})()
: window.close()
background: Rectangle {
implicitWidth: 32
implicitHeight: 32
@ -99,7 +117,6 @@ Window {
}
}
}
}
Item {
@ -229,6 +246,5 @@ Window {
}
}
}
}
}

@ -20,12 +20,11 @@ limitations under the License.
#include "SteamTarget.h"
#include "OverlayLogSink.h"
#include "Settings.h"
#include <spdlog/sinks/basic_file_sink.h>
#include <spdlog/sinks/stdout_color_sinks.h>
#include <spdlog/spdlog.h>
#include "OverlayLogSink.h"
#include "Settings.h"
#ifdef _WIN32
#ifdef CONSOLE

Loading…
Cancel
Save