From d6c6ef26f50b94036acbf77d3d2b08894dbc82ad Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Mon, 5 Sep 2022 20:48:50 +0200 Subject: [PATCH 1/4] Update driver installer filenames in buildscript --- build.ps1 | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/build.ps1 b/build.ps1 index 94ec4fa..0dcaa40 100644 --- a/build.ps1 +++ b/build.ps1 @@ -20,8 +20,8 @@ Copy-Item "..\..\deps\SFML\out\Release\lib\RelWithDebInfo\sfml-graphics-2.dll" - Copy-Item "..\..\deps\SFML\out\Release\lib\RelWithDebInfo\sfml-system-2.dll" -Destination "." Copy-Item "..\..\deps\SFML\out\Release\lib\RelWithDebInfo\sfml-window-2.dll" -Destination "." Copy-Item "..\..\GlosSIConfig\GetAUMIDs.ps1" -Destination "." -Copy-Item "..\..\HidHideMSI.msi" -Destination "." -Copy-Item "..\..\ViGEmBusSetup_x64.msi" -Destination "." +Copy-Item "..\..\HidHideSetup.exe" -Destination "." +Copy-Item "..\..\ViGEmBusSetup_x64.exe" -Destination "." Copy-Item "..\..\vc_redist.x64.exe" -Destination "." Copy-Item "..\..\LICENSE" -Destination "./LICENSE" Copy-Item "..\..\QT_License" -Destination "./QT_License" From 7e9e6f8e966305f80e73ed81c69d887bc231e9bc Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Thu, 8 Sep 2022 21:40:05 +0200 Subject: [PATCH 2/4] GlosSIConfig: Set c++ version to 20 --- GlosSIConfig/GlosSIConfig.vcxproj | 5 +++-- GlosSIConfig/GlosSIConfig.vcxproj.DotSettings | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/GlosSIConfig/GlosSIConfig.vcxproj b/GlosSIConfig/GlosSIConfig.vcxproj index f98cfdc..81997ba 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj +++ b/GlosSIConfig/GlosSIConfig.vcxproj @@ -63,7 +63,7 @@ - stdcpp17 + stdcpp20 %(AdditionalUsingDirectories) /Zc:__cplusplus /Zc:twoPhase- %(AdditionalOptions) false @@ -85,7 +85,7 @@ - stdcpp17 + stdcpp20 %(AdditionalUsingDirectories) /Zc:__cplusplus /Zc:zwoPhase- /permissive- %(AdditionalOptions) false @@ -153,6 +153,7 @@ + diff --git a/GlosSIConfig/GlosSIConfig.vcxproj.DotSettings b/GlosSIConfig/GlosSIConfig.vcxproj.DotSettings index 21022da..9da00c9 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj.DotSettings +++ b/GlosSIConfig/GlosSIConfig.vcxproj.DotSettings @@ -1,2 +1,2 @@  - Cpp17 \ No newline at end of file + Cpp20 \ No newline at end of file From 288bba14368fb134bc24ccb8ba6027caf4f8d624 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Thu, 8 Sep 2022 21:40:29 +0200 Subject: [PATCH 3/4] Properly rewrite ShortcutsVDF parser --- .gitmodules | 3 + GlosSIConfig/GlosSIConfig.vcxproj | 5 +- GlosSIConfig/GlosSIConfig.vcxproj.filters | 8 +- GlosSIConfig/Resource.rc | 256 +++++++++- GlosSIConfig/ShortcutsVDF.h | 493 ++++++++++++++++++ GlosSIConfig/UIModel.cpp | 51 +- GlosSIConfig/UIModel.h | 4 +- GlosSIConfig/VDFParser.h | 593 ---------------------- deps/fifo_map | 1 + 9 files changed, 775 insertions(+), 639 deletions(-) create mode 100644 GlosSIConfig/ShortcutsVDF.h delete mode 100644 GlosSIConfig/VDFParser.h create mode 160000 deps/fifo_map diff --git a/.gitmodules b/.gitmodules index c9b93ef..906e026 100644 --- a/.gitmodules +++ b/.gitmodules @@ -28,3 +28,6 @@ [submodule "deps/traypp"] path = deps/traypp url = https://github.com/Soundux/traypp.git +[submodule "deps/fifo_map"] + path = deps/fifo_map + url = git@github.com:nlohmann/fifo_map.git diff --git a/GlosSIConfig/GlosSIConfig.vcxproj b/GlosSIConfig/GlosSIConfig.vcxproj index 81997ba..a52c38d 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj +++ b/GlosSIConfig/GlosSIConfig.vcxproj @@ -68,7 +68,7 @@ /Zc:__cplusplus /Zc:twoPhase- %(AdditionalOptions) false NOMINMAX;%(PreprocessorDefinitions) - ..\deps\WinReg;%(AdditionalIncludeDirectories) + ..\deps\WinReg;..\deps\fifo_map\src;%(AdditionalIncludeDirectories) true @@ -90,7 +90,7 @@ /Zc:__cplusplus /Zc:zwoPhase- /permissive- %(AdditionalOptions) false NOMINMAX;%(PreprocessorDefinitions) - ..\deps\WinReg;%(AdditionalIncludeDirectories) + ..\deps\WinReg;..\deps\fifo_map\src;%(AdditionalIncludeDirectories) true @@ -155,7 +155,6 @@ - diff --git a/GlosSIConfig/GlosSIConfig.vcxproj.filters b/GlosSIConfig/GlosSIConfig.vcxproj.filters index 58136fc..ff925f0 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj.filters +++ b/GlosSIConfig/GlosSIConfig.vcxproj.filters @@ -75,9 +75,6 @@ Header Files - - Header Files - Header Files @@ -87,6 +84,9 @@ Header Files + + Header Files + @@ -100,7 +100,7 @@ - Resource Files + Resource Files \ No newline at end of file diff --git a/GlosSIConfig/Resource.rc b/GlosSIConfig/Resource.rc index 3049894..49f67af 100644 --- a/GlosSIConfig/Resource.rc +++ b/GlosSIConfig/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,0,7,1021003100876 - PRODUCTVERSION 0,0,7,1021003100876 + FILEVERSION 0,0,8,001000606002 + PRODUCTVERSION 0,0,8,001000606002 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - Config" - VALUE "FileVersion", "0.0.7.1-21-g31ee876" + VALUE "FileVersion", "0.0.8.0-1-gd6c6ef2" VALUE "InternalName", "GlosSIConfig" VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSIConfig.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.0.7.1-21-g31ee876" + VALUE "ProductVersion", "0.0.8.0-1-gd6c6ef2" END END BLOCK "VarFileInfo" @@ -308,6 +308,254 @@ IDI_ICON1 ICON "..\GloSC_Icon.ico" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GlosSIConfig/ShortcutsVDF.h b/GlosSIConfig/ShortcutsVDF.h new file mode 100644 index 0000000..3d03852 --- /dev/null +++ b/GlosSIConfig/ShortcutsVDF.h @@ -0,0 +1,493 @@ +/* +Copyright 2021-2022 Peter Repukat - FlatspotSoftware + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace VDFParser { + +namespace internal { + constexpr unsigned int str2int(const char* str, int h = 0) + { + return !str[h] ? 5381 : (str2int(str, h + 1) * 33) ^ str[h]; + } +} + + +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++) { + const 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++) { + const char ch = container.data()[i]; + const uint32_t t = (ch ^ crc) & 0xFF; + crc = (crc >> 8) ^ crc32_table[t]; + } + return ~crc; +} +} // namespace crc + +enum VDFTypeId { + Map = 0, + String, + Number, + EndMarker = 0x08 +}; + +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_FlatpakAppID[] = {"FlatpakAppID"}; +static constexpr const char k_tags[] = {"tags"}; + +static const std::string SHORTCUTS_IDENTIFIER = "shortcuts"; + +static inline const std::vector VDF_KEYS = { + k_appid, + k_appname, + k_exe, + k_StartDir, + k_icon, + k_ShortcutPath, + k_LaunchOptions, + k_IsHidden, + k_AllowDesktopConfig, + k_AllowOverlay, + k_openvr, + k_Devkit, + k_DevkitGameID, + k_DevkitOverrideAppID, + k_LastPlayTime, + k_FlatpakAppID, + k_tags +}; +struct VDFValue { + VDFTypeId type = EndMarker; + std::any value{}; + + [[nodiscard]] std::string to_json() const + { + if (!value.has_value()) { + return "null"; + } + switch (type) { + case Number: + return std::to_string(std::any_cast(value)); + case String: + return "\"" + std::regex_replace(std::regex_replace(std::any_cast(value), std::regex(R"(\\)"), R"(\\)"), std::regex(R"(")"), R"(\")") + "\""; + case Map: { + const auto& map = std::any_cast>(value); + std::string res = "{\n"; + for (const auto& [key, v] : map) { + res += "\"" + key + "\": " + v.to_json() + ",\n"; + } + if (res.ends_with(",\n")) { + res.pop_back(); + res.pop_back(); + } + return res + "\n}"; + } + case EndMarker: + // WTF?! + return "\"EndMarker\""; + default: + return "null"; + } + } + + operator std::string() const + { + return to_json(); + } +}; + +static inline const nlohmann::fifo_map DEFAULT_SHORTCUT_MAP = { + { k_appid, {VDFTypeId::Number, 0}}, + { k_appname, {VDFTypeId::String, ""}}, + { k_exe, {VDFTypeId::String, "\"\""}}, + { k_StartDir, {VDFTypeId::String, "\"\""}}, + { k_icon, {VDFTypeId::String, "\"\""}}, + { k_ShortcutPath, {VDFTypeId::String, "\"\""}}, + { k_LaunchOptions, {VDFTypeId::String, ""}}, + { k_IsHidden, {VDFTypeId::Number, ""}}, + { k_AllowDesktopConfig, {VDFTypeId::Number, 0}}, + { k_AllowOverlay, {VDFTypeId::Number, 1}}, + { k_openvr, {VDFTypeId::Number, 0}}, + { k_Devkit, {VDFTypeId::Number, 0}}, + { k_DevkitGameID, {VDFTypeId::String, ""}}, + { k_DevkitOverrideAppID, {VDFTypeId::Number, 0}}, + { k_LastPlayTime, {VDFTypeId::Number, ""}}, + { k_FlatpakAppID, {VDFTypeId::String, ""}}, + { k_tags, {VDFTypeId::Map, nlohmann::fifo_map < std::string, VDFValue >()}}, +}; + +struct Shortcut { + uint32_t appid{}; + std::string appname; + std::string exe; + std::string StartDir; + std::string icon; + std::string ShortcutPath; + std::string LaunchOptions; + uint32_t IsHidden{}; + uint32_t AllowDesktopConfig{}; + uint32_t AllowOverlay{}; + uint32_t openvr{}; + uint32_t Devkit{}; + std::string DevkitGameID; + uint32_t DevkitOverrideAppID{}; + uint32_t LastPlayTime{}; + std::string FlatpakAppID; + std::vector tags; + + nlohmann::fifo_map unsupported_keys; + + Shortcut() = default; + explicit Shortcut(const nlohmann::fifo_map& vdf_map) : Shortcut() + { + for (const auto& [key, value] : (vdf_map.empty() ? DEFAULT_SHORTCUT_MAP : vdf_map)) { + switch (value.type) { + case Number: { + switch (internal::str2int(key.c_str())) { + case internal::str2int(k_appid): + appid = std::any_cast(value.value); + break; + case internal::str2int(k_IsHidden): + IsHidden = std::any_cast(value.value); + break; + case internal::str2int(k_AllowDesktopConfig): + AllowDesktopConfig = std::any_cast(value.value); + break; + case internal::str2int(k_AllowOverlay): + AllowOverlay = std::any_cast(value.value); + break; + case internal::str2int(k_openvr): + openvr = std::any_cast(value.value); + break; + case internal::str2int(k_Devkit): + Devkit = std::any_cast(value.value); + break; + case internal::str2int(k_DevkitOverrideAppID): + DevkitOverrideAppID = std::any_cast(value.value); + break; + case internal::str2int(k_LastPlayTime): + LastPlayTime = std::any_cast(value.value); + break; + default: + unsupported_keys[key] = value; + break; + } + break; + case String: { + switch (internal::str2int(key.c_str())) { + case internal::str2int(k_appname): + appname = std::any_cast(value.value); + break; + case internal::str2int(k_exe): + exe = std::any_cast(value.value); + break; + case internal::str2int(k_StartDir): + StartDir = std::any_cast(value.value); + break; + case internal::str2int(k_icon): + icon = std::any_cast(value.value); + break; + case internal::str2int(k_ShortcutPath): + ShortcutPath = std::any_cast(value.value); + break; + case internal::str2int(k_LaunchOptions): + LaunchOptions = std::any_cast(value.value); + break; + case internal::str2int(k_DevkitGameID): + DevkitGameID = std::any_cast(value.value); + break; + case internal::str2int(k_FlatpakAppID): + FlatpakAppID = std::any_cast(value.value); + break; + default: + unsupported_keys[key] = value; + break; + } + break; + } + case Map: { + switch (internal::str2int(key.c_str())) { + case internal::str2int(k_tags): { + for (const auto& [type, tag] : std::any_cast>(value.value) | std::views::values) { + if (type == String) { + tags.push_back(std::any_cast(tag)); + } + } + break; + } + default: + unsupported_keys[key] = value; + break; + } + break; + } + } + } + } + } + + [[nodiscard]] uint32_t calculateAppId() const + { + const auto checksum = crc::calculate_crc(exe + appname); + return checksum | 0x80000000; + } + + operator VDFValue() const + { + nlohmann::fifo_map value; + value[k_appid] = {Number, appid ? appid : calculateAppId()}; + value[k_appname] = {String, appname}; + value[k_exe] = {String, exe}; + value[k_StartDir] = {String, StartDir}; + value[k_icon] = {String, icon}; + value[k_ShortcutPath] = {String, ShortcutPath}; + value[k_LaunchOptions] = {String, LaunchOptions}; + value[k_IsHidden] = {Number, IsHidden}; + value[k_AllowDesktopConfig] = {Number, AllowDesktopConfig}; + value[k_AllowOverlay] = {Number, AllowOverlay}; + value[k_openvr] = {Number, openvr}; + value[k_Devkit] = {Number, Devkit}; + value[k_DevkitGameID] = {String, DevkitGameID}; + value[k_DevkitOverrideAppID] = {Number, DevkitOverrideAppID}; + value[k_LastPlayTime] = {Number, LastPlayTime}; + value[k_FlatpakAppID] = {String, FlatpakAppID}; + + nlohmann::fifo_map tag_map; + for (size_t i = 0; i < tags.size(); i++) { + tag_map[std::to_string(i)] = {String,tags[i]}; + } + value[k_tags] = {Map, tag_map}; + + for (const auto& [key, v] : unsupported_keys) { + value[key] = v; + } + + return {Map, value}; + } +}; + +class Parser { + private: + static inline std::ifstream ifile; + static inline std::ofstream ofile; + + template + static inline auto readVDFValue() + { + uint8_t buff[sizeof(typ)]; + ifile.read((char*)buff, sizeof(typ)); + return *reinterpret_cast(buff); + } + + template <> + static inline auto readVDFValue() + { + 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; + } + + template <> + static inline auto readVDFValue>() + { + auto res = nlohmann::fifo_map(); + while (true) { + const auto& [key, value] = readVDFValue(); + if (value.type == EndMarker) { + return res; + } + res[key] = value; + } + } + + static inline std::pair readVDFValue() + { + auto res = std::pair("", VDFValue(EndMarker, nullptr)); + + if (ifile.eof()) { + return res; + } + const auto tid = static_cast(readVDFValue()); + if (tid == EndMarker) { + return res; + } + res.second.type = tid; + res.first = readVDFValue(); + switch (tid) { + case VDFTypeId::Map: + res.second.value = readVDFValue>(); + break; + case VDFTypeId::Number: + res.second.value = readVDFValue(); + break; + case VDFTypeId::String: + res.second.value = readVDFValue(); + break; + default: + throw std::exception("VDF: Unknown TypeID"); + break; + } + + return res; + } + + template + static inline auto writeVDFValue(typ v) + { + ofile.write((char*)&v, sizeof(typ)); + } + + template <> + static inline auto writeVDFValue(std::string v) + { + ofile.write(v.data(), v.length()); + ofile.write("\x00", 1); + } + + template <> + static inline auto writeVDFValue>(nlohmann::fifo_map v) + { + for (const auto& pair : v) { + writeVDFValue(pair); + } + ofile.write("\x08", 1); + } + + static inline void writeVDFValue(const std::pair& value) + { + ofile.write((char*)(&value.second.type), 1); + writeVDFValue(value.first); + switch (value.second.type) { + case Map: + writeVDFValue(std::any_cast>(value.second.value)); + break; + case Number: + writeVDFValue(std::any_cast(value.second.value)); + break; + case String: + writeVDFValue(std::any_cast(value.second.value)); + break; + default: + throw std::exception("VDF: Unknown TypeID"); + break; + } + } + + public: + template + static inline std::vector parseShortcuts(const std::filesystem::path& path, LogStream l = std::cout) + { + + ifile.open(path, std::ios::binary | std::ios::in); + if (!ifile.is_open()) { + return {}; + } + + const auto& shortcutsVDF = readVDFValue(); + ifile.close(); + if (shortcutsVDF.second.type != Map || shortcutsVDF.first != SHORTCUTS_IDENTIFIER) { + throw std::exception("invalid shortcuts file!"); + } + + const auto& v = std::any_cast>(shortcutsVDF.second.value); + + std::vector shortcuts; + for (const auto& [type, sc] : v | std::views::values) { + if (type != Map) { + // throw std::exception("invalid shortcuts file!"); + // TODO: warn unsupported + l << "unsupported or invalid shortcuts-file!"; + continue; + } + shortcuts.emplace_back(std::any_cast>(sc)); + } + + return shortcuts; + } + + template + static inline bool writeShortcuts(const std::filesystem::path& path, const std::vector& shortcuts, LogStream l = std::cout) + { + const auto backupFileName = path.wstring() + L".bak"; + if (std::filesystem::exists(path) && !std::filesystem::exists(backupFileName)) { + l << "No shortcuts backup detected... Creating now..."; + const auto copied = std::filesystem::copy_file(path, backupFileName, std::filesystem::copy_options::update_existing); + l << "failed to copy shortcuts.vdf to backup!"; + } + + ofile.open(path.wstring(), std::ios::binary | std::ios::out); + if (!ofile.is_open()) { + return false; + } + nlohmann::fifo_map shortcuts_map; + for (size_t i = 0; i < shortcuts.size(); i++) { + shortcuts_map[std::to_string(i)] = shortcuts[i]; + } + + writeVDFValue({SHORTCUTS_IDENTIFIER, {Map, shortcuts_map}}); + ofile.write("\x08", 1); + ofile.close(); + return true; + } +}; +} // namespace VDFParser diff --git a/GlosSIConfig/UIModel.cpp b/GlosSIConfig/UIModel.cpp index a79162b..a1c2451 100644 --- a/GlosSIConfig/UIModel.cpp +++ b/GlosSIConfig/UIModel.cpp @@ -141,9 +141,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_) { + if (map["name"].toString() == QString::fromStdString(steam_shortcut.appname)) { + if (QString::fromStdString(steam_shortcut.exe).toLower().contains("glossitarget.exe")) { return true; } } @@ -161,15 +161,13 @@ bool UIModel::addToSteam(QVariant shortcut, const QString& shortcutspath, bool f const auto launch = map["launch"].toBool(); VDFParser::Shortcut vdfshortcut; - 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.appname = name.toStdString(); + vdfshortcut.exe = ("\"" + appDir.absolutePath() + "/GlosSITarget.exe" + "\"").toStdString(); + vdfshortcut.StartDir = (launch && !maybeLaunchPath.isEmpty() ? (std::string("\"") + std::filesystem::path(maybeLaunchPath.toStdString()).parent_path().string() + "\"") : ("\"" + appDir.absolutePath() + "\"").toStdString()); - vdfshortcut.appId.value = VDFParser::Parser::calculateAppId(vdfshortcut); // ShortcutPath; default - vdfshortcut.LaunchOptions.value = (QString(name).replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json").toStdString(); + vdfshortcut.LaunchOptions = (QString(name).replace(QRegularExpression("[\\\\/:*?\"<>|]"), "") + ".json").toStdString(); // IsHidden; default // AllowDesktopConfig; default // AllowOverlay; default @@ -181,25 +179,18 @@ bool UIModel::addToSteam(QVariant shortcut, const QString& shortcutspath, bool f auto maybeIcon = map["icon"].toString(); if (maybeIcon.isEmpty()) { if (launch && !maybeLaunchPath.isEmpty()) - vdfshortcut.icon.value = + vdfshortcut.icon = "\"" + (is_windows_ ? QString(maybeLaunchPath).replace(QRegularExpression("\\/"), "\\").toStdString() : maybeLaunchPath.toStdString()) + "\""; } else { - vdfshortcut.icon.value = + vdfshortcut.icon = "\"" + (is_windows_ ? QString(maybeIcon).replace(QRegularExpression("\\/"), "\\").toStdString() : maybeIcon.toStdString()) + "\""; } // Add installed locally and GlosSI tag - VDFParser::ShortcutTag locallyTag; - locallyTag.idx = 0; - locallyTag.value = "Installed locally"; - vdfshortcut.tags.value.push_back(locallyTag); + vdfshortcut.tags.push_back("Installed locally"); + vdfshortcut.tags.push_back("GlosSI"); - VDFParser::ShortcutTag glossitag; - glossitag.idx = 1; - glossitag.value = "GlosSI"; - vdfshortcut.tags.value.push_back(glossitag); - - shortcuts_vdf_.shortcuts.push_back(vdfshortcut); + shortcuts_vdf_.push_back(vdfshortcut); return writeShortcutsVDF(L"add", name.toStdWString(), shortcutspath.toStdWString(), from_cmd); } @@ -220,16 +211,10 @@ bool UIModel::addToSteam(const QString& name, const QString& shortcutspath, bool bool UIModel::removeFromSteam(const QString& name, const QString& shortcutspath, bool from_cmd) { qDebug() << "trying to remove " << name << " from steam"; - 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[i].idx = i; - } - } + shortcuts_vdf_.erase(std::ranges::remove_if(shortcuts_vdf_, [&name](const auto& shortcut) { + return shortcut.appname == name.toStdString(); + }).begin(), + shortcuts_vdf_.end()); return writeShortcutsVDF(L"remove", name.toStdWString(), shortcutspath.toStdWString(), from_cmd); } @@ -271,7 +256,7 @@ bool UIModel::writeShortcutsVDF(const std::wstring& mode, const std::wstring& na bool write_res; try { - write_res = VDFParser::Parser::writeShortcuts(config_path, shortcuts_vdf_); + write_res = VDFParser::Parser::writeShortcuts(config_path, shortcuts_vdf_, qDebug()); } catch (const std::exception& e) { qDebug() << "Couldn't backup shortcuts file: " << e.what(); @@ -413,7 +398,7 @@ void UIModel::parseShortcutVDF() { const std::filesystem::path config_path = std::wstring(getSteamPath()) + user_data_path_.toStdWString() + getSteamUserId() + shortcutsfile_.toStdWString(); try { - shortcuts_vdf_ = VDFParser::Parser::parseShortcuts(config_path); + shortcuts_vdf_ = VDFParser::Parser::parseShortcuts(config_path, qDebug()); } catch (const std::exception& e) { qDebug() << "Error parsing VDF: " << e.what(); diff --git a/GlosSIConfig/UIModel.h b/GlosSIConfig/UIModel.h index 42f8a12..0a2f7d6 100644 --- a/GlosSIConfig/UIModel.h +++ b/GlosSIConfig/UIModel.h @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ #pragma once -#include "VDFParser.h" +#include "ShortcutsVDF.h" #include #include #include @@ -74,7 +74,7 @@ class UIModel : public QObject { QVariantList targets_; - VDFParser::VDFFile shortcuts_vdf_; + std::vector shortcuts_vdf_; #ifdef _WIN32 bool is_windows_ = true; diff --git a/GlosSIConfig/VDFParser.h b/GlosSIConfig/VDFParser.h deleted file mode 100644 index b4c4a93..0000000 --- a/GlosSIConfig/VDFParser.h +++ /dev/null @@ -1,593 +0,0 @@ -/* -Copyright 2021-2022 Peter Repukat - FlatspotSoftware - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// "Shitty shortcuts.vdf Parser"� - -#pragma once -#include -#include -#include -#include -#include -#include - -#include - -namespace VDFParser { -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; -} -} // 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_FlatpakAppID[] = {"FlatpakAppID"}; -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) - { - 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; - } -}; - -struct VDFIdx { - VDFIdx(){}; - VDFIdx(const VDFIdx& other) - { - data = other.data; - }; - VDFIdx(VDFIdx&& other) - { - data = std::move(other.data); - }; - VDFIdx(int idx) - { - data = std::to_string(idx); - } - std::string data; - operator int() const - { - int res = 0; - std::from_chars(data.data(), data.data() + data.size(), res); - return res; - } - - VDFIdx& operator=(const VDFIdx& other) - { - data = other.data; - - return *this; - } - VDFIdx& operator=(VDFIdx&& other) - { - data = std::move(other.data); - return *this; - } -}; - -struct ShortcutTag { - ShortcutTag(){}; - ShortcutTag(const ShortcutTag& other) - { - idx = other.idx; - value = other.value; - }; - ShortcutTag(ShortcutTag&& other) - { - idx = std::move(other.idx); - value = std::move(other.value); - }; - VDFIdx idx; - std::string value; - const uint16_t end_marker = 0x0808; - - ShortcutTag& operator=(const ShortcutTag& other) - { - idx = other.idx; - value = other.value; - return *this; - } - ShortcutTag& operator=(ShortcutTag&& other) - { - idx = std::move(other.idx); - value = std::move(other.value); - return *this; - } -}; - -struct Shortcut { - VDFIdx 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 FlatpakAppID{""}; // - 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; - FlatpakAppID = other.FlatpakAppID; - tags = other.tags; - return *this; - } - //std::wstring to_json() - //{ - // std::wstring res = L"{"; - // res += L"idx: " + std::to_wstring(idx.operator int()) + L",\n"; - // res += L"appId: " + std::to_wstring(appId.value) + L",\n"; - // res += L"appName: " + std::filesystem::path(appName.value).wstring() + L",\n"; - // res += L"StartDir: " + std::filesystem::path(StartDir.value).wstring() + L",\n"; - // res += L"ShortcutPath: " + std::filesystem::path(ShortcutPath.value).wstring() + L",\n"; - // res += L"LaunchOptions: " + std::filesystem::path(LaunchOptions.value).wstring() + L",\n"; - // res += L"IsHidden: " + (IsHidden.value ? L"true" : L"false") + L",\n"; - // res += L"AllowDesktopConfig: " + (AllowDesktopConfig.value ? L"true" : L"false") + L",\n"; - // res += L"idx: " + std::to_wstring(appId.value) + L",\n"; - // res += L"}"; - // return res; - //} -}; - -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; - } - //std::wstring to_json() - //{ - // std::wstring res = L"["; - - // res += L"]"; - // return res; - //} -}; - -class Parser { - private: - static inline std::ifstream ifile; - static inline std::ofstream ofile; - - template - static inline auto readVDFBuffer(typ* buff, size sz) - { - if (ifile.eof()) { - - return; - } - ifile.read((char*)buff, sz); - } - - template - static inline auto readVDFValue() - { - uint8_t buff[sizeof(typ)]; - ifile.read((char*)buff, sizeof(typ)); - return *reinterpret_cast(buff); - } - - 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; - } - - 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 VDFFile parseShortcuts(std::filesystem::path path) - { - VDFFile vdffile; - - ifile.open(path, std::ios::binary | std::ios::in); - if (!ifile.is_open()) { - return {}; - } - - auto firsty = readVDFValue(); - if (vdffile.first_byte != firsty) { - // TODO: invalid - ifile.close(); - throw std::exception("First byte is invalid in vdf"); - } - - auto headername = readVDFString(); - if (vdffile.identifier != headername) { - // TODO: invalid - ifile.close(); - throw std::exception("VDF header is invalid"); - } - - while (true) { - std::vector buff; - if (ifile.eof()) { - break; - } - char b = '\x0'; - readVDFBuffer(&b, 1); // skip 0 byte - Shortcut shortcut; - shortcut.idx.data = readVDFString(); - if (shortcut.idx.data == "\x08\x08") { - break; - } - while (true) // TODO; - { - if (ifile.eof()) { - break; - } - const auto tid = static_cast(readVDFValue()); - if (tid == 0x08) { - auto nextbyte = readVDFValue(); - if (nextbyte == 0x08) { - break; - } - else { - // WTF?! - // TODO: - throw std::exception("VDF: 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.FlatpakAppID.key) { - shortcut.FlatpakAppID.value = readVDFString(); - continue; - } - if (key == shortcut.tags.key) { - ShortcutTag tag; - while (true) { - if (ifile.eof()) { - break; - } - char tbuff[2]; - readVDFBuffer(tbuff, 2); // 2 bytes POSSIBLE end marker - ifile.seekg(-1, std::ios_base::cur); // go one back, skip typeId - if (tbuff[0] == 0x08 && tbuff[1] == 0x08) { - ifile.seekg(-1, std::ios_base::cur); // another back - break; - } - tag.idx.data = readVDFString(); - if (tag.idx.data == "\x08\x08") { - ifile.seekg(-2, std::ios_base::cur); - break; - } - tag.value = readVDFString(); - shortcut.tags.value.push_back(tag); - } - continue; - } - } - if (!(shortcut.idx.data == "\x00\x00")) { - vdffile.shortcuts.push_back(shortcut); - } - } - - ifile.close(); - - return vdffile; - } - - static inline bool writeShortcuts(std::filesystem::path path, const VDFFile& vdffile) - { - const auto backupFileName = path.wstring() + L".bak"; - if (std::filesystem::exists(path) && !std::filesystem::exists(backupFileName)) { - qDebug() << "No shortcuts backup detected... Creating now..."; - const auto copied = std::filesystem::copy_file(path, backupFileName, std::filesystem::copy_options::update_existing); - if (!copied) { - qDebug() << "failed to copy shortcuts.vdf to backup!"; - } - } - - ofile.open(path.wstring(), 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("\x00", 1); - ofile.write(shortcut.idx.data.data(), shortcut.idx.data.length()); - 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.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.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.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.FlatpakAppID.type_id, 1); - ofile.write(shortcut.FlatpakAppID.key, 13); - ofile.write(shortcut.FlatpakAppID.value.data(), shortcut.FlatpakAppID.value.length()); - ofile.write("\x00", 1); - - // - 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.data(), tag.idx.data.length()); - 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; - } -}; -} // namespace VDFParser diff --git a/deps/fifo_map b/deps/fifo_map new file mode 160000 index 0000000..d732aaf --- /dev/null +++ b/deps/fifo_map @@ -0,0 +1 @@ +Subproject commit d732aaf9a315415ae8fd7eb11e3a4c1f80e42a48 From 1ca127519a786a3d06f4070769f826d26874c580 Mon Sep 17 00:00:00 2001 From: Peter Repukat Date: Fri, 9 Sep 2022 16:09:02 +0200 Subject: [PATCH 4/4] Use shrotcuts_parser from submodule --- .gitmodules | 3 + GlosSIConfig/GlosSIConfig.vcxproj | 5 +- GlosSIConfig/GlosSIConfig.vcxproj.filters | 3 - GlosSIConfig/Resource.rc | 208 ++++++++- GlosSIConfig/ShortcutsVDF.h | 493 ---------------------- GlosSIConfig/UIModel.h | 2 +- deps/Shortcuts_VDF | 1 + 7 files changed, 211 insertions(+), 504 deletions(-) delete mode 100644 GlosSIConfig/ShortcutsVDF.h create mode 160000 deps/Shortcuts_VDF diff --git a/.gitmodules b/.gitmodules index 906e026..e54a7a8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "deps/fifo_map"] path = deps/fifo_map url = git@github.com:nlohmann/fifo_map.git +[submodule "deps/Shortcuts_VDF"] + path = deps/Shortcuts_VDF + url = git@github.com:Alia5/Shortcuts_VDF.git diff --git a/GlosSIConfig/GlosSIConfig.vcxproj b/GlosSIConfig/GlosSIConfig.vcxproj index a52c38d..2033835 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj +++ b/GlosSIConfig/GlosSIConfig.vcxproj @@ -68,7 +68,7 @@ /Zc:__cplusplus /Zc:twoPhase- %(AdditionalOptions) false NOMINMAX;%(PreprocessorDefinitions) - ..\deps\WinReg;..\deps\fifo_map\src;%(AdditionalIncludeDirectories) + ..\deps\WinReg;..\deps\fifo_map\src;..\deps\Shortcuts_VDF\include;%(AdditionalIncludeDirectories) true @@ -90,7 +90,7 @@ /Zc:__cplusplus /Zc:zwoPhase- /permissive- %(AdditionalOptions) false NOMINMAX;%(PreprocessorDefinitions) - ..\deps\WinReg;..\deps\fifo_map\src;%(AdditionalIncludeDirectories) + ..\deps\WinReg;..\deps\fifo_map\src;..\deps\Shortcuts_VDF\include;%(AdditionalIncludeDirectories) true @@ -153,7 +153,6 @@ - diff --git a/GlosSIConfig/GlosSIConfig.vcxproj.filters b/GlosSIConfig/GlosSIConfig.vcxproj.filters index ff925f0..e1d59d1 100644 --- a/GlosSIConfig/GlosSIConfig.vcxproj.filters +++ b/GlosSIConfig/GlosSIConfig.vcxproj.filters @@ -84,9 +84,6 @@ Header Files - - Header Files - diff --git a/GlosSIConfig/Resource.rc b/GlosSIConfig/Resource.rc index 49f67af..ee15a05 100644 --- a/GlosSIConfig/Resource.rc +++ b/GlosSIConfig/Resource.rc @@ -51,8 +51,8 @@ END // VS_VERSION_INFO VERSIONINFO - FILEVERSION 0,0,8,001000606002 - PRODUCTVERSION 0,0,8,001000606002 + FILEVERSION 0,0,8,003002880001 + PRODUCTVERSION 0,0,8,003002880001 FILEFLAGSMASK 0x3fL #ifdef _DEBUG FILEFLAGS 0x1L @@ -69,12 +69,12 @@ BEGIN BEGIN VALUE "CompanyName", "Peter Repukat - FlatspotSoftware" VALUE "FileDescription", "GlosSI - Config" - VALUE "FileVersion", "0.0.8.0-1-gd6c6ef2" + VALUE "FileVersion", "0.0.8.0-3-g288bba1" VALUE "InternalName", "GlosSIConfig" VALUE "LegalCopyright", "Copyright (C) 2021 Peter Repukat - FlatspotSoftware" VALUE "OriginalFilename", "GlosSIConfig.exe" VALUE "ProductName", "GlosSI" - VALUE "ProductVersion", "0.0.8.0-1-gd6c6ef2" + VALUE "ProductVersion", "0.0.8.0-3-g288bba1" END END BLOCK "VarFileInfo" @@ -556,6 +556,206 @@ IDI_ICON1 ICON "..\GloSC_Icon.ico" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/GlosSIConfig/ShortcutsVDF.h b/GlosSIConfig/ShortcutsVDF.h deleted file mode 100644 index 3d03852..0000000 --- a/GlosSIConfig/ShortcutsVDF.h +++ /dev/null @@ -1,493 +0,0 @@ -/* -Copyright 2021-2022 Peter Repukat - FlatspotSoftware - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -#pragma once -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace VDFParser { - -namespace internal { - constexpr unsigned int str2int(const char* str, int h = 0) - { - return !str[h] ? 5381 : (str2int(str, h + 1) * 33) ^ str[h]; - } -} - - -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++) { - const 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++) { - const char ch = container.data()[i]; - const uint32_t t = (ch ^ crc) & 0xFF; - crc = (crc >> 8) ^ crc32_table[t]; - } - return ~crc; -} -} // namespace crc - -enum VDFTypeId { - Map = 0, - String, - Number, - EndMarker = 0x08 -}; - -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_FlatpakAppID[] = {"FlatpakAppID"}; -static constexpr const char k_tags[] = {"tags"}; - -static const std::string SHORTCUTS_IDENTIFIER = "shortcuts"; - -static inline const std::vector VDF_KEYS = { - k_appid, - k_appname, - k_exe, - k_StartDir, - k_icon, - k_ShortcutPath, - k_LaunchOptions, - k_IsHidden, - k_AllowDesktopConfig, - k_AllowOverlay, - k_openvr, - k_Devkit, - k_DevkitGameID, - k_DevkitOverrideAppID, - k_LastPlayTime, - k_FlatpakAppID, - k_tags -}; -struct VDFValue { - VDFTypeId type = EndMarker; - std::any value{}; - - [[nodiscard]] std::string to_json() const - { - if (!value.has_value()) { - return "null"; - } - switch (type) { - case Number: - return std::to_string(std::any_cast(value)); - case String: - return "\"" + std::regex_replace(std::regex_replace(std::any_cast(value), std::regex(R"(\\)"), R"(\\)"), std::regex(R"(")"), R"(\")") + "\""; - case Map: { - const auto& map = std::any_cast>(value); - std::string res = "{\n"; - for (const auto& [key, v] : map) { - res += "\"" + key + "\": " + v.to_json() + ",\n"; - } - if (res.ends_with(",\n")) { - res.pop_back(); - res.pop_back(); - } - return res + "\n}"; - } - case EndMarker: - // WTF?! - return "\"EndMarker\""; - default: - return "null"; - } - } - - operator std::string() const - { - return to_json(); - } -}; - -static inline const nlohmann::fifo_map DEFAULT_SHORTCUT_MAP = { - { k_appid, {VDFTypeId::Number, 0}}, - { k_appname, {VDFTypeId::String, ""}}, - { k_exe, {VDFTypeId::String, "\"\""}}, - { k_StartDir, {VDFTypeId::String, "\"\""}}, - { k_icon, {VDFTypeId::String, "\"\""}}, - { k_ShortcutPath, {VDFTypeId::String, "\"\""}}, - { k_LaunchOptions, {VDFTypeId::String, ""}}, - { k_IsHidden, {VDFTypeId::Number, ""}}, - { k_AllowDesktopConfig, {VDFTypeId::Number, 0}}, - { k_AllowOverlay, {VDFTypeId::Number, 1}}, - { k_openvr, {VDFTypeId::Number, 0}}, - { k_Devkit, {VDFTypeId::Number, 0}}, - { k_DevkitGameID, {VDFTypeId::String, ""}}, - { k_DevkitOverrideAppID, {VDFTypeId::Number, 0}}, - { k_LastPlayTime, {VDFTypeId::Number, ""}}, - { k_FlatpakAppID, {VDFTypeId::String, ""}}, - { k_tags, {VDFTypeId::Map, nlohmann::fifo_map < std::string, VDFValue >()}}, -}; - -struct Shortcut { - uint32_t appid{}; - std::string appname; - std::string exe; - std::string StartDir; - std::string icon; - std::string ShortcutPath; - std::string LaunchOptions; - uint32_t IsHidden{}; - uint32_t AllowDesktopConfig{}; - uint32_t AllowOverlay{}; - uint32_t openvr{}; - uint32_t Devkit{}; - std::string DevkitGameID; - uint32_t DevkitOverrideAppID{}; - uint32_t LastPlayTime{}; - std::string FlatpakAppID; - std::vector tags; - - nlohmann::fifo_map unsupported_keys; - - Shortcut() = default; - explicit Shortcut(const nlohmann::fifo_map& vdf_map) : Shortcut() - { - for (const auto& [key, value] : (vdf_map.empty() ? DEFAULT_SHORTCUT_MAP : vdf_map)) { - switch (value.type) { - case Number: { - switch (internal::str2int(key.c_str())) { - case internal::str2int(k_appid): - appid = std::any_cast(value.value); - break; - case internal::str2int(k_IsHidden): - IsHidden = std::any_cast(value.value); - break; - case internal::str2int(k_AllowDesktopConfig): - AllowDesktopConfig = std::any_cast(value.value); - break; - case internal::str2int(k_AllowOverlay): - AllowOverlay = std::any_cast(value.value); - break; - case internal::str2int(k_openvr): - openvr = std::any_cast(value.value); - break; - case internal::str2int(k_Devkit): - Devkit = std::any_cast(value.value); - break; - case internal::str2int(k_DevkitOverrideAppID): - DevkitOverrideAppID = std::any_cast(value.value); - break; - case internal::str2int(k_LastPlayTime): - LastPlayTime = std::any_cast(value.value); - break; - default: - unsupported_keys[key] = value; - break; - } - break; - case String: { - switch (internal::str2int(key.c_str())) { - case internal::str2int(k_appname): - appname = std::any_cast(value.value); - break; - case internal::str2int(k_exe): - exe = std::any_cast(value.value); - break; - case internal::str2int(k_StartDir): - StartDir = std::any_cast(value.value); - break; - case internal::str2int(k_icon): - icon = std::any_cast(value.value); - break; - case internal::str2int(k_ShortcutPath): - ShortcutPath = std::any_cast(value.value); - break; - case internal::str2int(k_LaunchOptions): - LaunchOptions = std::any_cast(value.value); - break; - case internal::str2int(k_DevkitGameID): - DevkitGameID = std::any_cast(value.value); - break; - case internal::str2int(k_FlatpakAppID): - FlatpakAppID = std::any_cast(value.value); - break; - default: - unsupported_keys[key] = value; - break; - } - break; - } - case Map: { - switch (internal::str2int(key.c_str())) { - case internal::str2int(k_tags): { - for (const auto& [type, tag] : std::any_cast>(value.value) | std::views::values) { - if (type == String) { - tags.push_back(std::any_cast(tag)); - } - } - break; - } - default: - unsupported_keys[key] = value; - break; - } - break; - } - } - } - } - } - - [[nodiscard]] uint32_t calculateAppId() const - { - const auto checksum = crc::calculate_crc(exe + appname); - return checksum | 0x80000000; - } - - operator VDFValue() const - { - nlohmann::fifo_map value; - value[k_appid] = {Number, appid ? appid : calculateAppId()}; - value[k_appname] = {String, appname}; - value[k_exe] = {String, exe}; - value[k_StartDir] = {String, StartDir}; - value[k_icon] = {String, icon}; - value[k_ShortcutPath] = {String, ShortcutPath}; - value[k_LaunchOptions] = {String, LaunchOptions}; - value[k_IsHidden] = {Number, IsHidden}; - value[k_AllowDesktopConfig] = {Number, AllowDesktopConfig}; - value[k_AllowOverlay] = {Number, AllowOverlay}; - value[k_openvr] = {Number, openvr}; - value[k_Devkit] = {Number, Devkit}; - value[k_DevkitGameID] = {String, DevkitGameID}; - value[k_DevkitOverrideAppID] = {Number, DevkitOverrideAppID}; - value[k_LastPlayTime] = {Number, LastPlayTime}; - value[k_FlatpakAppID] = {String, FlatpakAppID}; - - nlohmann::fifo_map tag_map; - for (size_t i = 0; i < tags.size(); i++) { - tag_map[std::to_string(i)] = {String,tags[i]}; - } - value[k_tags] = {Map, tag_map}; - - for (const auto& [key, v] : unsupported_keys) { - value[key] = v; - } - - return {Map, value}; - } -}; - -class Parser { - private: - static inline std::ifstream ifile; - static inline std::ofstream ofile; - - template - static inline auto readVDFValue() - { - uint8_t buff[sizeof(typ)]; - ifile.read((char*)buff, sizeof(typ)); - return *reinterpret_cast(buff); - } - - template <> - static inline auto readVDFValue() - { - 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; - } - - template <> - static inline auto readVDFValue>() - { - auto res = nlohmann::fifo_map(); - while (true) { - const auto& [key, value] = readVDFValue(); - if (value.type == EndMarker) { - return res; - } - res[key] = value; - } - } - - static inline std::pair readVDFValue() - { - auto res = std::pair("", VDFValue(EndMarker, nullptr)); - - if (ifile.eof()) { - return res; - } - const auto tid = static_cast(readVDFValue()); - if (tid == EndMarker) { - return res; - } - res.second.type = tid; - res.first = readVDFValue(); - switch (tid) { - case VDFTypeId::Map: - res.second.value = readVDFValue>(); - break; - case VDFTypeId::Number: - res.second.value = readVDFValue(); - break; - case VDFTypeId::String: - res.second.value = readVDFValue(); - break; - default: - throw std::exception("VDF: Unknown TypeID"); - break; - } - - return res; - } - - template - static inline auto writeVDFValue(typ v) - { - ofile.write((char*)&v, sizeof(typ)); - } - - template <> - static inline auto writeVDFValue(std::string v) - { - ofile.write(v.data(), v.length()); - ofile.write("\x00", 1); - } - - template <> - static inline auto writeVDFValue>(nlohmann::fifo_map v) - { - for (const auto& pair : v) { - writeVDFValue(pair); - } - ofile.write("\x08", 1); - } - - static inline void writeVDFValue(const std::pair& value) - { - ofile.write((char*)(&value.second.type), 1); - writeVDFValue(value.first); - switch (value.second.type) { - case Map: - writeVDFValue(std::any_cast>(value.second.value)); - break; - case Number: - writeVDFValue(std::any_cast(value.second.value)); - break; - case String: - writeVDFValue(std::any_cast(value.second.value)); - break; - default: - throw std::exception("VDF: Unknown TypeID"); - break; - } - } - - public: - template - static inline std::vector parseShortcuts(const std::filesystem::path& path, LogStream l = std::cout) - { - - ifile.open(path, std::ios::binary | std::ios::in); - if (!ifile.is_open()) { - return {}; - } - - const auto& shortcutsVDF = readVDFValue(); - ifile.close(); - if (shortcutsVDF.second.type != Map || shortcutsVDF.first != SHORTCUTS_IDENTIFIER) { - throw std::exception("invalid shortcuts file!"); - } - - const auto& v = std::any_cast>(shortcutsVDF.second.value); - - std::vector shortcuts; - for (const auto& [type, sc] : v | std::views::values) { - if (type != Map) { - // throw std::exception("invalid shortcuts file!"); - // TODO: warn unsupported - l << "unsupported or invalid shortcuts-file!"; - continue; - } - shortcuts.emplace_back(std::any_cast>(sc)); - } - - return shortcuts; - } - - template - static inline bool writeShortcuts(const std::filesystem::path& path, const std::vector& shortcuts, LogStream l = std::cout) - { - const auto backupFileName = path.wstring() + L".bak"; - if (std::filesystem::exists(path) && !std::filesystem::exists(backupFileName)) { - l << "No shortcuts backup detected... Creating now..."; - const auto copied = std::filesystem::copy_file(path, backupFileName, std::filesystem::copy_options::update_existing); - l << "failed to copy shortcuts.vdf to backup!"; - } - - ofile.open(path.wstring(), std::ios::binary | std::ios::out); - if (!ofile.is_open()) { - return false; - } - nlohmann::fifo_map shortcuts_map; - for (size_t i = 0; i < shortcuts.size(); i++) { - shortcuts_map[std::to_string(i)] = shortcuts[i]; - } - - writeVDFValue({SHORTCUTS_IDENTIFIER, {Map, shortcuts_map}}); - ofile.write("\x08", 1); - ofile.close(); - return true; - } -}; -} // namespace VDFParser diff --git a/GlosSIConfig/UIModel.h b/GlosSIConfig/UIModel.h index 0a2f7d6..d9c1595 100644 --- a/GlosSIConfig/UIModel.h +++ b/GlosSIConfig/UIModel.h @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ #pragma once -#include "ShortcutsVDF.h" +#include #include #include #include diff --git a/deps/Shortcuts_VDF b/deps/Shortcuts_VDF new file mode 160000 index 0000000..2816b31 --- /dev/null +++ b/deps/Shortcuts_VDF @@ -0,0 +1 @@ +Subproject commit 2816b31c8e777c2920e1f0881ce10c5c66e30c63