The open source OpenXR runtime

external/valve-file-vdf: update to 1.0.0

https://github.com/TinyTinni/ValveFileVDF/releases/tag/v1.0.0

Part-of: <https://gitlab.freedesktop.org/monado/monado/-/merge_requests/2266>

authored by

Simon Zeni and committed by
Marge Bot
a628e14d f29275ce

+701 -866
+1 -1
scripts/codespell-project.sh
··· 19 set -e 20 21 # Comma-delimited list of words for codespell to not try to correct. 22 - IGNORE_WORDS_LIST="ang,sinc,sie,stoll,wil,daa,localy,od,ser,unknwn,parm,inflight,marge,devault,errorprone" 23 IGNORE_REGEX="\b(pEvent|inout|Kimera)\b" 24 25 SCRIPTDIR=$(cd "$(dirname "$0")" && pwd)
··· 19 set -e 20 21 # Comma-delimited list of words for codespell to not try to correct. 22 + IGNORE_WORDS_LIST="ang,sinc,sie,stoll,wil,daa,localy,od,ser,unknwn,parm,inflight,marge,devault,errorprone,childs" 23 IGNORE_REGEX="\b(pEvent|inout|Kimera)\b" 24 25 SCRIPTDIR=$(cd "$(dirname "$0")" && pwd)
+21
src/external/valve-file-vdf/LICENSE
···
··· 1 + The MIT License (MIT) 2 + 3 + Copyright (c) Matthias Moeller 2016 m_moeller@live.de 4 + 5 + Permission is hereby granted, free of charge, to any person obtaining a copy 6 + of this software and associated documentation files (the "Software"), to deal 7 + in the Software without restriction, including without limitation the rights 8 + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 + copies of the Software, and to permit persons to whom the Software is 10 + furnished to do so, subject to the following conditions: 11 + 12 + The above copyright notice and this permission notice shall be included in all 13 + copies or substantial portions of the Software. 14 + 15 + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 + SOFTWARE.
-280
src/external/valve-file-vdf/Readme.md
··· 1 - Upstream source - https://github.com/TinyTinni/ValveFileVDF 2 - 3 - # Valve Data Format (.vdf) Reader and Writer in C++ 4 - 5 - [![CMake](https://github.com/TinyTinni/ValveFileVDF/actions/workflows/cmake.yml/badge.svg)](https://github.com/TinyTinni/ValveFileVDF/actions/workflows/cmake.yml) 6 - 7 - Valve uses its own JSON-like data format: [KeyValue, also known as vdf.](https://developer.valvesoftware.com/wiki/KeyValues) 8 - e.g. in game manifest files or as SteamCMD output. 9 - This header-only file provides a parser and writer to load and save the given data. 10 - 11 - ## Features: 12 - - read and write vdf data in C++ 13 - - build-in encodings: `char` and `wchar_t` 14 - - supports custom character sets 15 - - support for C++ (//) and C (/**/) comments 16 - - `#include`/`#base` keyword (note: searches for files in the current working directory) 17 - - platform independent 18 - - header-only 19 - 20 - ## Requirements 21 - - C++11 22 - 23 - ## Test Requirements 24 - - C++14 (uses [catch2](https://github.com/catchorg/Catch2)) 25 - 26 - (works with the C++11 features of vs120/"Visual Studio 2013" and newer) 27 - 28 - ## How-To Use 29 - First, you have to include the main file `vdf-Parser.h`. 30 - This file provides several functions and data-structures which are 31 - in the namespace `tyti::vdf`. 32 - 33 - All functions and data structures supports wide characters. 34 - The wide character data structure is indicated by the commonly known `w`-prefix. 35 - Functions are templates and don't need a prefix. 36 - 37 - To read an file, create a stream e.g. `std::ifsteam` or `std::wifstream` 38 - and call the `tyti::vdf::read` function. 39 - ```c++ 40 - std::ifstream file("PathToMyFile"); 41 - auto root = tyti::vdf::read(file); 42 - ``` 43 - You can also define a sequence of character defined by a range. 44 - ```c++ 45 - std::string blob; 46 - ... 47 - auto root = tyti::vdf::read(std::cbegin(blob), std::cend(blob)); 48 - 49 - //given .vdf below, following holds 50 - assert(root.name == "name"); 51 - const std::shared_ptr<tyti::vdf::object> child = root.childs["child0"]; 52 - assert(child->name == "child0"); 53 - const std::string& k = root[0].attribs["attrib0"]; 54 - assert(k == "value"); 55 - ``` 56 - 57 - The `tyti::vdf::object` is a tree like data structure. 58 - It has its name, some attributes as a pair of `key` and `value` 59 - and its object childs. Below you can see a vdf data structure and how it is stored by naming: 60 - ```javascript 61 - "name" 62 - { 63 - "attrib0" "value" // saved as a pair, first -> key, second -> value 64 - "#base" "includeFile.vdf" // appends object defined in the file to childs 65 - "child0" 66 - { 67 - ... 68 - } 69 - ... 70 - } 71 - ``` 72 - 73 - Given such an object, you can also write it into vdf files via: 74 - ```c++ 75 - tyti::vdf::write(file, object); 76 - ``` 77 - 78 - ## Multi-Key and Custom Output Type 79 - 80 - It is also possible to customize your output dataformat. 81 - Per default, the parser stores all items in a std::unordered_map, which, per definition, 82 - doesn't allow different entries with the same key. 83 - 84 - However, the Valve vdf format supports multiple keys. Therefore, the output data format 85 - has to store all items in e.g. a std::unordered_multimap. 86 - 87 - You can change the output format by passing the output type via template argument to 88 - the read function 89 - ```c++ 90 - namespace tyti; 91 - vdf::object no_multi_key = vdf::read(file); 92 - vdf::multikey_object multi_key = vdf::read<vdf::multikey_object>(file); 93 - ``` 94 - 95 - __Note__: The interface of [std::unordered_map](http://en.cppreference.com/w/cpp/container/unordered_map) and [std::unordered_multimap](http://en.cppreference.com/w/cpp/container/unordered_multimap) 96 - are different when you access the elements. 97 - 98 - It is also possible to create your own data structure which is used by the parser. 99 - Your output class needs to define 3 functions with the following signature: 100 - 101 - ```c++ 102 - void add_attribute(std::basic_string<CHAR> key, std::basic_string<CHAR> value); 103 - void add_child(std::unique_ptr< MYCLASS > child); 104 - void set_name(std::basic_string<CHAR> n); 105 - ``` 106 - where ```MYCLASS``` is the tpe of your class and ```CHAR``` the type of your character set. 107 - Also, the type has to be [default constructible](http://en.cppreference.com/w/cpp/types/is_default_constructible) 108 - and [move constructible](http://en.cppreference.com/w/cpp/types/is_move_constructible). 109 - 110 - This also allows you, to inspect the file without storing it in a data structure. 111 - Lets say, for example, you want to count all attributes of a file without storing it. 112 - You can do this by using this class 113 - 114 - ```c++ 115 - struct counter 116 - { 117 - size_t num_attributes = 0; 118 - void add_attribute(std::string key, std::string value) 119 - { 120 - ++num_attributes; 121 - } 122 - void add_child(std::unique_ptr< counter > child) 123 - { 124 - num_attributes += child->num_attributes; 125 - } 126 - void set_name(std::string n) 127 - {} 128 - }; 129 - ``` 130 - 131 - and then call the read function 132 - ```c++ 133 - std::vector<counter> num = tyti::vdf::read<counter>(file); 134 - ``` 135 - 136 - ## Options (experimental) 137 - 138 - You can configure the parser, the non default options are not well tested yet. 139 - 140 - ```c++ 141 - struct Options 142 - { 143 - bool strip_escape_symbols; //default true 144 - bool ignore_all_platform_conditionals; // default false 145 - bool ignore_includes; //default false 146 - }; 147 - 148 - ``` 149 - 150 - ## Reference 151 - ```c++ 152 - ///////////////////////////////////////////////////////////// 153 - // pre-defined output classes 154 - ///////////////////////////////////////////////////////////// 155 - // default output object 156 - template<typename T> 157 - basic_object<T> 158 - { 159 - std::basic_string<char_type> name; 160 - std::unordered_map<std::basic_string<char_type>, std::basic_string<char_type> > attribs; 161 - std::unordered_map<std::basic_string<char_type>, std::shared_ptr< basic_object<char_type> > > childs; 162 - }; 163 - typedef basic_object<char> object; 164 - typedef basic_object<wchar_t> wobject 165 - 166 - // output object with multikey support 167 - template<typename T> 168 - basic_multikey_object<T> 169 - { 170 - std::basic_string<char_type> name; 171 - std::unordered_multimap<std::basic_string<char_type>, std::basic_string<char_type> > attribs; 172 - std::unordered_multimap<std::basic_string<char_type>, std::shared_ptr< basic_object<char_type> > > childs; 173 - }; 174 - typedef basic_multikey_object<char> multikey_object; 175 - typedef basic_multikey_object<wchar_t> wmultikey_object 176 - 177 - ///////////////////////////////////////////////////////////// 178 - // error codes 179 - ///////////////////////////////////////////////////////////// 180 - /* 181 - Possible error codes: 182 - std::errc::protocol_error: file is mailformatted 183 - std::errc::not_enough_memory: not enough space 184 - std::errc::invalid_argument: iterators throws e.g. out of range 185 - */ 186 - 187 - ///////////////////////////////////////////////////////////// 188 - // read from stream 189 - ///////////////////////////////////////////////////////////// 190 - 191 - /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. 192 - throws "std::bad_alloc" if file buffer could not be allocated 193 - throws "std::runtime_error" if a parsing error occured 194 - */ 195 - template<ytpename OutputT, typename iStreamT> 196 - std::vector<OutputT> read(iStreamT& inStream, const Options &opt = Options{}); 197 - 198 - template<typename iStreamT> 199 - std::vector<basic_object<typename iStreamT::char_type>> read(iStreamT& inStream, const Options &opt = Options{}); 200 - 201 - /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. 202 - throws "std::bad_alloc" if file buffer could not be allocated 203 - ok == false, if a parsing error occured 204 - */ 205 - template<typename OutputT, typename iStreamT> 206 - std::vector<OutputT> read(iStreamT& inStream, bool* ok, const Options &opt = Options{}); 207 - 208 - template<typename iStreamT> 209 - std::vector<basic_object<typename iStreamT::char_type>> read(iStreamT& inStream, bool* ok, const Options &opt = Options{}); 210 - 211 - /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. 212 - throws "std::bad_alloc" if file buffer could not be allocated 213 - */ 214 - template<typename OutputT, typename iStreamT> 215 - std::vector<OutputT> read(iStreamT& inStream, std::error_code& ec, const Options &opt = Options{}); 216 - 217 - template<typename iStreamT> 218 - std::vector<basic_object<iStreamT::char_type>> read(iStreamT& inStream, std::error_code& ec, const Options &opt = Options{}); 219 - 220 - ///////////////////////////////////////////////////////////// 221 - // read from memory 222 - ///////////////////////////////////////////////////////////// 223 - 224 - /** \brief Read VDF formatted sequences defined by the range [first, last). 225 - If the file is mailformatted, parser will try to read it until it can. 226 - @param first begin iterator 227 - @param end end iterator 228 - 229 - throws "std::runtime_error" if a parsing error occured 230 - throws "std::bad_alloc" if not enough memory could be allocated 231 - */ 232 - template<typename OutputT, typename IterT> 233 - std::vector<OutputT> read(IterT first, IterT last, const Options &opt = Options{}); 234 - 235 - template<typename IterT> 236 - std::vector<basic_object<typename std::iterator_traits<IterT>::value_type>> read(IterT first, IterT last, const Options &opt = Options{}); 237 - 238 - /** \brief Read VDF formatted sequences defined by the range [first, last). 239 - If the file is mailformatted, parser will try to read it until it can. 240 - @param first begin iterator 241 - @param end end iterator 242 - @param ok output bool. true, if parser successed, false, if parser failed 243 - */ 244 - template<typename OutputT, typename IterT> 245 - std::vector<OutputT> read(IterT first, IterT last, bool* ok, const Options &opt = Options{}) noexcept; 246 - 247 - template<typename IterT> 248 - std::vector<basic_object<typename std::iterator_traits<IterT>::value_type>> read(IterT first, IterT last, bool* ok, const Options &opt = Options{}) noexcept; 249 - 250 - 251 - 252 - /** \brief Read VDF formatted sequences defined by the range [first, last). 253 - If the file is mailformatted, parser will try to read it until it can. 254 - @param first begin iterator 255 - @param end end iterator 256 - @param ec output bool. 0 if ok, otherwise, holds an system error code 257 - */ 258 - template<typename OutputT, typename IterT> 259 - std::vector<OutputT> read(IterT first, IterT last, std::error_code& ec, const Options &opt = Options{}) noexcept; 260 - 261 - template<typename IterT> 262 - std::vector<basic_object<typename std::iterator_traits<IterT>::value_type>> read(IterT first, IterT last, std::error_code& ec, const Options &opt = Options{}) noexcept; 263 - 264 - 265 - ///////////////////////////////////////////////////////////////////////////// 266 - // Writer functions 267 - /// writes given obj into out in vdf style 268 - /// Output is prettyfied, using tabs 269 - template<typename oStreamT, typename T> 270 - void write(oStreamT& out, const T& obj, const Options &opt = Options{}); 271 - 272 - ``` 273 - 274 - ## Remarks for Errors 275 - The current version is a greedy implementation and jumps over unrecognized fields. 276 - Therefore, the error detection is very imprecise an does not give the line, where the error occurs. 277 - 278 - ## License 279 - 280 - [MIT License](./LICENSE) © Matthias Möller. Made with ♥ in Germany.
···
+677 -583
src/external/valve-file-vdf/vdf_parser.hpp
··· 1 - //MIT License 2 // 3 - //Copyright(c) 2016 Matthias Moeller 4 // 5 - //Permission is hereby granted, free of charge, to any person obtaining a copy 6 - //of this software and associated documentation files(the "Software"), to deal 7 - //in the Software without restriction, including without limitation the rights 8 - //to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 9 - //copies of the Software, and to permit persons to whom the Software is 10 - //furnished to do so, subject to the following conditions : 11 // 12 - //The above copyright notice and this permission notice shall be included in all 13 - //copies or substantial portions of the Software. 14 // 15 - //THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 - //IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 - //FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 - //AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 - //LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 - //OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 - //SOFTWARE. 22 23 #ifndef __TYTI_STEAM_VDF_PARSER_H__ 24 #define __TYTI_STEAM_VDF_PARSER_H__ 25 26 #include <map> 27 - #include <vector> 28 #include <unordered_map> 29 - #include <utility> 30 - #include <fstream> 31 - #include <memory> 32 #include <unordered_set> 33 - #include <algorithm> 34 - #include <iterator> 35 - #include <functional> 36 37 - #include <system_error> 38 #include <exception> 39 40 - //for wstring support 41 #include <locale> 42 #include <string> 43 44 // internal 45 #include <stack> 46 47 - //VS < 2015 has only partial C++11 support 48 #if defined(_MSC_VER) && _MSC_VER < 1900 49 #ifndef CONSTEXPR 50 #define CONSTEXPR ··· 68 69 namespace tyti 70 { 71 - namespace vdf 72 { 73 - namespace detail 74 - { 75 - /////////////////////////////////////////////////////////////////////////// 76 - // Helper functions selecting the right encoding (char/wchar_T) 77 - /////////////////////////////////////////////////////////////////////////// 78 79 - template <typename T> 80 - struct literal_macro_help 81 - { 82 - static CONSTEXPR const char* result(const char* c, const wchar_t*) NOEXCEPT 83 - { 84 - return c; 85 - } 86 - static CONSTEXPR const char result(const char c, const wchar_t) NOEXCEPT 87 - { 88 - return c; 89 - } 90 - }; 91 92 - template <> 93 - struct literal_macro_help<wchar_t> 94 - { 95 - static CONSTEXPR const wchar_t* result(const char*, const wchar_t* wc) NOEXCEPT 96 - { 97 - return wc; 98 - } 99 - static CONSTEXPR const wchar_t result(const char, const wchar_t wc) NOEXCEPT 100 - { 101 - return wc; 102 - } 103 - }; 104 - #define TYTI_L(type, text) vdf::detail::literal_macro_help<type>::result(text, L##text) 105 106 - inline std::string string_converter(const std::string& w) NOEXCEPT 107 - { 108 - return w; 109 - } 110 111 - // utility wrapper to adapt locale-bound facets for wstring/wbuffer convert 112 - // from cppreference 113 - template <class Facet> 114 - struct deletable_facet : Facet 115 - { 116 - template <class... Args> 117 - deletable_facet(Args &&... args) : Facet(std::forward<Args>(args)...) {} 118 - ~deletable_facet() {} 119 - }; 120 121 - inline std::string string_converter(const std::wstring& w) //todo: use us-locale 122 - { 123 - std::wstring_convert<deletable_facet<std::codecvt<wchar_t, char, std::mbstate_t>>> conv1; 124 - return conv1.to_bytes(w); 125 - } 126 127 - /////////////////////////////////////////////////////////////////////////// 128 - // Writer helper functions 129 - /////////////////////////////////////////////////////////////////////////// 130 131 - template <typename charT> 132 - class tabs 133 - { 134 - const size_t t; 135 136 - public: 137 - explicit CONSTEXPR tabs(size_t i) NOEXCEPT : t(i) {} 138 - std::basic_string<charT> print() const { return std::basic_string<charT>(t, TYTI_L(charT, '\t')); } 139 - inline CONSTEXPR tabs operator+(size_t i) const NOEXCEPT 140 - { 141 - return tabs(t + i); 142 - } 143 - }; 144 145 - template <typename oStreamT> 146 - oStreamT& operator<<(oStreamT& s, const tabs<typename oStreamT::char_type> t) 147 - { 148 - s << t.print(); 149 - return s; 150 - } 151 - } // end namespace detail 152 153 - /////////////////////////////////////////////////////////////////////////// 154 - // Interface 155 - /////////////////////////////////////////////////////////////////////////// 156 157 - /// custom objects and their corresponding write functions 158 159 - /// basic object node. Every object has a name and can contains attributes saved as key_value pairs or childrens 160 - template <typename CharT> 161 - struct basic_object 162 - { 163 - typedef CharT char_type; 164 - std::basic_string<char_type> name; 165 - std::unordered_map<std::basic_string<char_type>, std::basic_string<char_type>> attribs; 166 - std::unordered_map<std::basic_string<char_type>, std::shared_ptr<basic_object<char_type>>> children; 167 168 - void add_attribute(std::basic_string<char_type> key, std::basic_string<char_type> value) 169 - { 170 - attribs.emplace(std::move(key), std::move(value)); 171 - } 172 - void add_child(std::unique_ptr<basic_object<char_type>> child) 173 - { 174 - std::shared_ptr<basic_object<char_type>> obj{ child.release() }; 175 - children.emplace(obj->name, obj); 176 - } 177 - void set_name(std::basic_string<char_type> n) 178 - { 179 - name = std::move(n); 180 - } 181 - }; 182 183 - template <typename CharT> 184 - struct basic_multikey_object 185 - { 186 - typedef CharT char_type; 187 - std::basic_string<char_type> name; 188 - std::unordered_multimap<std::basic_string<char_type>, std::basic_string<char_type>> attribs; 189 - std::unordered_multimap<std::basic_string<char_type>, std::shared_ptr<basic_multikey_object<char_type>>> children; 190 - 191 - void add_attribute(std::basic_string<char_type> key, std::basic_string<char_type> value) 192 - { 193 - attribs.emplace(std::move(key), std::move(value)); 194 - } 195 - void add_child(std::unique_ptr<basic_multikey_object<char_type>> child) 196 - { 197 - std::shared_ptr<basic_multikey_object<char_type>> obj{ child.release() }; 198 - children.emplace(obj->name, obj); 199 - } 200 - void set_name(std::basic_string<char_type> n) 201 - { 202 - name = std::move(n); 203 - } 204 - }; 205 206 - typedef basic_object<char> object; 207 - typedef basic_object<wchar_t> wobject; 208 - typedef basic_multikey_object<char> multikey_object; 209 - typedef basic_multikey_object<wchar_t> wmultikey_object; 210 211 - struct Options 212 - { 213 - bool strip_escape_symbols; 214 - bool ignore_all_platform_conditionals; 215 - bool ignore_includes; 216 217 - Options() : strip_escape_symbols(true), ignore_all_platform_conditionals(false), ignore_includes(false) {} 218 - }; 219 220 - //forward decls 221 - //forward decl 222 - template <typename OutputT, typename iStreamT> 223 - OutputT read(iStreamT& inStream, const Options& opt = Options{}); 224 225 - /** \brief writes given object tree in vdf format to given stream. 226 - Output is prettyfied, using tabs 227 - */ 228 - template <typename oStreamT, typename T> 229 - void write(oStreamT& s, const T& r, 230 - const detail::tabs<typename oStreamT::char_type> tab = detail::tabs<typename oStreamT::char_type>(0)) 231 - { 232 - typedef typename oStreamT::char_type charT; 233 - using namespace detail; 234 - s << tab << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tab << TYTI_L(charT, "{\n"); 235 - for (const auto& i : r.attribs) 236 - s << tab + 1 << TYTI_L(charT, '"') << i.first << TYTI_L(charT, "\"\t\t\"") << i.second << TYTI_L(charT, "\"\n"); 237 - for (const auto& i : r.children) 238 - if (i.second) 239 - write(s, *i.second, tab + 1); 240 - s << tab << TYTI_L(charT, "}\n"); 241 - } 242 243 - namespace detail 244 - { 245 - template <typename iStreamT> 246 - std::basic_string<typename iStreamT::char_type> read_file(iStreamT& inStream) 247 - { 248 - // cache the file 249 - typedef typename iStreamT::char_type charT; 250 - std::basic_string<charT> str; 251 - inStream.seekg(0, std::ios::end); 252 - str.resize(static_cast<size_t>(inStream.tellg())); 253 - if (str.empty()) 254 - return str; 255 256 - inStream.seekg(0, std::ios::beg); 257 - inStream.read(&str[0], str.size()); 258 - return str; 259 - } 260 261 - /** \brief Read VDF formatted sequences defined by the range [first, last). 262 - If the file is mailformatted, parser will try to read it until it can. 263 - @param first begin iterator 264 - @param end end iterator 265 - @param exclude_files list of files which cant be included anymore. 266 - prevents circular includes 267 268 - can thow: 269 - - "std::runtime_error" if a parsing error occured 270 - - "std::bad_alloc" if not enough memory coup be allocated 271 - */ 272 - template <typename OutputT, typename IterT> 273 - std::vector<std::unique_ptr<OutputT>> read_internal(IterT first, const IterT last, 274 - std::unordered_set<std::basic_string<typename std::iterator_traits<IterT>::value_type>>& exclude_files, 275 - const Options& opt) 276 - { 277 - static_assert(std::is_default_constructible<OutputT>::value, 278 - "Output Type must be default constructible (provide constructor without arguments)"); 279 - static_assert(std::is_move_constructible<OutputT>::value, 280 - "Output Type must be move constructible"); 281 282 - typedef typename std::iterator_traits<IterT>::value_type charT; 283 284 - const std::basic_string<charT> comment_end_str = TYTI_L(charT, "*/"); 285 - const std::basic_string<charT> whitespaces = TYTI_L(charT, " \n\v\f\r\t"); 286 287 #ifdef WIN32 288 - std::function<bool(const std::basic_string<charT>&)> is_platform_str = [](const std::basic_string<charT>& in) { 289 - return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$WINDOWS"); 290 - }; 291 #elif __APPLE__ 292 - // WIN32 stands for pc in general 293 - std::function<bool(const std::basic_string<charT>&)> is_platform_str = [](const std::basic_string<charT>& in) { 294 - return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || in == TYTI_L(charT, "$OSX"); 295 - }; 296 297 #elif __linux__ 298 - // WIN32 stands for pc in general 299 - std::function<bool(const std::basic_string<charT>&)> is_platform_str = [](const std::basic_string<charT>& in) { 300 - return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || in == TYTI_L(charT, "$LINUX"); 301 - }; 302 #else 303 - std::function<bool(const std::basic_string<charT>&)> is_platform_str = [](const std::basic_string<charT>& in) { 304 - return false; 305 - }; 306 #endif 307 308 - if (opt.ignore_all_platform_conditionals) 309 - is_platform_str = [](const std::basic_string<charT>&) { 310 - return false; 311 - }; 312 313 - // function for skipping a comment block 314 - // iter: iterator poition to the position after a '/' 315 - auto skip_comments = [&comment_end_str](IterT iter, const IterT& last) -> IterT { 316 - ++iter; 317 - if (iter != last) 318 - { 319 - if (*iter == TYTI_L(charT, '/')) 320 - { 321 - // line comment, skip whole line 322 - iter = std::find(iter + 1, last, TYTI_L(charT, '\n')); 323 - } 324 325 - if (*iter == '*') 326 - { 327 - // block comment, skip until next occurance of "*\" 328 - iter = std::search(iter + 1, last, std::begin(comment_end_str), std::end(comment_end_str)); 329 - iter += 2; 330 - } 331 - } 332 - return iter; 333 - }; 334 335 - auto end_quote = [](IterT iter, const IterT& last) -> IterT { 336 - const auto begin = iter; 337 - auto last_esc = iter; 338 - do 339 - { 340 - ++iter; 341 - iter = std::find(iter, last, TYTI_L(charT, '\"')); 342 - if (iter == last) 343 - break; 344 345 - last_esc = std::prev(iter); 346 - while (last_esc != begin && *last_esc == '\\') 347 - --last_esc; 348 - } while (!(std::distance(last_esc, iter) % 2)); 349 - if (iter == last) 350 - throw std::runtime_error{ "quote was opened but not closed." }; 351 - return iter; 352 - }; 353 354 - auto end_word = [&whitespaces](IterT iter, const IterT& last) -> IterT { 355 - const auto begin = iter; 356 - auto last_esc = iter; 357 - do 358 - { 359 - ++iter; 360 - iter = std::find_first_of(iter, last, std::begin(whitespaces), std::end(whitespaces)); 361 - if (iter == last) 362 - break; 363 364 - last_esc = std::prev(iter); 365 - while (last_esc != begin && *last_esc == '\\') 366 - --last_esc; 367 - } while (!(std::distance(last_esc, iter) % 2)); 368 - //if (iter == last) 369 - // throw std::runtime_error{ "word wasnt properly ended" }; 370 - return iter; 371 - }; 372 373 - auto skip_whitespaces = [&whitespaces](IterT iter, const IterT& last) -> IterT { 374 - iter = std::find_if_not(iter, last, [&whitespaces](charT c) { 375 - // return true if whitespace 376 - return std::any_of(std::begin(whitespaces), std::end(whitespaces), [c](charT pc) { return pc == c; }); 377 - }); 378 - return iter; 379 - }; 380 381 - std::function<void(std::basic_string<charT>&)> strip_escape_symbols = [](std::basic_string<charT>& s) { 382 - auto quote_searcher = [&s](size_t pos) { return s.find(TYTI_L(charT, "\\\""), pos); }; 383 - auto p = quote_searcher(0); 384 - while (p != s.npos) 385 - { 386 - s.replace(p, 2, TYTI_L(charT, "\"")); 387 - p = quote_searcher(p); 388 - } 389 - auto searcher = [&s](size_t pos) { return s.find(TYTI_L(charT, "\\\\"), pos); }; 390 - p = searcher(0); 391 - while (p != s.npos) 392 - { 393 - s.replace(p, 2, TYTI_L(charT, "\\")); 394 - p = searcher(p); 395 - } 396 - }; 397 398 - if (!opt.strip_escape_symbols) 399 - strip_escape_symbols = [](std::basic_string<charT>&) {}; 400 401 - auto conditional_fullfilled = [&skip_whitespaces, &is_platform_str](IterT& iter, const IterT& last) { 402 - iter = skip_whitespaces(iter, last); 403 - if (*iter == '[') 404 - { 405 - ++iter; 406 - const auto end = std::find(iter, last, ']'); 407 - const bool negate = *iter == '!'; 408 - if (negate) 409 - ++iter; 410 - auto conditional = std::basic_string<charT>(iter, end); 411 412 - const bool is_platform = is_platform_str(conditional); 413 - iter = end + 1; 414 415 - return static_cast<bool>(is_platform ^ negate); 416 - } 417 - return true; 418 - }; 419 420 - //read header 421 - // first, quoted name 422 - std::unique_ptr<OutputT> curObj = nullptr; 423 - std::vector<std::unique_ptr<OutputT>> roots; 424 - std::stack<std::unique_ptr<OutputT>> lvls; 425 - auto curIter = first; 426 427 - while (curIter != last && *curIter != '\0') 428 - { 429 - //find first starting attrib/child, or ending 430 - curIter = skip_whitespaces(curIter, last); 431 - if (curIter == last || *curIter == '\0') 432 - break; 433 - if (*curIter == TYTI_L(charT, '/')) 434 - { 435 - curIter = skip_comments(curIter, last); 436 - } 437 - else if (*curIter != TYTI_L(charT, '}')) 438 - { 439 440 - // get key 441 - const auto keyEnd = (*curIter == TYTI_L(charT, '\"')) ? end_quote(curIter, last) : end_word(curIter, last); 442 - if (*curIter == TYTI_L(charT, '\"')) 443 - ++curIter; 444 - std::basic_string<charT> key(curIter, keyEnd); 445 - strip_escape_symbols(key); 446 - curIter = keyEnd + ((*keyEnd == TYTI_L(charT, '\"')) ? 1 : 0); 447 448 - curIter = skip_whitespaces(curIter, last); 449 450 - auto conditional = conditional_fullfilled(curIter, last); 451 - if (!conditional) 452 - continue; 453 454 - while (*curIter == TYTI_L(charT, '/')) 455 - { 456 457 - curIter = skip_comments(curIter, last); 458 - if (curIter == last || *curIter == '}') 459 - throw std::runtime_error{ "key declared, but no value" }; 460 - curIter = skip_whitespaces(curIter, last); 461 - if (curIter == last || *curIter == '}') 462 - throw std::runtime_error{ "key declared, but no value" }; 463 - } 464 - // get value 465 - if (*curIter != '{') 466 - { 467 - const auto valueEnd = (*curIter == TYTI_L(charT, '\"')) ? end_quote(curIter, last) : end_word(curIter, last); 468 - if (*curIter == TYTI_L(charT, '\"')) 469 - ++curIter; 470 471 - auto value = std::basic_string<charT>(curIter, valueEnd); 472 - strip_escape_symbols(value); 473 - curIter = valueEnd + ((*valueEnd == TYTI_L(charT, '\"')) ? 1 : 0); 474 475 - auto conditional = conditional_fullfilled(curIter, last); 476 - if (!conditional) 477 - continue; 478 479 - // process value 480 - if (key != TYTI_L(charT, "#include") && key != TYTI_L(charT, "#base")) 481 - { 482 - if (curObj) 483 - { 484 - curObj->add_attribute(std::move(key), std::move(value)); 485 - } 486 - else 487 - { 488 - throw std::runtime_error{ "unexpected key without object" }; 489 - } 490 - } 491 - else 492 - { 493 - if (!opt.ignore_includes && exclude_files.find(value) == exclude_files.end()) 494 - { 495 - exclude_files.insert(value); 496 - std::basic_ifstream<charT> i(detail::string_converter(value)); 497 - auto str = read_file(i); 498 - auto file_objs = read_internal<OutputT>(str.begin(), str.end(), exclude_files, opt); 499 - for (auto& n : file_objs) 500 - { 501 - if (curObj) 502 - curObj->add_child(std::move(n)); 503 - else 504 - roots.push_back(std::move(n)); 505 - } 506 - exclude_files.erase(value); 507 - } 508 - } 509 - } 510 - else if (*curIter == '{') 511 - { 512 - if (curObj) 513 - lvls.push(std::move(curObj)); 514 - curObj = std::make_unique<OutputT>(); 515 - curObj->set_name(std::move(key)); 516 - ++curIter; 517 - } 518 - } 519 - //end of new object 520 - else if (curObj && *curIter == TYTI_L(charT, '}')) 521 { 522 - if (!lvls.empty()) 523 - { 524 - //get object before 525 - std::unique_ptr<OutputT> prev{ std::move(lvls.top()) }; 526 - lvls.pop(); 527 - 528 - // add finished obj to obj before and release it from processing 529 - prev->add_child(std::move(curObj)); 530 - curObj = std::move(prev); 531 - } 532 - else 533 - { 534 - roots.push_back(std::move(curObj)); 535 - curObj.reset(); 536 - } 537 - ++curIter; 538 } 539 else 540 { 541 - throw std::runtime_error{ "unexpected '}'" }; 542 } 543 } 544 - if (curObj != nullptr || !lvls.empty()) 545 { 546 - throw std::runtime_error{ "object is not closed with '}'" }; 547 } 548 - 549 - return roots; 550 } 551 - 552 - } // namespace detail 553 - 554 - /** \brief Read VDF formatted sequences defined by the range [first, last). 555 - If the file is mailformatted, parser will try to read it until it can. 556 - @param first begin iterator 557 - @param end end iterator 558 - 559 - can thow: 560 - - "std::runtime_error" if a parsing error occured 561 - - "std::bad_alloc" if not enough memory coup be allocated 562 - */ 563 - template <typename OutputT, typename IterT> 564 - OutputT read(IterT first, const IterT last, const Options& opt = Options{}) 565 - { 566 - auto exclude_files = std::unordered_set<std::basic_string<typename std::iterator_traits<IterT>::value_type>>{}; 567 - auto roots = detail::read_internal<OutputT>(first, last, exclude_files, opt); 568 - 569 - OutputT result; 570 - if (roots.size() > 1) 571 { 572 - for (auto& i : roots) 573 - result.add_child(std::move(i)); 574 } 575 - else if (roots.size() == 1) 576 - result = std::move(*roots[0]); 577 - 578 - return result; 579 } 580 - 581 - /** \brief Read VDF formatted sequences defined by the range [first, last). 582 - If the file is mailformatted, parser will try to read it until it can. 583 - @param first begin iterator 584 - @param end end iterator 585 - @param ec output bool. 0 if ok, otherwise, holds an system error code 586 - 587 - Possible error codes: 588 - std::errc::protocol_error: file is mailformatted 589 - std::errc::not_enough_memory: not enough space 590 - std::errc::invalid_argument: iterators throws e.g. out of range 591 - */ 592 - template <typename OutputT, typename IterT> 593 - OutputT read(IterT first, IterT last, std::error_code& ec, const Options& opt = Options{}) NOEXCEPT 594 - 595 { 596 - ec.clear(); 597 - OutputT r{}; 598 - try 599 { 600 - r = read<OutputT>(first, last, opt); 601 } 602 - catch (std::runtime_error&) 603 { 604 - ec = std::make_error_code(std::errc::protocol_error); 605 - } 606 - catch (std::bad_alloc&) 607 - { 608 - ec = std::make_error_code(std::errc::not_enough_memory); 609 - } 610 - catch (...) 611 - { 612 - ec = std::make_error_code(std::errc::invalid_argument); 613 } 614 - return r; 615 } 616 - 617 - /** \brief Read VDF formatted sequences defined by the range [first, last). 618 - If the file is mailformatted, parser will try to read it until it can. 619 - @param first begin iterator 620 - @param end end iterator 621 - @param ok output bool. true, if parser successed, false, if parser failed 622 - */ 623 - template <typename OutputT, typename IterT> 624 - OutputT read(IterT first, const IterT last, bool* ok, const Options& opt = Options{}) NOEXCEPT 625 { 626 - std::error_code ec; 627 - auto r = read<OutputT>(first, last, ec, opt); 628 - if (ok) 629 - *ok = !ec; 630 - return r; 631 } 632 633 - template <typename IterT> 634 - inline auto read(IterT first, const IterT last, bool* ok, const Options& opt = Options{}) NOEXCEPT -> basic_object<typename std::iterator_traits<IterT>::value_type> 635 - { 636 - return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(first, last, ok, opt); 637 - } 638 639 - template <typename IterT> 640 - inline auto read(IterT first, IterT last, std::error_code& ec, const Options& opt = Options{}) NOEXCEPT 641 - -> basic_object<typename std::iterator_traits<IterT>::value_type> 642 - { 643 - return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(first, last, ec, opt); 644 - } 645 646 - template <typename IterT> 647 - inline auto read(IterT first, const IterT last, const Options& opt = Options{}) 648 - -> basic_object<typename std::iterator_traits<IterT>::value_type> 649 - { 650 - return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(first, last, opt); 651 - } 652 653 - /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. 654 - throws "std::bad_alloc" if file buffer could not be allocated 655 - */ 656 - template <typename OutputT, typename iStreamT> 657 - OutputT read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{}) 658 - { 659 - // cache the file 660 - typedef typename iStreamT::char_type charT; 661 - std::basic_string<charT> str = detail::read_file(inStream); 662 663 - // parse it 664 - return read<OutputT>(str.begin(), str.end(), ec, opt); 665 - } 666 667 - template <typename iStreamT> 668 - inline basic_object<typename iStreamT::char_type> read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{}) 669 - { 670 - return read<basic_object<typename iStreamT::char_type>>(inStream, ec, opt); 671 - } 672 673 - /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. 674 - throws "std::bad_alloc" if file buffer could not be allocated 675 - ok == false, if a parsing error occured 676 - */ 677 - template <typename OutputT, typename iStreamT> 678 - OutputT read(iStreamT& inStream, bool* ok, const Options& opt = Options{}) 679 - { 680 - std::error_code ec; 681 - const auto r = read<OutputT>(inStream, ec, opt); 682 - if (ok) 683 - *ok = !ec; 684 - return r; 685 - } 686 687 - template <typename iStreamT> 688 - inline basic_object<typename iStreamT::char_type> read(iStreamT& inStream, bool* ok, const Options& opt = Options{}) 689 - { 690 - return read<basic_object<typename iStreamT::char_type>>(inStream, ok, opt); 691 - } 692 693 - /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. 694 - throws "std::bad_alloc" if file buffer could not be allocated 695 - throws "std::runtime_error" if a parsing error occured 696 - */ 697 - template <typename OutputT, typename iStreamT> 698 - OutputT read(iStreamT& inStream, const Options& opt) 699 - { 700 701 - // cache the file 702 - typedef typename iStreamT::char_type charT; 703 - std::basic_string<charT> str = detail::read_file(inStream); 704 - // parse it 705 - return read<OutputT>(str.begin(), str.end(), opt); 706 - } 707 708 - template <typename iStreamT> 709 - inline basic_object<typename iStreamT::char_type> read(iStreamT& inStream, const Options& opt = Options{}) 710 - { 711 - return read<basic_object<typename iStreamT::char_type>>(inStream, opt); 712 - } 713 714 - } // namespace vdf 715 } // namespace tyti 716 #ifndef TYTI_NO_L_UNDEF 717 #undef TYTI_L
··· 1 + // MIT License 2 // 3 + // Copyright(c) 2016 Matthias Moeller 4 // 5 + // Permission is hereby granted, free of charge, to any person obtaining a copy 6 + // of this software and associated documentation files(the "Software"), to deal 7 + // in the Software without restriction, including without limitation the rights 8 + // to use, copy, modify, merge, publish, distribute, sublicense, and / or sell 9 + // copies of the Software, and to permit persons to whom the Software is 10 + // furnished to do so, subject to the following conditions : 11 // 12 + // The above copyright notice and this permission notice shall be included in 13 + // all copies or substantial portions of the Software. 14 // 15 + // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 + // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 + // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE 18 + // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 + // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 + // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 + // SOFTWARE. 22 23 #ifndef __TYTI_STEAM_VDF_PARSER_H__ 24 #define __TYTI_STEAM_VDF_PARSER_H__ 25 26 + #include <algorithm> 27 + #include <fstream> 28 + #include <functional> 29 + #include <iterator> 30 #include <map> 31 + #include <memory> 32 #include <unordered_map> 33 #include <unordered_set> 34 + #include <utility> 35 + #include <vector> 36 37 #include <exception> 38 + #include <system_error> 39 40 + // for wstring support 41 #include <locale> 42 #include <string> 43 44 // internal 45 #include <stack> 46 47 + // VS < 2015 has only partial C++11 support 48 #if defined(_MSC_VER) && _MSC_VER < 1900 49 #ifndef CONSTEXPR 50 #define CONSTEXPR ··· 68 69 namespace tyti 70 { 71 + namespace vdf 72 + { 73 + namespace detail 74 + { 75 + /////////////////////////////////////////////////////////////////////////// 76 + // Helper functions selecting the right encoding (char/wchar_T) 77 + /////////////////////////////////////////////////////////////////////////// 78 + 79 + template <typename T> struct literal_macro_help 80 + { 81 + static CONSTEXPR const char *result(const char *c, const wchar_t *) NOEXCEPT 82 + { 83 + return c; 84 + } 85 + static CONSTEXPR const char result(const char c, const wchar_t) NOEXCEPT 86 { 87 + return c; 88 + } 89 + }; 90 91 + template <> struct literal_macro_help<wchar_t> 92 + { 93 + static CONSTEXPR const wchar_t *result(const char *, 94 + const wchar_t *wc) NOEXCEPT 95 + { 96 + return wc; 97 + } 98 + static CONSTEXPR const wchar_t result(const char, const wchar_t wc) NOEXCEPT 99 + { 100 + return wc; 101 + } 102 + }; 103 + #define TYTI_L(type, text) \ 104 + vdf::detail::literal_macro_help<type>::result(text, L##text) 105 106 + inline std::string string_converter(const std::string &w) NOEXCEPT { return w; } 107 108 + // utility wrapper to adapt locale-bound facets for wstring/wbuffer convert 109 + // from cppreference 110 + template <class Facet> struct deletable_facet : Facet 111 + { 112 + template <class... Args> 113 + deletable_facet(Args &&...args) : Facet(std::forward<Args>(args)...) 114 + { 115 + } 116 + ~deletable_facet() {} 117 + }; 118 119 + inline std::string 120 + string_converter(const std::wstring &w) // todo: use us-locale 121 + { 122 + std::wstring_convert< 123 + deletable_facet<std::codecvt<wchar_t, char, std::mbstate_t>>> 124 + conv1; 125 + return conv1.to_bytes(w); 126 + } 127 128 + /////////////////////////////////////////////////////////////////////////// 129 + // Writer helper functions 130 + /////////////////////////////////////////////////////////////////////////// 131 132 + template <typename charT> class tabs 133 + { 134 + const size_t t; 135 136 + public: 137 + explicit CONSTEXPR tabs(size_t i) NOEXCEPT : t(i) {} 138 + std::basic_string<charT> print() const 139 + { 140 + return std::basic_string<charT>(t, TYTI_L(charT, '\t')); 141 + } 142 + inline CONSTEXPR tabs operator+(size_t i) const NOEXCEPT 143 + { 144 + return tabs(t + i); 145 + } 146 + }; 147 148 + template <typename oStreamT> 149 + oStreamT &operator<<(oStreamT &s, const tabs<typename oStreamT::char_type> t) 150 + { 151 + s << t.print(); 152 + return s; 153 + } 154 + } // end namespace detail 155 156 + /////////////////////////////////////////////////////////////////////////// 157 + // Interface 158 + /////////////////////////////////////////////////////////////////////////// 159 160 + /// custom objects and their corresponding write functions 161 162 + /// basic object node. Every object has a name and can contains attributes saved 163 + /// as key_value pairs or childrens 164 + template <typename CharT> struct basic_object 165 + { 166 + typedef CharT char_type; 167 + std::basic_string<char_type> name; 168 + std::unordered_map<std::basic_string<char_type>, 169 + std::basic_string<char_type>> 170 + attribs; 171 + std::unordered_map<std::basic_string<char_type>, 172 + std::shared_ptr<basic_object<char_type>>> 173 + childs; 174 175 + void add_attribute(std::basic_string<char_type> key, 176 + std::basic_string<char_type> value) 177 + { 178 + attribs.emplace(std::move(key), std::move(value)); 179 + } 180 + void add_child(std::unique_ptr<basic_object<char_type>> child) 181 + { 182 + std::shared_ptr<basic_object<char_type>> obj{child.release()}; 183 + childs.emplace(obj->name, obj); 184 + } 185 + void set_name(std::basic_string<char_type> n) { name = std::move(n); } 186 + }; 187 188 + template <typename CharT> struct basic_multikey_object 189 + { 190 + typedef CharT char_type; 191 + std::basic_string<char_type> name; 192 + std::unordered_multimap<std::basic_string<char_type>, 193 + std::basic_string<char_type>> 194 + attribs; 195 + std::unordered_multimap<std::basic_string<char_type>, 196 + std::shared_ptr<basic_multikey_object<char_type>>> 197 + childs; 198 199 + void add_attribute(std::basic_string<char_type> key, 200 + std::basic_string<char_type> value) 201 + { 202 + attribs.emplace(std::move(key), std::move(value)); 203 + } 204 + void add_child(std::unique_ptr<basic_multikey_object<char_type>> child) 205 + { 206 + std::shared_ptr<basic_multikey_object<char_type>> obj{child.release()}; 207 + childs.emplace(obj->name, obj); 208 + } 209 + void set_name(std::basic_string<char_type> n) { name = std::move(n); } 210 + }; 211 212 + typedef basic_object<char> object; 213 + typedef basic_object<wchar_t> wobject; 214 + typedef basic_multikey_object<char> multikey_object; 215 + typedef basic_multikey_object<wchar_t> wmultikey_object; 216 217 + struct Options 218 + { 219 + bool strip_escape_symbols; 220 + bool ignore_all_platform_conditionals; 221 + bool ignore_includes; 222 223 + Options() 224 + : strip_escape_symbols(true), ignore_all_platform_conditionals(false), 225 + ignore_includes(false) 226 + { 227 + } 228 + }; 229 230 + // forward decls 231 + // forward decl 232 + template <typename OutputT, typename iStreamT> 233 + OutputT read(iStreamT &inStream, const Options &opt = Options{}); 234 235 + /** \brief writes given object tree in vdf format to given stream. 236 + Output is prettyfied, using tabs 237 + */ 238 + template <typename oStreamT, typename T> 239 + void write(oStreamT &s, const T &r, 240 + const detail::tabs<typename oStreamT::char_type> tab = 241 + detail::tabs<typename oStreamT::char_type>(0)) 242 + { 243 + typedef typename oStreamT::char_type charT; 244 + using namespace detail; 245 + s << tab << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tab 246 + << TYTI_L(charT, "{\n"); 247 + for (const auto &i : r.attribs) 248 + s << tab + 1 << TYTI_L(charT, '"') << i.first 249 + << TYTI_L(charT, "\"\t\t\"") << i.second << TYTI_L(charT, "\"\n"); 250 + for (const auto &i : r.childs) 251 + if (i.second) 252 + write(s, *i.second, tab + 1); 253 + s << tab << TYTI_L(charT, "}\n"); 254 + } 255 256 + namespace detail 257 + { 258 + template <typename iStreamT> 259 + std::basic_string<typename iStreamT::char_type> read_file(iStreamT &inStream) 260 + { 261 + // cache the file 262 + typedef typename iStreamT::char_type charT; 263 + std::basic_string<charT> str; 264 + inStream.seekg(0, std::ios::end); 265 + str.resize(static_cast<size_t>(inStream.tellg())); 266 + if (str.empty()) 267 + return str; 268 269 + inStream.seekg(0, std::ios::beg); 270 + inStream.read(&str[0], str.size()); 271 + return str; 272 + } 273 274 + /** \brief Read VDF formatted sequences defined by the range [first, last). 275 + If the file is mailformatted, parser will try to read it until it can. 276 + @param first begin iterator 277 + @param end end iterator 278 + @param exclude_files list of files which cant be included anymore. 279 + prevents circular includes 280 281 + can thow: 282 + - "std::runtime_error" if a parsing error occured 283 + - "std::bad_alloc" if not enough memory coup be allocated 284 + */ 285 + template <typename OutputT, typename IterT> 286 + std::vector<std::unique_ptr<OutputT>> read_internal( 287 + IterT first, const IterT last, 288 + std::unordered_set< 289 + std::basic_string<typename std::iterator_traits<IterT>::value_type>> 290 + &exclude_files, 291 + const Options &opt) 292 + { 293 + static_assert(std::is_default_constructible<OutputT>::value, 294 + "Output Type must be default constructible (provide " 295 + "constructor without arguments)"); 296 + static_assert(std::is_move_constructible<OutputT>::value, 297 + "Output Type must be move constructible"); 298 299 + typedef typename std::iterator_traits<IterT>::value_type charT; 300 301 + const std::basic_string<charT> comment_end_str = TYTI_L(charT, "*/"); 302 + const std::basic_string<charT> whitespaces = TYTI_L(charT, " \n\v\f\r\t"); 303 304 #ifdef WIN32 305 + std::function<bool(const std::basic_string<charT> &)> is_platform_str = 306 + [](const std::basic_string<charT> &in) 307 + { 308 + return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$WINDOWS"); 309 + }; 310 #elif __APPLE__ 311 + // WIN32 stands for pc in general 312 + std::function<bool(const std::basic_string<charT> &)> is_platform_str = 313 + [](const std::basic_string<charT> &in) 314 + { 315 + return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || 316 + in == TYTI_L(charT, "$OSX"); 317 + }; 318 319 #elif __linux__ 320 + // WIN32 stands for pc in general 321 + std::function<bool(const std::basic_string<charT> &)> is_platform_str = 322 + [](const std::basic_string<charT> &in) 323 + { 324 + return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || 325 + in == TYTI_L(charT, "$LINUX"); 326 + }; 327 #else 328 + std::function<bool(const std::basic_string<charT> &)> is_platform_str = 329 + [](const std::basic_string<charT> &in) { return false; }; 330 #endif 331 332 + if (opt.ignore_all_platform_conditionals) 333 + is_platform_str = [](const std::basic_string<charT> &) 334 + { return false; }; 335 336 + // function for skipping a comment block 337 + // iter: iterator poition to the position after a '/' 338 + auto skip_comments = [&comment_end_str](IterT iter, 339 + const IterT &last) -> IterT 340 + { 341 + ++iter; 342 + if (iter == last) 343 + return last; 344 345 + if (*iter == TYTI_L(charT, '/')) 346 + { 347 + // line comment, skip whole line 348 + iter = std::find(iter + 1, last, TYTI_L(charT, '\n')); 349 + if (iter == last) 350 + return last; 351 + } 352 353 + if (*iter == '*') 354 + { 355 + // block comment, skip until next occurance of "*\" 356 + iter = std::search(iter + 1, last, std::begin(comment_end_str), 357 + std::end(comment_end_str)); 358 + if (std::distance(iter, last) <= 2) 359 + return last; 360 + iter += 2; 361 + } 362 363 + return iter; 364 + }; 365 366 + auto end_quote = [](IterT iter, const IterT &last) -> IterT 367 + { 368 + const auto begin = iter; 369 + auto last_esc = iter; 370 + if (iter == last) 371 + throw std::runtime_error{"quote was opened but not closed."}; 372 + do 373 + { 374 + ++iter; 375 + iter = std::find(iter, last, TYTI_L(charT, '\"')); 376 + if (iter == last) 377 + break; 378 379 + last_esc = std::prev(iter); 380 + while (last_esc != begin && *last_esc == '\\') 381 + --last_esc; 382 + } while (!(std::distance(last_esc, iter) % 2) && iter != last); 383 + if (iter == last) 384 + throw std::runtime_error{"quote was opened but not closed."}; 385 + return iter; 386 + }; 387 388 + auto end_word = [&whitespaces](IterT iter, const IterT &last) -> IterT 389 + { 390 + const auto begin = iter; 391 + auto last_esc = iter; 392 + if (iter == last) 393 + throw std::runtime_error{"quote was opened but not closed."}; 394 + do 395 + { 396 + ++iter; 397 + iter = std::find_first_of(iter, last, std::begin(whitespaces), 398 + std::end(whitespaces)); 399 + if (iter == last) 400 + break; 401 402 + last_esc = std::prev(iter); 403 + while (last_esc != begin && *last_esc == '\\') 404 + --last_esc; 405 + } while (!(std::distance(last_esc, iter) % 2) && iter != last); 406 + if (iter == last) 407 + throw std::runtime_error{"word wasnt properly ended"}; 408 + return iter; 409 + }; 410 411 + auto skip_whitespaces = [&whitespaces](IterT iter, 412 + const IterT &last) -> IterT 413 + { 414 + if (iter == last) 415 + return iter; 416 + iter = std::find_if_not(iter, last, 417 + [&whitespaces](charT c) 418 + { 419 + // return true if whitespace 420 + return std::any_of(std::begin(whitespaces), 421 + std::end(whitespaces), 422 + [c](charT pc) 423 + { return pc == c; }); 424 + }); 425 + return iter; 426 + }; 427 428 + std::function<void(std::basic_string<charT> &)> strip_escape_symbols = 429 + [](std::basic_string<charT> &s) 430 + { 431 + auto quote_searcher = [&s](size_t pos) 432 + { return s.find(TYTI_L(charT, "\\\""), pos); }; 433 + auto p = quote_searcher(0); 434 + while (p != s.npos) 435 + { 436 + s.replace(p, 2, TYTI_L(charT, "\"")); 437 + p = quote_searcher(p); 438 + } 439 + auto searcher = [&s](size_t pos) 440 + { return s.find(TYTI_L(charT, "\\\\"), pos); }; 441 + p = searcher(0); 442 + while (p != s.npos) 443 + { 444 + s.replace(p, 2, TYTI_L(charT, "\\")); 445 + p = searcher(p); 446 + } 447 + }; 448 449 + if (!opt.strip_escape_symbols) 450 + strip_escape_symbols = [](std::basic_string<charT> &) {}; 451 452 + auto conditional_fullfilled = 453 + [&skip_whitespaces, &is_platform_str](IterT &iter, const IterT &last) 454 + { 455 + iter = skip_whitespaces(iter, last); 456 + if (iter == last) 457 + return true; 458 + if (*iter == '[') 459 + { 460 + ++iter; 461 + if (iter == last) 462 + throw std::runtime_error("conditional not closed"); 463 + const auto end = std::find(iter, last, ']'); 464 + if (end == last) 465 + throw std::runtime_error("conditional not closed"); 466 + const bool negate = *iter == '!'; 467 + if (negate) 468 + ++iter; 469 + auto conditional = std::basic_string<charT>(iter, end); 470 471 + const bool is_platform = is_platform_str(conditional); 472 + iter = end + 1; 473 474 + return static_cast<bool>(is_platform ^ negate); 475 + } 476 + return true; 477 + }; 478 479 + // read header 480 + // first, quoted name 481 + std::unique_ptr<OutputT> curObj = nullptr; 482 + std::vector<std::unique_ptr<OutputT>> roots; 483 + std::stack<std::unique_ptr<OutputT>> lvls; 484 + auto curIter = first; 485 + 486 + while (curIter != last && *curIter != '\0') 487 + { 488 + // find first starting attrib/child, or ending 489 + curIter = skip_whitespaces(curIter, last); 490 + if (curIter == last || *curIter == '\0') 491 + break; 492 + if (*curIter == TYTI_L(charT, '/')) 493 + { 494 + curIter = skip_comments(curIter, last); 495 + if (curIter == last || *curIter == '\0') 496 + throw std::runtime_error("Unexpected eof"); 497 + } 498 + else if (*curIter != TYTI_L(charT, '}')) 499 + { 500 + // get key 501 + const auto keyEnd = (*curIter == TYTI_L(charT, '\"')) 502 + ? end_quote(curIter, last) 503 + : end_word(curIter, last); 504 + if (*curIter == TYTI_L(charT, '\"')) 505 + ++curIter; 506 + std::basic_string<charT> key(curIter, keyEnd); 507 + strip_escape_symbols(key); 508 + curIter = keyEnd + ((*keyEnd == TYTI_L(charT, '\"')) ? 1 : 0); 509 + if (curIter == last) 510 + throw std::runtime_error{"key opened, but never closed"}; 511 512 + curIter = skip_whitespaces(curIter, last); 513 514 + auto conditional = conditional_fullfilled(curIter, last); 515 + if (!conditional) 516 + continue; 517 + if (curIter == last) 518 + throw std::runtime_error{"key declared, but no value"}; 519 520 + while (*curIter == TYTI_L(charT, '/')) 521 + { 522 523 + curIter = skip_comments(curIter, last); 524 + if (curIter == last || *curIter == '}') 525 + throw std::runtime_error{"key declared, but no value"}; 526 + curIter = skip_whitespaces(curIter, last); 527 + if (curIter == last || *curIter == '}') 528 + throw std::runtime_error{"key declared, but no value"}; 529 + } 530 + // get value 531 + if (*curIter != '{') 532 + { 533 + if (curIter == last) 534 + throw std::runtime_error{"key declared, but no value"}; 535 + const auto valueEnd = (*curIter == TYTI_L(charT, '\"')) 536 + ? end_quote(curIter, last) 537 + : end_word(curIter, last); 538 + if (valueEnd == last) 539 + throw std::runtime_error("No closed word"); 540 + if (*curIter == TYTI_L(charT, '\"')) 541 + ++curIter; 542 + if (curIter == last) 543 + throw std::runtime_error("No closed word"); 544 545 + auto value = std::basic_string<charT>(curIter, valueEnd); 546 + strip_escape_symbols(value); 547 + curIter = 548 + valueEnd + ((*valueEnd == TYTI_L(charT, '\"')) ? 1 : 0); 549 550 + auto conditional = conditional_fullfilled(curIter, last); 551 + if (!conditional) 552 + continue; 553 554 + // process value 555 + if (key != TYTI_L(charT, "#include") && 556 + key != TYTI_L(charT, "#base")) 557 + { 558 + if (curObj) 559 { 560 + curObj->add_attribute(std::move(key), std::move(value)); 561 } 562 else 563 { 564 + throw std::runtime_error{ 565 + "unexpected key without object"}; 566 } 567 } 568 + else 569 { 570 + if (!opt.ignore_includes && 571 + exclude_files.find(value) == exclude_files.end()) 572 + { 573 + exclude_files.insert(value); 574 + std::basic_ifstream<charT> i( 575 + detail::string_converter(value)); 576 + auto str = read_file(i); 577 + auto file_objs = read_internal<OutputT>( 578 + str.begin(), str.end(), exclude_files, opt); 579 + for (auto &n : file_objs) 580 + { 581 + if (curObj) 582 + curObj->add_child(std::move(n)); 583 + else 584 + roots.push_back(std::move(n)); 585 + } 586 + exclude_files.erase(value); 587 + } 588 } 589 } 590 + else if (*curIter == '{') 591 { 592 + if (curObj) 593 + lvls.push(std::move(curObj)); 594 + curObj = std::make_unique<OutputT>(); 595 + curObj->set_name(std::move(key)); 596 + ++curIter; 597 } 598 } 599 + // end of new object 600 + else if (curObj && *curIter == TYTI_L(charT, '}')) 601 { 602 + if (!lvls.empty()) 603 { 604 + // get object before 605 + std::unique_ptr<OutputT> prev{std::move(lvls.top())}; 606 + lvls.pop(); 607 + 608 + // add finished obj to obj before and release it from processing 609 + prev->add_child(std::move(curObj)); 610 + curObj = std::move(prev); 611 } 612 + else 613 { 614 + roots.push_back(std::move(curObj)); 615 + curObj.reset(); 616 } 617 + ++curIter; 618 } 619 + else 620 { 621 + throw std::runtime_error{"unexpected '}'"}; 622 } 623 + } 624 + if (curObj != nullptr || !lvls.empty()) 625 + { 626 + throw std::runtime_error{"object is not closed with '}'"}; 627 + } 628 629 + return roots; 630 + } 631 + 632 + } // namespace detail 633 + 634 + /** \brief Read VDF formatted sequences defined by the range [first, last). 635 + If the file is mailformatted, parser will try to read it until it can. 636 + @param first begin iterator 637 + @param end end iterator 638 + 639 + can thow: 640 + - "std::runtime_error" if a parsing error occured 641 + - "std::bad_alloc" if not enough memory coup be allocated 642 + */ 643 + template <typename OutputT, typename IterT> 644 + OutputT read(IterT first, const IterT last, const Options &opt = Options{}) 645 + { 646 + auto exclude_files = std::unordered_set< 647 + std::basic_string<typename std::iterator_traits<IterT>::value_type>>{}; 648 + auto roots = 649 + detail::read_internal<OutputT>(first, last, exclude_files, opt); 650 + 651 + OutputT result; 652 + if (roots.size() > 1) 653 + { 654 + for (auto &i : roots) 655 + result.add_child(std::move(i)); 656 + } 657 + else if (roots.size() == 1) 658 + result = std::move(*roots[0]); 659 + 660 + return result; 661 + } 662 + 663 + /** \brief Read VDF formatted sequences defined by the range [first, last). 664 + If the file is mailformatted, parser will try to read it until it can. 665 + @param first begin iterator 666 + @param end end iterator 667 + @param ec output bool. 0 if ok, otherwise, holds an system error code 668 + 669 + Possible error codes: 670 + std::errc::protocol_error: file is mailformatted 671 + std::errc::not_enough_memory: not enough space 672 + std::errc::invalid_argument: iterators throws e.g. out of range 673 + */ 674 + template <typename OutputT, typename IterT> 675 + OutputT read(IterT first, IterT last, std::error_code &ec, 676 + const Options &opt = Options{}) NOEXCEPT 677 + 678 + { 679 + ec.clear(); 680 + OutputT r{}; 681 + try 682 + { 683 + r = read<OutputT>(first, last, opt); 684 + } 685 + catch (std::runtime_error &) 686 + { 687 + ec = std::make_error_code(std::errc::protocol_error); 688 + } 689 + catch (std::bad_alloc &) 690 + { 691 + ec = std::make_error_code(std::errc::not_enough_memory); 692 + } 693 + catch (...) 694 + { 695 + ec = std::make_error_code(std::errc::invalid_argument); 696 + } 697 + return r; 698 + } 699 + 700 + /** \brief Read VDF formatted sequences defined by the range [first, last). 701 + If the file is mailformatted, parser will try to read it until it can. 702 + @param first begin iterator 703 + @param end end iterator 704 + @param ok output bool. true, if parser successed, false, if parser failed 705 + */ 706 + template <typename OutputT, typename IterT> 707 + OutputT read(IterT first, const IterT last, bool *ok, 708 + const Options &opt = Options{}) NOEXCEPT 709 + { 710 + std::error_code ec; 711 + auto r = read<OutputT>(first, last, ec, opt); 712 + if (ok) 713 + *ok = !ec; 714 + return r; 715 + } 716 717 + template <typename IterT> 718 + inline auto read(IterT first, const IterT last, bool *ok, 719 + const Options &opt = Options{}) NOEXCEPT 720 + ->basic_object<typename std::iterator_traits<IterT>::value_type> 721 + { 722 + return read<basic_object<typename std::iterator_traits<IterT>::value_type>>( 723 + first, last, ok, opt); 724 + } 725 726 + template <typename IterT> 727 + inline auto read(IterT first, IterT last, std::error_code &ec, 728 + const Options &opt = Options{}) NOEXCEPT 729 + ->basic_object<typename std::iterator_traits<IterT>::value_type> 730 + { 731 + return read<basic_object<typename std::iterator_traits<IterT>::value_type>>( 732 + first, last, ec, opt); 733 + } 734 + 735 + template <typename IterT> 736 + inline auto read(IterT first, const IterT last, const Options &opt = Options{}) 737 + -> basic_object<typename std::iterator_traits<IterT>::value_type> 738 + { 739 + return read<basic_object<typename std::iterator_traits<IterT>::value_type>>( 740 + first, last, opt); 741 + } 742 743 + /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf 744 + formatted data. throws "std::bad_alloc" if file buffer could not be allocated 745 + */ 746 + template <typename OutputT, typename iStreamT> 747 + OutputT read(iStreamT &inStream, std::error_code &ec, 748 + const Options &opt = Options{}) 749 + { 750 + // cache the file 751 + typedef typename iStreamT::char_type charT; 752 + std::basic_string<charT> str = detail::read_file(inStream); 753 754 + // parse it 755 + return read<OutputT>(str.begin(), str.end(), ec, opt); 756 + } 757 758 + template <typename iStreamT> 759 + inline basic_object<typename iStreamT::char_type> 760 + read(iStreamT &inStream, std::error_code &ec, const Options &opt = Options{}) 761 + { 762 + return read<basic_object<typename iStreamT::char_type>>(inStream, ec, opt); 763 + } 764 765 + /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf 766 + formatted data. throws "std::bad_alloc" if file buffer could not be allocated 767 + ok == false, if a parsing error occured 768 + */ 769 + template <typename OutputT, typename iStreamT> 770 + OutputT read(iStreamT &inStream, bool *ok, const Options &opt = Options{}) 771 + { 772 + std::error_code ec; 773 + const auto r = read<OutputT>(inStream, ec, opt); 774 + if (ok) 775 + *ok = !ec; 776 + return r; 777 + } 778 779 + template <typename iStreamT> 780 + inline basic_object<typename iStreamT::char_type> 781 + read(iStreamT &inStream, bool *ok, const Options &opt = Options{}) 782 + { 783 + return read<basic_object<typename iStreamT::char_type>>(inStream, ok, opt); 784 + } 785 786 + /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf 787 + formatted data. throws "std::bad_alloc" if file buffer could not be allocated 788 + throws "std::runtime_error" if a parsing error occured 789 + */ 790 + template <typename OutputT, typename iStreamT> 791 + OutputT read(iStreamT &inStream, const Options &opt) 792 + { 793 794 + // cache the file 795 + typedef typename iStreamT::char_type charT; 796 + std::basic_string<charT> str = detail::read_file(inStream); 797 + // parse it 798 + return read<OutputT>(str.begin(), str.end(), opt); 799 + } 800 801 + template <typename iStreamT> 802 + inline basic_object<typename iStreamT::char_type> 803 + read(iStreamT &inStream, const Options &opt = Options{}) 804 + { 805 + return read<basic_object<typename iStreamT::char_type>>(inStream, opt); 806 + } 807 808 + } // namespace vdf 809 } // namespace tyti 810 #ifndef TYTI_NO_L_UNDEF 811 #undef TYTI_L
+2 -2
src/xrt/drivers/steamvr_lh/steamvr_lh.cpp
··· 79 std::ifstream file(STEAM_INSTALL_DIR + "/steamapps/libraryfolders.vdf"); 80 auto root = vdf::read(file); 81 assert(root.name == "libraryfolders"); 82 - for (auto &[_, child] : root.children) { 83 U_LOG_D("Found library folder %s", child->attribs["path"].c_str()); 84 - std::shared_ptr<vdf::object> apps = child->children["apps"]; 85 for (auto &[appid, _] : apps->attribs) { 86 if (appid == STEAMVR_APPID) { 87 return child->attribs["path"] + "/steamapps/common/SteamVR";
··· 79 std::ifstream file(STEAM_INSTALL_DIR + "/steamapps/libraryfolders.vdf"); 80 auto root = vdf::read(file); 81 assert(root.name == "libraryfolders"); 82 + for (auto &[_, child] : root.childs) { 83 U_LOG_D("Found library folder %s", child->attribs["path"].c_str()); 84 + std::shared_ptr<vdf::object> apps = child->childs["apps"]; 85 for (auto &[appid, _] : apps->attribs) { 86 if (appid == STEAMVR_APPID) { 87 return child->attribs["path"] + "/steamapps/common/SteamVR";