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