diff --git a/GlosSIConfig/.clang-format b/GlosSIConfig/.clang-format new file mode 100644 index 0000000..dc813fd --- /dev/null +++ b/GlosSIConfig/.clang-format @@ -0,0 +1,7 @@ +UseTab: false +IndentWidth: 4 +BreakBeforeBraces: "Stroustrup" +AllowShortIfStatementsOnASingleLine: false +IndentCaseLabels: false +ColumnLimit: 0 +PointerAlignment: "Left" \ No newline at end of file diff --git a/GlosSIConfig/GlosSIConfig.vcxproj b/GlosSIConfig/GlosSIConfig.vcxproj index 8ca3a14..afb441c 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj +++ b/GlosSIConfig/GlosSIConfig.vcxproj @@ -123,8 +123,10 @@ + + @@ -136,6 +138,7 @@ + diff --git a/GlosSIConfig/GlosSIConfig.vcxproj.filters b/GlosSIConfig/GlosSIConfig.vcxproj.filters index 78d2624..8162392 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj.filters +++ b/GlosSIConfig/GlosSIConfig.vcxproj.filters @@ -51,12 +51,16 @@ qml - - Source Files - qml + + qml + + + qml + + @@ -70,6 +74,9 @@ Header Files + + Header Files + diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index 7188cfb..0e111aa 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -16,35 +16,21 @@ limitations under the License. #include "UIModel.h" #include -#include - -#ifdef _WIN32 -#include -#include -#include -#include -#include -#include -#include -#include -#pragma comment(lib, "Shlwapi.lib") -using namespace Windows::Management::Deployment; -using namespace Windows::Foundation::Collections; - #include +#include -#ifdef _WIN32 #include -#endif +#ifdef _WIN32 +#include "UWPFetch.h" #endif UIModel::UIModel() : QObject(nullptr) { auto path = std::filesystem::temp_directory_path() - .parent_path() - .parent_path() - .parent_path(); + .parent_path() + .parent_path() + .parent_path(); path /= "Roaming"; path /= "GlosSI"; @@ -59,7 +45,6 @@ UIModel::UIModel() : QObject(nullptr) parseShortcutVDF(); readConfigs(); - } void UIModel::readConfigs() @@ -68,41 +53,38 @@ void UIModel::readConfigs() auto entries = dir.entryList(QDir::Files, QDir::SortFlag::Name); entries.removeIf([](const auto& entry) { return !entry.endsWith(".json"); - }); - - 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)) - { - // meh - return; - } - const auto data = file.readAll(); - file.close(); - const auto jsondoc = QJsonDocument::fromJson(data); - const auto filejson = jsondoc.object(); - - QJsonObject json; - json["version"] = filejson["version"]; - json["icon"] = filejson["icon"]; - json["launch"] = filejson["launch"]["launch"]; - json["launchPath"] = filejson["launch"]["launchPath"]; - json["launchAppArgs"] = filejson["launch"]["launchAppArgs"]; - json["closeOnExit"] = filejson["launch"]["closeOnExit"]; - json["hideDevices"] = filejson["devices"]["hideDevices"]; - json["windowMode"] = filejson["window"]["windowMode"]; - json["maxFps"] = filejson["window"]["maxFps"]; - json["scale"] = filejson["window"]["scale"]; - - json["name"] = QString(name).replace(QRegularExpression("\.json"), ""); - - targets_.append(json.toVariantMap()); - }); - + }); + + 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)) { + // meh + return; + } + const auto data = file.readAll(); + file.close(); + const auto jsondoc = QJsonDocument::fromJson(data); + const auto filejson = jsondoc.object(); + + QJsonObject json; + json["version"] = filejson["version"]; + json["icon"] = filejson["icon"]; + json["launch"] = filejson["launch"]["launch"]; + json["launchPath"] = filejson["launch"]["launchPath"]; + json["launchAppArgs"] = filejson["launch"]["launchAppArgs"]; + json["closeOnExit"] = filejson["launch"]["closeOnExit"]; + json["hideDevices"] = filejson["devices"]["hideDevices"]; + json["windowMode"] = filejson["window"]["windowMode"]; + json["maxFps"] = filejson["window"]["maxFps"]; + json["scale"] = filejson["window"]["scale"]; + + json["name"] = QString(name).replace(QRegularExpression("\.json"), ""); + + 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() - ? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"") - : ("\"" + appDir.absolutePath() + "\"").toStdString() - ); + vdfshortcut.StartDir.value = (launch && !maybeLaunchPath.isEmpty() + ? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"") + : ("\"" + 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) - { - return shortcut.appName.value == name.toStdString(); - }), scuts.end()); - for (int i = 0; i < scuts.size(); i++) - { - if (scuts[i].idx != i) - { + 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[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 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^ 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 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; } @@ -527,7 +275,7 @@ std::filesystem::path UIModel::getSteamPath() const #ifdef _WIN32 // TODO: check if keys/value exist // steam should always be open and have written reg values... - winreg::RegKey key{ HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam" }; + winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam"}; const auto res = key.GetStringValue(L"SteamPath"); return res; #else @@ -540,7 +288,7 @@ std::wstring UIModel::getSteamUserId() const #ifdef _WIN32 // TODO: check if keys/value exist // steam should always be open and have written reg values... - winreg::RegKey key{ HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess" }; + winreg::RegKey key{HKEY_CURRENT_USER, L"SOFTWARE\\Valve\\Steam\\ActiveProcess"}; const auto res = std::to_wstring(key.GetDwordValue(L"ActiveUser")); return res; #else diff --git a/GlosSIConfig/UIModel.h b/GlosSIConfig/UIModel.h index 19d09db..f706d45 100644 --- a/GlosSIConfig/UIModel.h +++ b/GlosSIConfig/UIModel.h @@ -14,23 +14,21 @@ See the License for the specific language governing permissions and limitations under the License. */ #pragma once -#include +#include "VDFParser.h" +#include #include #include -#include -#include "VDFParser.h" - +#include -class UIModel : public QObject -{ +class UIModel : public QObject { Q_OBJECT - Q_PROPERTY(bool isWindows READ getIsWindows CONSTANT) - Q_PROPERTY(bool hasAcrlyicEffect READ hasAcrylicEffect NOTIFY acrylicChanged) - Q_PROPERTY(QVariantList targetList READ getTargetList NOTIFY targetListChanged) - Q_PROPERTY(QVariantList uwpList READ uwpApps CONSTANT) + Q_PROPERTY(bool isWindows READ getIsWindows CONSTANT) + Q_PROPERTY(bool hasAcrlyicEffect READ hasAcrylicEffect NOTIFY acrylicChanged) + Q_PROPERTY(QVariantList targetList READ getTargetList NOTIFY targetListChanged) + Q_PROPERTY(QVariantList uwpList READ uwpApps CONSTANT) -public: + public: UIModel(); Q_INVOKABLE void readConfigs(); @@ -49,17 +47,16 @@ public: [[nodiscard]] bool hasAcrylicEffect() const; void setAcrylicEffect(bool has_acrylic_affect); -signals: + signals: void acrylicChanged(); void targetListChanged(); -private: + private: std::filesystem::path config_path_; QString config_dir_name_; void writeTarget(const QJsonObject& json, const QString& name); - std::filesystem::path getSteamPath() const; std::wstring getSteamUserId() const; void parseShortcutVDF(); diff --git a/GlosSIConfig/UWPFetch.h b/GlosSIConfig/UWPFetch.h new file mode 100644 index 0000000..330948a --- /dev/null +++ b/GlosSIConfig/UWPFetch.h @@ -0,0 +1,237 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 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 ^ 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 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 \ No newline at end of file diff --git a/GlosSIConfig/VDFParser.h b/GlosSIConfig/VDFParser.h index 4300611..ec16169 100644 --- a/GlosSIConfig/VDFParser.h +++ b/GlosSIConfig/VDFParser.h @@ -17,593 +17,545 @@ limitations under the License. // "Shitty shortcuts.vdf Parser"� #pragma once +#include #include +#include +#include #include #include -#include -#include -#include -namespace VDFParser +namespace VDFParser { +namespace crc { +template +uint32_t calculate_crc(CONT container) { - namespace crc - { - template - uint32_t calculate_crc(CONT container) - { - uint32_t crc32_table[256]; - for (uint32_t i = 0; i < 256; i++) { - uint32_t ch = i; - uint32_t crc = 0; - for (size_t j = 0; j < 8; j++) { - uint32_t b = (ch ^ crc) & 1; - crc >>= 1; - if (b) crc = crc ^ 0xEDB88320; - ch >>= 1; - } - crc32_table[i] = crc; - } - uint32_t crc = 0xFFFFFFFF; - for (size_t i = 0; i < container.size(); i++) { - char ch = container.data()[i]; - uint32_t t = (ch ^ crc) & 0xFF; - crc = (crc >> 8) ^ crc32_table[t]; - } - return ~crc; + uint32_t crc32_table[256]; + for (uint32_t i = 0; i < 256; i++) { + uint32_t ch = i; + uint32_t crc = 0; + for (size_t j = 0; j < 8; j++) { + uint32_t b = (ch ^ crc) & 1; + crc >>= 1; + if (b) + crc = crc ^ 0xEDB88320; + ch >>= 1; } + crc32_table[i] = crc; } - - static constexpr const char k_appid[] = { "appid" }; - static constexpr const char k_appname[] = { "appname" }; - static constexpr const char k_exe[] = { "exe" }; - static constexpr const char k_StartDir[] = { "StartDir" }; - static constexpr const char k_icon[] = { "icon" }; - static constexpr const char k_ShortcutPath[] = { "ShortcutPath" }; - static constexpr const char k_LaunchOptions[] = { "LaunchOptions" }; - static constexpr const char k_IsHidden[] = { "IsHidden" }; - static constexpr const char k_AllowDesktopConfig[] = { "AllowDesktopConfig" }; - static constexpr const char k_AllowOverlay[] = { "AllowOverlay" }; - static constexpr const char k_openvr[] = { "openvr" }; - static constexpr const char k_Devkit[] = { "Devkit" }; - static constexpr const char k_DevkitGameID[] = { "DevkitGameID" }; - static constexpr const char k_DevkitOverrideAppID[] = { "DevkitOverrideAppID" }; - static constexpr const char k_LastPlayTime[] = { "LastPlayTime" }; - static constexpr const char k_tags[] = { "tags" }; - - enum VDFTypeId + uint32_t crc = 0xFFFFFFFF; + for (size_t i = 0; i < container.size(); i++) { + char ch = container.data()[i]; + uint32_t t = (ch ^ crc) & 0xFF; + crc = (crc >> 8) ^ crc32_table[t]; + } + return ~crc; +} +} // namespace crc + +static constexpr const char k_appid[] = {"appid"}; +static constexpr const char k_appname[] = {"appname"}; +static constexpr const char k_exe[] = {"exe"}; +static constexpr const char k_StartDir[] = {"StartDir"}; +static constexpr const char k_icon[] = {"icon"}; +static constexpr const char k_ShortcutPath[] = {"ShortcutPath"}; +static constexpr const char k_LaunchOptions[] = {"LaunchOptions"}; +static constexpr const char k_IsHidden[] = {"IsHidden"}; +static constexpr const char k_AllowDesktopConfig[] = {"AllowDesktopConfig"}; +static constexpr const char k_AllowOverlay[] = {"AllowOverlay"}; +static constexpr const char k_openvr[] = {"openvr"}; +static constexpr const char k_Devkit[] = {"Devkit"}; +static constexpr const char k_DevkitGameID[] = {"DevkitGameID"}; +static constexpr const char k_DevkitOverrideAppID[] = {"DevkitOverrideAppID"}; +static constexpr const char k_LastPlayTime[] = {"LastPlayTime"}; +static constexpr const char k_tags[] = {"tags"}; + +enum VDFTypeId { + StringList = 0, + String, + Number, +}; + +template +struct VDFKeyPair { + VDFKeyPair() {} + explicit VDFKeyPair(type _value) : value(_value) {} + static constexpr uint8_t _TID = _type_id; + static constexpr const char* const _KEY = keyname; + const uint8_t type_id = _TID; + const char* const key = _KEY; + type value; + VDFKeyPair(const VDFKeyPair& other) { - StringList = 0, - String, - Number, + value = other.value; }; - - template - struct VDFKeyPair + VDFKeyPair(VDFKeyPair&& other) { - VDFKeyPair() {} - explicit VDFKeyPair(type _value) : value(_value) {} - static constexpr uint8_t _TID = _type_id; - static constexpr const char* const _KEY = keyname; - const uint8_t type_id = _TID; - const char* const key = _KEY; - type value; - VDFKeyPair(const VDFKeyPair& other) - { - value = other.value; - }; - VDFKeyPair(VDFKeyPair&& other) - { - value = std::move(other.value); - }; - VDFKeyPair& operator=(const VDFKeyPair& other) - { - value = other.value; - return *this; - } - VDFKeyPair& operator=(VDFKeyPair&& other) - { - value = std::move(other.value); - return *this; - } + value = std::move(other.value); }; + VDFKeyPair& operator=(const VDFKeyPair& other) + { + value = other.value; + return *this; + } + VDFKeyPair& operator=(VDFKeyPair&& other) + { + value = std::move(other.value); + return *this; + } +}; - template - struct VDFIdx +template +struct VDFIdx { + VDFIdx(){}; + VDFIdx(const VDFIdx& other) { - VDFIdx() {}; - VDFIdx(const VDFIdx& other) - { - data[0] = other.data[0]; - data[1] = other.data[1]; - }; - VDFIdx(VDFIdx&& other) - { - data[0] = std::move(other.data[0]); - data[1] = std::move(other.data[1]); - }; - VDFIdx(int idx) - { - if (idx > 99) - { - data[0] = initialByte; - data[1] = 0; - } - else if (idx < 10) - { - data[1] = std::to_string(idx).c_str()[0]; - } - else - { - auto meh = std::to_string(idx).c_str(); - data[0] = meh[0] + initialByte; - data[1] = meh[1]; - } + data[0] = other.data[0]; + data[1] = other.data[1]; + }; + VDFIdx(VDFIdx&& other) + { + data[0] = std::move(other.data[0]); + data[1] = std::move(other.data[1]); + }; + VDFIdx(int idx) + { + if (idx > 99) { + data[0] = initialByte; + data[1] = 0; } - char data[2] = { initialByte, 0x0 }; - operator int() const { - if (data[0] == initialByte) - { - int res = 0; - std::from_chars(&data[1], &data[1], res); - return res; - } + else if (idx < 10) { + data[1] = std::to_string(idx).c_str()[0]; + } + 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) { int res = 0; - char copy[2] = { data[0] - initialByte, data[1]}; - std::from_chars(©[0], ©[1], res); + std::from_chars(&data[1], &data[1], res); return res; } + int res = 0; + char copy[2] = {data[0] - initialByte, data[1]}; + std::from_chars(©[0], ©[1], res); + return res; + } - VDFIdx& operator=(const VDFIdx& other) - { - data[0] = other.data[0]; - data[1] = other.data[1]; - return *this; - } - VDFIdx& operator=(VDFIdx&& other) - { - data[0] = std::move(other.data[0]); - data[1] = std::move(other.data[1]); - return *this; - } - }; + VDFIdx& operator=(const VDFIdx& other) + { + data[0] = other.data[0]; + data[1] = other.data[1]; + return *this; + } + VDFIdx& operator=(VDFIdx&& other) + { + data[0] = std::move(other.data[0]); + data[1] = std::move(other.data[1]); + return *this; + } +}; - struct ShortcutTag +struct ShortcutTag { + ShortcutTag(){}; + ShortcutTag(const ShortcutTag& other) { - ShortcutTag() {}; - ShortcutTag(const ShortcutTag& other) - { - value = other.value; - }; - ShortcutTag(ShortcutTag&& other) - { - value = std::move(other.value); - }; - VDFIdx<0x01> idx; // I Hope this is how it works... See VDFIdx - std::string value; - const uint16_t end_marker = 0x0808; - - ShortcutTag& operator=(const ShortcutTag& other) - { - value = other.value; - return *this; - } - ShortcutTag& operator=(ShortcutTag&& other) - { - value = std::move(other.value); - return *this; - } + value = other.value; + }; + ShortcutTag(ShortcutTag&& other) + { + value = std::move(other.value); }; + VDFIdx<0x01> idx; // I Hope this is how it works... See VDFIdx + std::string value; + const uint16_t end_marker = 0x0808; - struct Shortcut + ShortcutTag& operator=(const ShortcutTag& other) { - VDFIdx<0x00> idx; - VDFKeyPair appId{ 0x000000 }; - VDFKeyPair appName{ "" }; - VDFKeyPair exe{ "\"\"" }; // Qouted - VDFKeyPair StartDir{ "\"\"" }; // Qouted - VDFKeyPair icon{ "" }; // Qouted or empty - VDFKeyPair ShortcutPath{ "" }; // Qouted or empty? - VDFKeyPair LaunchOptions{ "" }; // UNQOUTED or empty - VDFKeyPair IsHidden{ 0 }; - VDFKeyPair AllowDesktopConfig{ 1 }; - VDFKeyPair AllowOverlay{ 1 }; - VDFKeyPair openvr{ 0 }; - VDFKeyPair Devkit{ 0 }; - VDFKeyPair DevkitGameID{ "" }; - VDFKeyPair DevkitOverrideAppID{ 0 }; // - VDFKeyPair LastPlayTime{ 0 }; // - VDFKeyPair, VDFTypeId::StringList> tags{ }; - Shortcut& operator=(const Shortcut& other) - { - idx = other.idx; - appId = other.appId; - appName = other.appName; - exe = other.exe; - StartDir = other.StartDir; - icon = other.icon; - ShortcutPath = other.ShortcutPath; - LaunchOptions = other.LaunchOptions; - LaunchOptions = other.LaunchOptions; - IsHidden = other.IsHidden; - AllowDesktopConfig = other.AllowDesktopConfig; - AllowOverlay = other.AllowOverlay; - openvr = other.openvr; - Devkit = other.Devkit; - DevkitGameID = other.DevkitGameID; - DevkitOverrideAppID = other.DevkitOverrideAppID; - LastPlayTime = other.LastPlayTime; - tags = other.tags; - return *this; - } + value = other.value; + return *this; + } + ShortcutTag& operator=(ShortcutTag&& other) + { + value = std::move(other.value); + return *this; + } +}; + +struct Shortcut { + VDFIdx<0x00> idx; + VDFKeyPair appId{0x000000}; + VDFKeyPair appName{""}; + VDFKeyPair exe{"\"\""}; // Qouted + VDFKeyPair StartDir{"\"\""}; // Qouted + VDFKeyPair icon{""}; // Qouted or empty + VDFKeyPair ShortcutPath{""}; // Qouted or empty? + VDFKeyPair LaunchOptions{""}; // UNQOUTED or empty + VDFKeyPair IsHidden{0}; + VDFKeyPair AllowDesktopConfig{1}; + VDFKeyPair AllowOverlay{1}; + VDFKeyPair openvr{0}; + VDFKeyPair Devkit{0}; + VDFKeyPair DevkitGameID{""}; + VDFKeyPair DevkitOverrideAppID{0}; // + VDFKeyPair LastPlayTime{0}; // + VDFKeyPair, VDFTypeId::StringList> tags{}; + Shortcut& operator=(const Shortcut& other) + { + idx = other.idx; + appId = other.appId; + appName = other.appName; + exe = other.exe; + StartDir = other.StartDir; + icon = other.icon; + ShortcutPath = other.ShortcutPath; + LaunchOptions = other.LaunchOptions; + LaunchOptions = other.LaunchOptions; + IsHidden = other.IsHidden; + AllowDesktopConfig = other.AllowDesktopConfig; + AllowOverlay = other.AllowOverlay; + openvr = other.openvr; + Devkit = other.Devkit; + DevkitGameID = other.DevkitGameID; + DevkitOverrideAppID = other.DevkitOverrideAppID; + LastPlayTime = other.LastPlayTime; + tags = other.tags; + return *this; + } +}; + +struct VDFFile { + VDFFile(){}; + VDFFile(const VDFFile& other) + { + shortcuts = other.shortcuts; + }; + VDFFile(VDFFile&& other) + { + shortcuts = std::move(other.shortcuts); }; + const uint8_t first_byte = 0x00; + const std::string identifier = "shortcuts"; + std::vector shortcuts; + const uint16_t end_marker = 0x0808; + VDFFile& operator=(const VDFFile& other) + { + shortcuts = other.shortcuts; + return *this; + } + VDFFile& operator=(VDFFile&& other) + { + shortcuts = std::move(other.shortcuts); + return *this; + } +}; + +class Parser { + private: + static inline std::ifstream ifile; + static inline std::ofstream ofile; - struct VDFFile + template + static inline auto readVDFBuffer(typ& buff, size sz) { - VDFFile(){}; - VDFFile(const VDFFile& other) - { - shortcuts = other.shortcuts; - }; - VDFFile(VDFFile&& other) - { - shortcuts = std::move(other.shortcuts); - }; - const uint8_t first_byte = 0x00; - const std::string identifier = "shortcuts"; - std::vector shortcuts; - const uint16_t end_marker = 0x0808; - VDFFile& operator=(const VDFFile& other) - { - shortcuts = other.shortcuts; - return *this; - } - VDFFile& operator=(VDFFile&& other) - { - shortcuts = std::move(other.shortcuts); - return *this; + if (ifile.eof()) { + + return; } - }; + ifile.read((char*)buff, sz); + } - class Parser + template + static inline auto readVDFValue() { - private: - static inline std::ifstream ifile; - static inline std::ofstream ofile; - - template - static inline auto readVDFBuffer(typ& buff,size sz) - { - if (ifile.eof()) - { + uint8_t buff[sizeof(typ)]; + ifile.read((char*)buff, sizeof(typ)); + return *reinterpret_cast(buff); + } - return; + static inline std::string readVDFString() + { + std::string str; + char ch = '\x0'; + do { + if (ifile.eof()) { + return str; } - ifile.read((char*)buff, sz); - } + ifile.read(&ch, sizeof(char)); + if (ch != '\x0') + str.push_back(ch); + } while (ch != '\x0'); + return str; + } - template - static inline auto readVDFValue() - { - uint8_t buff[sizeof(typ)]; - ifile.read((char*)buff, sizeof(typ)); - return *reinterpret_cast(buff); - } + public: + static inline uint32_t calculateAppId(const Shortcut& shortcut) + { + std::string buff = shortcut.exe.value + shortcut.appName.value; + auto checksum = crc::calculate_crc(buff); + return checksum | 0x80000000; + } - static inline std::string readVDFString() - { - std::string str; - char ch = '\x0'; - do - { - if (ifile.eof()) - { - return str; - } - ifile.read(&ch, sizeof(char)); - if (ch != '\x0') - str.push_back(ch); - } while (ch != '\x0'); - return str; - } + static inline VDFFile parseShortcuts(std::filesystem::path path) + { + VDFFile vdffile; - public: - static inline uint32_t calculateAppId(const Shortcut& shortcut) - { - std::string buff = shortcut.exe.value + shortcut.appName.value; - auto checksum = crc::calculate_crc(buff); - return checksum | 0x80000000; + ifile.open(path.string(), std::ios::binary | std::ios::in); + if (!ifile.is_open()) { + return {}; } - static inline VDFFile parseShortcuts(std::filesystem::path path) - { - VDFFile vdffile; + auto firsty = readVDFValue(); + if (vdffile.first_byte != firsty) { + // TODO: invalid + ifile.close(); + return vdffile; + } - ifile.open(path.string(), std::ios::binary | std::ios::in); - if (!ifile.is_open()) - { - return {}; - } + auto headername = readVDFString(); + if (vdffile.identifier != headername) { + // TODO: invalid + ifile.close(); + return vdffile; + } - auto firsty = readVDFValue(); - if (vdffile.first_byte != firsty) - { - // TODO: invalid - ifile.close(); - return vdffile; + char buff[3]; + while (true) { + if (ifile.eof()) { + break; } - - auto headername = readVDFString(); - if (vdffile.identifier != headername) - { - // TODO: invalid - ifile.close(); - return vdffile; + readVDFBuffer(buff, 3); // 2 bytes idx, 0x00 delmiter + if (buff[0] == 0x08 && buff[1] == 0x08) { + break; } - - - char buff[3]; - while(true) + Shortcut shortcut; + shortcut.idx.data[0] = buff[0]; + shortcut.idx.data[1] = buff[1]; + while (true) // TODO; { - if (ifile.eof()) - { - break; - } - readVDFBuffer(buff, 3); // 2 bytes idx, 0x00 delmiter - if (buff[0] == 0x08 && buff[1] == 0x08) - { + if (ifile.eof()) { break; } - Shortcut shortcut; - shortcut.idx.data[0] = buff[0]; shortcut.idx.data[1] = buff[1]; - while (true) // TODO; - { - if (ifile.eof()) - { + VDFTypeId tid = static_cast(readVDFValue()); + if (tid == 0x08) { + auto nextbyte = readVDFValue(); + if (nextbyte == 0x08) { break; } - VDFTypeId tid = static_cast(readVDFValue()); - if (tid == 0x08) - { - auto nextbyte = readVDFValue(); - if (nextbyte == 0x08) - { + else { + // WTF?! + // TODO: + throw std::exception("WTF"); + } + } + auto key = readVDFString(); + if ((tid == 0x08 && key[0] == 0x08) || key == "\x08\x08") { + break; + } + if (key == shortcut.appId.key) { + shortcut.appId.value = readVDFValue(); + continue; + } + if (key == shortcut.appName.key) { + shortcut.appName.value = readVDFString(); + continue; + } + if (key == shortcut.exe.key) { + shortcut.exe.value = readVDFString(); + continue; + } + if (key == shortcut.StartDir.key) { + shortcut.StartDir.value = readVDFString(); + continue; + } + if (key == shortcut.icon.key) { + shortcut.icon.value = readVDFString(); + continue; + } + if (key == shortcut.ShortcutPath.key) { + shortcut.ShortcutPath.value = readVDFString(); + continue; + } + if (key == shortcut.LaunchOptions.key) { + shortcut.LaunchOptions.value = readVDFString(); + continue; + } + if (key == shortcut.IsHidden.key) { + shortcut.IsHidden.value = readVDFValue(); + continue; + } + if (key == shortcut.AllowDesktopConfig.key) { + shortcut.AllowDesktopConfig.value = readVDFValue(); + continue; + } + if (key == shortcut.AllowOverlay.key) { + shortcut.AllowOverlay.value = readVDFValue(); + continue; + } + if (key == shortcut.openvr.key) { + shortcut.openvr.value = readVDFValue(); + continue; + } + if (key == shortcut.Devkit.key) { + shortcut.Devkit.value = readVDFValue(); + continue; + } + if (key == shortcut.DevkitGameID.key) { + shortcut.DevkitGameID.value = readVDFString(); + continue; + } + if (key == shortcut.DevkitOverrideAppID.key) { + shortcut.DevkitOverrideAppID.value = readVDFValue(); + continue; + } + if (key == shortcut.LastPlayTime.key) { + shortcut.LastPlayTime.value = readVDFValue(); + continue; + } + if (key == shortcut.tags.key) { + // TODO: read tags + ShortcutTag tag; + while (true) { + if (ifile.eof()) { break; - } else { - // WTF?! - // TODO: - throw std::exception("WTF"); } - } - auto key = readVDFString(); - if ((tid == 0x08 && key[0] == 0x08) || key == "\x08\x08") - { - break; - } - if (key == shortcut.appId.key) - { - shortcut.appId.value = readVDFValue(); - continue; - } - if (key == shortcut.appName.key) - { - shortcut.appName.value = readVDFString(); - continue; - } - if (key == shortcut.exe.key) - { - shortcut.exe.value = readVDFString(); - continue; - } - if (key == shortcut.StartDir.key) - { - shortcut.StartDir.value = readVDFString(); - continue; - } - if (key == shortcut.icon.key) - { - shortcut.icon.value = readVDFString(); - continue; - } - if (key == shortcut.ShortcutPath.key) - { - shortcut.ShortcutPath.value = readVDFString(); - continue; - } - if (key == shortcut.LaunchOptions.key) - { - shortcut.LaunchOptions.value = readVDFString(); - continue; - } - if (key == shortcut.IsHidden.key) - { - shortcut.IsHidden.value = readVDFValue(); - continue; - } - if (key == shortcut.AllowDesktopConfig.key) - { - shortcut.AllowDesktopConfig.value = readVDFValue(); - continue; - } - if (key == shortcut.AllowOverlay.key) - { - shortcut.AllowOverlay.value = readVDFValue(); - continue; - } - if (key == shortcut.openvr.key) - { - shortcut.openvr.value = readVDFValue(); - continue; - } - if (key == shortcut.Devkit.key) - { - shortcut.Devkit.value = readVDFValue(); - continue; - } - if (key == shortcut.DevkitGameID.key) - { - shortcut.DevkitGameID.value = readVDFString(); - continue; - } - if (key == shortcut.DevkitOverrideAppID.key) - { - shortcut.DevkitOverrideAppID.value = readVDFValue(); - continue; - } - if (key == shortcut.LastPlayTime.key) - { - shortcut.LastPlayTime.value = readVDFValue(); - continue; - } - if (key == shortcut.tags.key) - { - // TODO: read tags - ShortcutTag tag; - while (true) - { - if (ifile.eof()) - { - break; - } - char tbuff[2]; - readVDFBuffer(tbuff, 2); // 2 bytes idx - 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]; - ifile.seekg(1, std::ios_base::cur); - tag.value = readVDFString(); - shortcut.tags.value.push_back(tag); + char tbuff[2]; + readVDFBuffer(tbuff, 2); // 2 bytes idx + if (tbuff[0] == 0x08 && tbuff[1] == 0x08) { + ifile.seekg(-2, std::ios_base::cur); + break; } - continue; + 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); } - - } - if (!(shortcut.idx.data[0] == 0x0 && shortcut.idx.data[1] == 0x0)) - { - vdffile.shortcuts.push_back(shortcut); + continue; } } + if (!(shortcut.idx.data[0] == 0x0 && shortcut.idx.data[1] == 0x0)) { + vdffile.shortcuts.push_back(shortcut); + } + } - ifile.close(); + ifile.close(); - return vdffile; + return vdffile; + } + + 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()) { + 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) { + ofile.write(shortcut.idx.data, 2); + ofile.write("\x00", 1); + // + ofile.write((char*)&shortcut.appId.type_id, 1); + ofile.write(shortcut.appId.key, 6); + ofile.write((char*)&shortcut.appId.value, 4); + + // + ofile.write((char*)&shortcut.appName.type_id, 1); + ofile.write(shortcut.appName.key, 8); + ofile.write(shortcut.appName.value.data(), shortcut.appName.value.length()); + ofile.write("\x00", 1); - 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()) - { - return false; - } - ofile.write((char*)&vdffile.first_byte, 1); - ofile.write(vdffile.identifier.data(), vdffile.identifier.length()); + // + ofile.write((char*)&shortcut.exe.type_id, 1); + ofile.write(shortcut.exe.key, 4); + ofile.write(shortcut.exe.value.data(), shortcut.exe.value.length()); ofile.write("\x00", 1); - for (auto& shortcut : vdffile.shortcuts) - { - ofile.write(shortcut.idx.data, 2); - ofile.write("\x00", 1); - // - ofile.write((char*)&shortcut.appId.type_id, 1); - ofile.write(shortcut.appId.key, 6); - ofile.write((char*)&shortcut.appId.value, 4); - - // - ofile.write((char*)&shortcut.appName.type_id, 1); - ofile.write(shortcut.appName.key, 8); - ofile.write(shortcut.appName.value.data(), shortcut.appName.value.length()); - ofile.write("\x00", 1); - // - ofile.write((char*)&shortcut.exe.type_id, 1); - ofile.write(shortcut.exe.key, 4); - ofile.write(shortcut.exe.value.data(), shortcut.exe.value.length()); - ofile.write("\x00", 1); + // + ofile.write((char*)&shortcut.StartDir.type_id, 1); + ofile.write(shortcut.StartDir.key, 9); + ofile.write(shortcut.StartDir.value.data(), shortcut.StartDir.value.length()); + ofile.write("\x00", 1); - // - ofile.write((char*)&shortcut.StartDir.type_id, 1); - ofile.write(shortcut.StartDir.key, 9); - ofile.write(shortcut.StartDir.value.data(), shortcut.StartDir.value.length()); - ofile.write("\x00", 1); + // + ofile.write((char*)&shortcut.icon.type_id, 1); + ofile.write(shortcut.icon.key, 5); + ofile.write(shortcut.icon.value.data(), shortcut.icon.value.length()); + ofile.write("\x00", 1); - // - ofile.write((char*)&shortcut.icon.type_id, 1); - ofile.write(shortcut.icon.key, 5); - ofile.write(shortcut.icon.value.data(), shortcut.icon.value.length()); - ofile.write("\x00", 1); + // + ofile.write((char*)&shortcut.ShortcutPath.type_id, 1); + ofile.write(shortcut.ShortcutPath.key, 13); + ofile.write(shortcut.ShortcutPath.value.data(), shortcut.ShortcutPath.value.length()); + ofile.write("\x00", 1); - // - ofile.write((char*)&shortcut.ShortcutPath.type_id, 1); - ofile.write(shortcut.ShortcutPath.key, 13); - ofile.write(shortcut.ShortcutPath.value.data(), shortcut.ShortcutPath.value.length()); - ofile.write("\x00", 1); + // + ofile.write((char*)&shortcut.LaunchOptions.type_id, 1); + ofile.write(shortcut.LaunchOptions.key, 14); + ofile.write(shortcut.LaunchOptions.value.data(), shortcut.LaunchOptions.value.length()); + ofile.write("\x00", 1); - // - ofile.write((char*)&shortcut.LaunchOptions.type_id, 1); - ofile.write(shortcut.LaunchOptions.key, 14); - ofile.write(shortcut.LaunchOptions.value.data(), shortcut.LaunchOptions.value.length()); - ofile.write("\x00", 1); + // + ofile.write((char*)&shortcut.IsHidden.type_id, 1); + ofile.write(shortcut.IsHidden.key, 9); + ofile.write((char*)&shortcut.IsHidden.value, 4); + + // + ofile.write((char*)&shortcut.AllowDesktopConfig.type_id, 1); + ofile.write(shortcut.AllowDesktopConfig.key, 19); + ofile.write((char*)&shortcut.AllowDesktopConfig.value, 4); + + // + ofile.write((char*)&shortcut.AllowOverlay.type_id, 1); + ofile.write(shortcut.AllowOverlay.key, 13); + ofile.write((char*)&shortcut.AllowOverlay.value, 4); + + // + ofile.write((char*)&shortcut.openvr.type_id, 1); + ofile.write(shortcut.openvr.key, 7); + ofile.write((char*)&shortcut.openvr.value, 4); + + // + ofile.write((char*)&shortcut.Devkit.type_id, 1); + ofile.write(shortcut.Devkit.key, 7); + ofile.write((char*)&shortcut.Devkit.value, 4); + + // + ofile.write((char*)&shortcut.DevkitGameID.type_id, 1); + ofile.write(shortcut.DevkitGameID.key, 13); + ofile.write(shortcut.DevkitGameID.value.data(), shortcut.DevkitGameID.value.length()); + ofile.write("\x00", 1); - // - ofile.write((char*)&shortcut.IsHidden.type_id, 1); - ofile.write(shortcut.IsHidden.key, 9); - ofile.write((char*)&shortcut.IsHidden.value, 4); - - // - ofile.write((char*)&shortcut.AllowDesktopConfig.type_id, 1); - ofile.write(shortcut.AllowDesktopConfig.key, 19); - ofile.write((char*)&shortcut.AllowDesktopConfig.value, 4); - - // - ofile.write((char*)&shortcut.AllowOverlay.type_id, 1); - ofile.write(shortcut.AllowOverlay.key, 13); - ofile.write((char*)&shortcut.AllowOverlay.value, 4); - - // - ofile.write((char*)&shortcut.openvr.type_id, 1); - ofile.write(shortcut.openvr.key, 7); - ofile.write((char*)&shortcut.openvr.value, 4); - - // - ofile.write((char*)&shortcut.Devkit.type_id, 1); - ofile.write(shortcut.Devkit.key, 7); - ofile.write((char*)&shortcut.Devkit.value, 4); - - // - ofile.write((char*)&shortcut.DevkitGameID.type_id, 1); - ofile.write(shortcut.DevkitGameID.key, 13); - ofile.write(shortcut.DevkitGameID.value.data(), shortcut.DevkitGameID.value.length()); + // + ofile.write((char*)&shortcut.DevkitOverrideAppID.type_id, 1); + ofile.write(shortcut.DevkitOverrideAppID.key, 20); + ofile.write((char*)&shortcut.DevkitOverrideAppID.value, 4); + + // + ofile.write((char*)&shortcut.LastPlayTime.type_id, 1); + 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) { + ofile.write(tag.idx.data, 2); + ofile.write("\x00", 1); + ofile.write(tag.value.data(), tag.value.length()); ofile.write("\x00", 1); - - // - ofile.write((char*)&shortcut.DevkitOverrideAppID.type_id, 1); - ofile.write(shortcut.DevkitOverrideAppID.key, 20); - ofile.write((char*)&shortcut.DevkitOverrideAppID.value, 4); - - // - ofile.write((char*)&shortcut.LastPlayTime.type_id, 1); - 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) - { - ofile.write(tag.idx.data, 2); - ofile.write("\x00", 1); - ofile.write(tag.value.data(), tag.value.length()); - ofile.write("\x00", 1); - } - ofile.write("\x08\x08", 2); } ofile.write("\x08\x08", 2); - ofile.close(); - return true; } - - }; -} + ofile.write("\x08\x08", 2); + ofile.close(); + return true; + } +}; +} // namespace VDFParser diff --git a/GlosSIConfig/WinEventFilter.h b/GlosSIConfig/WinEventFilter.h index d5ddf44..ccd39c2 100644 --- a/GlosSIConfig/WinEventFilter.h +++ b/GlosSIConfig/WinEventFilter.h @@ -19,9 +19,8 @@ limitations under the License. #define NOMINMAX #include -class WinEventFilter : public QAbstractNativeEventFilter -{ -public: +class WinEventFilter : public QAbstractNativeEventFilter { + public: WinEventFilter() = default; /* @@ -37,8 +36,7 @@ public: auto msg = static_cast(message)->message; auto lParam = static_cast(message)->lParam; auto hwnd = static_cast(message)->hwnd; - if (msg == WM_NCCALCSIZE) - { + if (msg == WM_NCCALCSIZE) { auto sz = reinterpret_cast(lParam); sz->rgrc[0].top -= 6; } diff --git a/GlosSIConfig/main.cpp b/GlosSIConfig/main.cpp index 4f5bcb6..63ba47b 100644 --- a/GlosSIConfig/main.cpp +++ b/GlosSIConfig/main.cpp @@ -19,9 +19,9 @@ limitations under the License. #include #ifdef _WIN32 +#include #include #include -#include #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,31 +93,29 @@ 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; - data.Data = &accPol; - data.SizeOfData = sizeof(accPol); + data.Data = &accPol; + data.SizeOfData = sizeof(accPol); auto user32dll = GetModuleHandle(L"user32.dll"); if (user32dll) { - PSetWindowCompositionAttribute SetWindowCompositionAttribute = ( - reinterpret_cast(GetProcAddress(user32dll, "SetWindowCompositionAttribute")) - ); - if (SetWindowCompositionAttribute) - { + PSetWindowCompositionAttribute SetWindowCompositionAttribute = (reinterpret_cast(GetProcAddress(user32dll, "SetWindowCompositionAttribute"))); + if (SetWindowCompositionAttribute) { auto res = SetWindowCompositionAttribute(hwnd, &data); - if (SUCCEEDED(res)) - { + if (SUCCEEDED(res)) { uimodel.setAcrylicEffect(true); } } @@ -130,7 +123,7 @@ int main(int argc, char* argv[]) } // extend the frame fully into the client area => draw all outside the window frame. - MARGINS margins = { -1 }; + MARGINS margins = {-1}; DwmExtendFrameIntoClientArea(hwnd, &margins); // To Fix top window frame, install native event filter @@ -144,10 +137,10 @@ int main(int argc, char* argv[]) // Inform the application of the frame change. SetWindowPos(hwnd, - NULL, - rcClient.left, rcClient.top, - rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, - SWP_FRAMECHANGED); + NULL, + rcClient.left, rcClient.top, + rcClient.right - rcClient.left, rcClient.bottom - rcClient.top, + SWP_FRAMECHANGED); #endif diff --git a/GlosSIConfig/qml.qrc b/GlosSIConfig/qml.qrc index 3f7fdba..d0b1c8d 100644 --- a/GlosSIConfig/qml.qrc +++ b/GlosSIConfig/qml.qrc @@ -10,5 +10,6 @@ svg/edit_white_24dp.svg svg/steam.svg qml/UWPSelectDialog.qml + qml/InfoDialog.qml diff --git a/GlosSIConfig/qml/InfoDialog.qml b/GlosSIConfig/qml/InfoDialog.qml new file mode 100644 index 0000000..efd7a7a --- /dev/null +++ b/GlosSIConfig/qml/InfoDialog.qml @@ -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 + } + } + +} diff --git a/GlosSIConfig/qml/ShortcutCards.qml b/GlosSIConfig/qml/ShortcutCards.qml index 7bf4662..cefac18 100644 --- a/GlosSIConfig/qml/ShortcutCards.qml +++ b/GlosSIConfig/qml/ShortcutCards.qml @@ -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 diff --git a/GlosSIConfig/qml/main.qml b/GlosSIConfig/qml/main.qml index bc71fb1..6346df8 100644 --- a/GlosSIConfig/qml/main.qml +++ b/GlosSIConfig/qml/main.qml @@ -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 { } } } - } } diff --git a/GlosSITarget/main.cpp b/GlosSITarget/main.cpp index 73e104c..be693a3 100644 --- a/GlosSITarget/main.cpp +++ b/GlosSITarget/main.cpp @@ -20,12 +20,11 @@ limitations under the License. #include "SteamTarget.h" +#include "OverlayLogSink.h" +#include "Settings.h" #include #include #include -#include "OverlayLogSink.h" -#include "Settings.h" - #ifdef _WIN32 #ifdef CONSOLE