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