diff --git a/scripts/codespell-project.sh b/scripts/codespell-project.sh index 9cb16b592..63c21ae33 100755 --- a/scripts/codespell-project.sh +++ b/scripts/codespell-project.sh @@ -19,7 +19,7 @@ set -e # Comma-delimited list of words for codespell to not try to correct. -IGNORE_WORDS_LIST="ang,sinc,sie,stoll,wil,daa,localy,od,ser,unknwn,parm,inflight,marge,devault,errorprone" +IGNORE_WORDS_LIST="ang,sinc,sie,stoll,wil,daa,localy,od,ser,unknwn,parm,inflight,marge,devault,errorprone,childs" IGNORE_REGEX="\b(pEvent|inout|Kimera)\b" SCRIPTDIR=$(cd "$(dirname "$0")" && pwd) diff --git a/src/external/valve-file-vdf/LICENSE b/src/external/valve-file-vdf/LICENSE new file mode 100644 index 000000000..5c5d47d03 --- /dev/null +++ b/src/external/valve-file-vdf/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Matthias Moeller 2016 m_moeller@live.de + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/external/valve-file-vdf/Readme.md b/src/external/valve-file-vdf/Readme.md deleted file mode 100644 index 6388cd58b..000000000 --- a/src/external/valve-file-vdf/Readme.md +++ /dev/null @@ -1,280 +0,0 @@ -Upstream source - https://github.com/TinyTinni/ValveFileVDF - -# Valve Data Format (.vdf) Reader and Writer in C++ - -[![CMake](https://github.com/TinyTinni/ValveFileVDF/actions/workflows/cmake.yml/badge.svg)](https://github.com/TinyTinni/ValveFileVDF/actions/workflows/cmake.yml) - -Valve uses its own JSON-like data format: [KeyValue, also known as vdf.](https://developer.valvesoftware.com/wiki/KeyValues) -e.g. in game manifest files or as SteamCMD output. -This header-only file provides a parser and writer to load and save the given data. - -## Features: -- read and write vdf data in C++ -- build-in encodings: `char` and `wchar_t` -- supports custom character sets -- support for C++ (//) and C (/**/) comments -- `#include`/`#base` keyword (note: searches for files in the current working directory) -- platform independent -- header-only - -## Requirements -- C++11 - -## Test Requirements -- C++14 (uses [catch2](https://github.com/catchorg/Catch2)) - -(works with the C++11 features of vs120/"Visual Studio 2013" and newer) - -## How-To Use -First, you have to include the main file `vdf-Parser.h`. -This file provides several functions and data-structures which are -in the namespace `tyti::vdf`. - -All functions and data structures supports wide characters. -The wide character data structure is indicated by the commonly known `w`-prefix. -Functions are templates and don't need a prefix. - -To read an file, create a stream e.g. `std::ifsteam` or `std::wifstream` -and call the `tyti::vdf::read` function. -```c++ -std::ifstream file("PathToMyFile"); -auto root = tyti::vdf::read(file); -``` -You can also define a sequence of character defined by a range. -```c++ -std::string blob; -... -auto root = tyti::vdf::read(std::cbegin(blob), std::cend(blob)); - -//given .vdf below, following holds -assert(root.name == "name"); -const std::shared_ptr child = root.childs["child0"]; -assert(child->name == "child0"); -const std::string& k = root[0].attribs["attrib0"]; -assert(k == "value"); -``` - -The `tyti::vdf::object` is a tree like data structure. -It has its name, some attributes as a pair of `key` and `value` -and its object childs. Below you can see a vdf data structure and how it is stored by naming: -```javascript -"name" -{ - "attrib0" "value" // saved as a pair, first -> key, second -> value - "#base" "includeFile.vdf" // appends object defined in the file to childs - "child0" - { - ... - } - ... -} -``` - -Given such an object, you can also write it into vdf files via: -```c++ -tyti::vdf::write(file, object); -``` - -## Multi-Key and Custom Output Type - -It is also possible to customize your output dataformat. -Per default, the parser stores all items in a std::unordered_map, which, per definition, -doesn't allow different entries with the same key. - -However, the Valve vdf format supports multiple keys. Therefore, the output data format -has to store all items in e.g. a std::unordered_multimap. - -You can change the output format by passing the output type via template argument to -the read function -```c++ -namespace tyti; -vdf::object no_multi_key = vdf::read(file); -vdf::multikey_object multi_key = vdf::read(file); -``` - -__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) -are different when you access the elements. - -It is also possible to create your own data structure which is used by the parser. -Your output class needs to define 3 functions with the following signature: - -```c++ -void add_attribute(std::basic_string key, std::basic_string value); -void add_child(std::unique_ptr< MYCLASS > child); -void set_name(std::basic_string n); -``` -where ```MYCLASS``` is the tpe of your class and ```CHAR``` the type of your character set. -Also, the type has to be [default constructible](http://en.cppreference.com/w/cpp/types/is_default_constructible) -and [move constructible](http://en.cppreference.com/w/cpp/types/is_move_constructible). - -This also allows you, to inspect the file without storing it in a data structure. -Lets say, for example, you want to count all attributes of a file without storing it. -You can do this by using this class - -```c++ -struct counter -{ - size_t num_attributes = 0; - void add_attribute(std::string key, std::string value) - { - ++num_attributes; - } - void add_child(std::unique_ptr< counter > child) - { - num_attributes += child->num_attributes; - } - void set_name(std::string n) - {} -}; -``` - -and then call the read function -```c++ -std::vector num = tyti::vdf::read(file); -``` - -## Options (experimental) - -You can configure the parser, the non default options are not well tested yet. - -```c++ -struct Options -{ - bool strip_escape_symbols; //default true - bool ignore_all_platform_conditionals; // default false - bool ignore_includes; //default false -}; - -``` - -## Reference -```c++ -///////////////////////////////////////////////////////////// -// pre-defined output classes -///////////////////////////////////////////////////////////// - // default output object - template - basic_object - { - std::basic_string name; - std::unordered_map, std::basic_string > attribs; - std::unordered_map, std::shared_ptr< basic_object > > childs; - }; - typedef basic_object object; - typedef basic_object wobject - - // output object with multikey support - template - basic_multikey_object - { - std::basic_string name; - std::unordered_multimap, std::basic_string > attribs; - std::unordered_multimap, std::shared_ptr< basic_object > > childs; - }; - typedef basic_multikey_object multikey_object; - typedef basic_multikey_object wmultikey_object - -///////////////////////////////////////////////////////////// -// error codes -///////////////////////////////////////////////////////////// -/* - Possible error codes: - std::errc::protocol_error: file is mailformatted - std::errc::not_enough_memory: not enough space - std::errc::invalid_argument: iterators throws e.g. out of range -*/ - -///////////////////////////////////////////////////////////// -// read from stream -///////////////////////////////////////////////////////////// - - /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. - throws "std::bad_alloc" if file buffer could not be allocated - throws "std::runtime_error" if a parsing error occured - */ - template - std::vector read(iStreamT& inStream, const Options &opt = Options{}); - - template - std::vector> read(iStreamT& inStream, const Options &opt = Options{}); - - /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. - throws "std::bad_alloc" if file buffer could not be allocated - ok == false, if a parsing error occured - */ - template - std::vector read(iStreamT& inStream, bool* ok, const Options &opt = Options{}); - - template - std::vector> read(iStreamT& inStream, bool* ok, const Options &opt = Options{}); - - /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. - throws "std::bad_alloc" if file buffer could not be allocated - */ - template - std::vector read(iStreamT& inStream, std::error_code& ec, const Options &opt = Options{}); - - template - std::vector> read(iStreamT& inStream, std::error_code& ec, const Options &opt = Options{}); - -///////////////////////////////////////////////////////////// -// read from memory -///////////////////////////////////////////////////////////// - - /** \brief Read VDF formatted sequences defined by the range [first, last). - If the file is mailformatted, parser will try to read it until it can. - @param first begin iterator - @param end end iterator - - throws "std::runtime_error" if a parsing error occured - throws "std::bad_alloc" if not enough memory could be allocated - */ - template - std::vector read(IterT first, IterT last, const Options &opt = Options{}); - - template - std::vector::value_type>> read(IterT first, IterT last, const Options &opt = Options{}); - - /** \brief Read VDF formatted sequences defined by the range [first, last). - If the file is mailformatted, parser will try to read it until it can. - @param first begin iterator - @param end end iterator - @param ok output bool. true, if parser successed, false, if parser failed - */ - template - std::vector read(IterT first, IterT last, bool* ok, const Options &opt = Options{}) noexcept; - - template - std::vector::value_type>> read(IterT first, IterT last, bool* ok, const Options &opt = Options{}) noexcept; - - - - /** \brief Read VDF formatted sequences defined by the range [first, last). - If the file is mailformatted, parser will try to read it until it can. - @param first begin iterator - @param end end iterator - @param ec output bool. 0 if ok, otherwise, holds an system error code - */ - template - std::vector read(IterT first, IterT last, std::error_code& ec, const Options &opt = Options{}) noexcept; - - template - std::vector::value_type>> read(IterT first, IterT last, std::error_code& ec, const Options &opt = Options{}) noexcept; - - -///////////////////////////////////////////////////////////////////////////// - // Writer functions - /// writes given obj into out in vdf style - /// Output is prettyfied, using tabs - template - void write(oStreamT& out, const T& obj, const Options &opt = Options{}); - -``` - -## Remarks for Errors -The current version is a greedy implementation and jumps over unrecognized fields. -Therefore, the error detection is very imprecise an does not give the line, where the error occurs. - -## License - -[MIT License](./LICENSE) © Matthias Möller. Made with ♥ in Germany. diff --git a/src/external/valve-file-vdf/vdf_parser.hpp b/src/external/valve-file-vdf/vdf_parser.hpp index ec0932087..c3e2db7fe 100644 --- a/src/external/valve-file-vdf/vdf_parser.hpp +++ b/src/external/valve-file-vdf/vdf_parser.hpp @@ -1,50 +1,50 @@ -//MIT License +// MIT License // -//Copyright(c) 2016 Matthias Moeller +// Copyright(c) 2016 Matthias Moeller // -//Permission is hereby granted, free of charge, to any person obtaining a copy -//of this software and associated documentation files(the "Software"), to deal -//in the Software without restriction, including without limitation the rights -//to use, copy, modify, merge, publish, distribute, sublicense, and / or sell -//copies of the Software, and to permit persons to whom the Software is -//furnished to do so, subject to the following conditions : +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files(the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions : // -//The above copyright notice and this permission notice shall be included in all -//copies or substantial portions of the Software. +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. // -//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE -//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -//SOFTWARE. +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. #ifndef __TYTI_STEAM_VDF_PARSER_H__ #define __TYTI_STEAM_VDF_PARSER_H__ -#include -#include -#include -#include -#include -#include -#include #include -#include +#include #include +#include +#include +#include +#include +#include +#include +#include -#include #include +#include -//for wstring support +// for wstring support #include #include // internal #include -//VS < 2015 has only partial C++11 support +// VS < 2015 has only partial C++11 support #if defined(_MSC_VER) && _MSC_VER < 1900 #ifndef CONSTEXPR #define CONSTEXPR @@ -68,650 +68,744 @@ namespace tyti { - namespace vdf +namespace vdf +{ +namespace detail +{ +/////////////////////////////////////////////////////////////////////////// +// Helper functions selecting the right encoding (char/wchar_T) +/////////////////////////////////////////////////////////////////////////// + +template struct literal_macro_help +{ + static CONSTEXPR const char *result(const char *c, const wchar_t *) NOEXCEPT { - namespace detail - { - /////////////////////////////////////////////////////////////////////////// - // Helper functions selecting the right encoding (char/wchar_T) - /////////////////////////////////////////////////////////////////////////// + return c; + } + static CONSTEXPR const char result(const char c, const wchar_t) NOEXCEPT + { + return c; + } +}; - template - struct literal_macro_help - { - static CONSTEXPR const char* result(const char* c, const wchar_t*) NOEXCEPT - { - return c; - } - static CONSTEXPR const char result(const char c, const wchar_t) NOEXCEPT - { - return c; - } - }; +template <> struct literal_macro_help +{ + static CONSTEXPR const wchar_t *result(const char *, + const wchar_t *wc) NOEXCEPT + { + return wc; + } + static CONSTEXPR const wchar_t result(const char, const wchar_t wc) NOEXCEPT + { + return wc; + } +}; +#define TYTI_L(type, text) \ + vdf::detail::literal_macro_help::result(text, L##text) - template <> - struct literal_macro_help - { - static CONSTEXPR const wchar_t* result(const char*, const wchar_t* wc) NOEXCEPT - { - return wc; - } - static CONSTEXPR const wchar_t result(const char, const wchar_t wc) NOEXCEPT - { - return wc; - } - }; -#define TYTI_L(type, text) vdf::detail::literal_macro_help::result(text, L##text) +inline std::string string_converter(const std::string &w) NOEXCEPT { return w; } - inline std::string string_converter(const std::string& w) NOEXCEPT - { - return w; - } +// utility wrapper to adapt locale-bound facets for wstring/wbuffer convert +// from cppreference +template struct deletable_facet : Facet +{ + template + deletable_facet(Args &&...args) : Facet(std::forward(args)...) + { + } + ~deletable_facet() {} +}; - // utility wrapper to adapt locale-bound facets for wstring/wbuffer convert - // from cppreference - template - struct deletable_facet : Facet - { - template - deletable_facet(Args &&... args) : Facet(std::forward(args)...) {} - ~deletable_facet() {} - }; +inline std::string +string_converter(const std::wstring &w) // todo: use us-locale +{ + std::wstring_convert< + deletable_facet>> + conv1; + return conv1.to_bytes(w); +} - inline std::string string_converter(const std::wstring& w) //todo: use us-locale - { - std::wstring_convert>> conv1; - return conv1.to_bytes(w); - } +/////////////////////////////////////////////////////////////////////////// +// Writer helper functions +/////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - // Writer helper functions - /////////////////////////////////////////////////////////////////////////// +template class tabs +{ + const size_t t; - template - class tabs - { - const size_t t; + public: + explicit CONSTEXPR tabs(size_t i) NOEXCEPT : t(i) {} + std::basic_string print() const + { + return std::basic_string(t, TYTI_L(charT, '\t')); + } + inline CONSTEXPR tabs operator+(size_t i) const NOEXCEPT + { + return tabs(t + i); + } +}; - public: - explicit CONSTEXPR tabs(size_t i) NOEXCEPT : t(i) {} - std::basic_string print() const { return std::basic_string(t, TYTI_L(charT, '\t')); } - inline CONSTEXPR tabs operator+(size_t i) const NOEXCEPT - { - return tabs(t + i); - } - }; +template +oStreamT &operator<<(oStreamT &s, const tabs t) +{ + s << t.print(); + return s; +} +} // end namespace detail - template - oStreamT& operator<<(oStreamT& s, const tabs t) - { - s << t.print(); - return s; - } - } // end namespace detail +/////////////////////////////////////////////////////////////////////////// +// Interface +/////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////// - // Interface - /////////////////////////////////////////////////////////////////////////// +/// custom objects and their corresponding write functions - /// custom objects and their corresponding write functions +/// basic object node. Every object has a name and can contains attributes saved +/// as key_value pairs or childrens +template struct basic_object +{ + typedef CharT char_type; + std::basic_string name; + std::unordered_map, + std::basic_string> + attribs; + std::unordered_map, + std::shared_ptr>> + childs; - /// basic object node. Every object has a name and can contains attributes saved as key_value pairs or childrens - template - struct basic_object - { - typedef CharT char_type; - std::basic_string name; - std::unordered_map, std::basic_string> attribs; - std::unordered_map, std::shared_ptr>> children; + void add_attribute(std::basic_string key, + std::basic_string value) + { + attribs.emplace(std::move(key), std::move(value)); + } + void add_child(std::unique_ptr> child) + { + std::shared_ptr> obj{child.release()}; + childs.emplace(obj->name, obj); + } + void set_name(std::basic_string n) { name = std::move(n); } +}; - void add_attribute(std::basic_string key, std::basic_string value) - { - attribs.emplace(std::move(key), std::move(value)); - } - void add_child(std::unique_ptr> child) - { - std::shared_ptr> obj{ child.release() }; - children.emplace(obj->name, obj); - } - void set_name(std::basic_string n) - { - name = std::move(n); - } - }; +template struct basic_multikey_object +{ + typedef CharT char_type; + std::basic_string name; + std::unordered_multimap, + std::basic_string> + attribs; + std::unordered_multimap, + std::shared_ptr>> + childs; - template - struct basic_multikey_object - { - typedef CharT char_type; - std::basic_string name; - std::unordered_multimap, std::basic_string> attribs; - std::unordered_multimap, std::shared_ptr>> children; + void add_attribute(std::basic_string key, + std::basic_string value) + { + attribs.emplace(std::move(key), std::move(value)); + } + void add_child(std::unique_ptr> child) + { + std::shared_ptr> obj{child.release()}; + childs.emplace(obj->name, obj); + } + void set_name(std::basic_string n) { name = std::move(n); } +}; - void add_attribute(std::basic_string key, std::basic_string value) - { - attribs.emplace(std::move(key), std::move(value)); - } - void add_child(std::unique_ptr> child) - { - std::shared_ptr> obj{ child.release() }; - children.emplace(obj->name, obj); - } - void set_name(std::basic_string n) - { - name = std::move(n); - } - }; +typedef basic_object object; +typedef basic_object wobject; +typedef basic_multikey_object multikey_object; +typedef basic_multikey_object wmultikey_object; - typedef basic_object object; - typedef basic_object wobject; - typedef basic_multikey_object multikey_object; - typedef basic_multikey_object wmultikey_object; +struct Options +{ + bool strip_escape_symbols; + bool ignore_all_platform_conditionals; + bool ignore_includes; - struct Options - { - bool strip_escape_symbols; - bool ignore_all_platform_conditionals; - bool ignore_includes; + Options() + : strip_escape_symbols(true), ignore_all_platform_conditionals(false), + ignore_includes(false) + { + } +}; - Options() : strip_escape_symbols(true), ignore_all_platform_conditionals(false), ignore_includes(false) {} - }; +// forward decls +// forward decl +template +OutputT read(iStreamT &inStream, const Options &opt = Options{}); - //forward decls - //forward decl - template - OutputT read(iStreamT& inStream, const Options& opt = Options{}); +/** \brief writes given object tree in vdf format to given stream. +Output is prettyfied, using tabs +*/ +template +void write(oStreamT &s, const T &r, + const detail::tabs tab = + detail::tabs(0)) +{ + typedef typename oStreamT::char_type charT; + using namespace detail; + s << tab << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tab + << TYTI_L(charT, "{\n"); + for (const auto &i : r.attribs) + s << tab + 1 << TYTI_L(charT, '"') << i.first + << TYTI_L(charT, "\"\t\t\"") << i.second << TYTI_L(charT, "\"\n"); + for (const auto &i : r.childs) + if (i.second) + write(s, *i.second, tab + 1); + s << tab << TYTI_L(charT, "}\n"); +} - /** \brief writes given object tree in vdf format to given stream. - Output is prettyfied, using tabs - */ - template - void write(oStreamT& s, const T& r, - const detail::tabs tab = detail::tabs(0)) - { - typedef typename oStreamT::char_type charT; - using namespace detail; - s << tab << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tab << TYTI_L(charT, "{\n"); - for (const auto& i : r.attribs) - s << tab + 1 << TYTI_L(charT, '"') << i.first << TYTI_L(charT, "\"\t\t\"") << i.second << TYTI_L(charT, "\"\n"); - for (const auto& i : r.children) - if (i.second) - write(s, *i.second, tab + 1); - s << tab << TYTI_L(charT, "}\n"); - } +namespace detail +{ +template +std::basic_string read_file(iStreamT &inStream) +{ + // cache the file + typedef typename iStreamT::char_type charT; + std::basic_string str; + inStream.seekg(0, std::ios::end); + str.resize(static_cast(inStream.tellg())); + if (str.empty()) + return str; - namespace detail - { - template - std::basic_string read_file(iStreamT& inStream) - { - // cache the file - typedef typename iStreamT::char_type charT; - std::basic_string str; - inStream.seekg(0, std::ios::end); - str.resize(static_cast(inStream.tellg())); - if (str.empty()) - return str; + inStream.seekg(0, std::ios::beg); + inStream.read(&str[0], str.size()); + return str; +} - inStream.seekg(0, std::ios::beg); - inStream.read(&str[0], str.size()); - return str; - } +/** \brief Read VDF formatted sequences defined by the range [first, last). +If the file is mailformatted, parser will try to read it until it can. +@param first begin iterator +@param end end iterator +@param exclude_files list of files which cant be included anymore. + prevents circular includes - /** \brief Read VDF formatted sequences defined by the range [first, last). - If the file is mailformatted, parser will try to read it until it can. - @param first begin iterator - @param end end iterator - @param exclude_files list of files which cant be included anymore. - prevents circular includes +can thow: + - "std::runtime_error" if a parsing error occured + - "std::bad_alloc" if not enough memory coup be allocated +*/ +template +std::vector> read_internal( + IterT first, const IterT last, + std::unordered_set< + std::basic_string::value_type>> + &exclude_files, + const Options &opt) +{ + static_assert(std::is_default_constructible::value, + "Output Type must be default constructible (provide " + "constructor without arguments)"); + static_assert(std::is_move_constructible::value, + "Output Type must be move constructible"); - can thow: - - "std::runtime_error" if a parsing error occured - - "std::bad_alloc" if not enough memory coup be allocated - */ - template - std::vector> read_internal(IterT first, const IterT last, - std::unordered_set::value_type>>& exclude_files, - const Options& opt) - { - static_assert(std::is_default_constructible::value, - "Output Type must be default constructible (provide constructor without arguments)"); - static_assert(std::is_move_constructible::value, - "Output Type must be move constructible"); + typedef typename std::iterator_traits::value_type charT; - typedef typename std::iterator_traits::value_type charT; - - const std::basic_string comment_end_str = TYTI_L(charT, "*/"); - const std::basic_string whitespaces = TYTI_L(charT, " \n\v\f\r\t"); + const std::basic_string comment_end_str = TYTI_L(charT, "*/"); + const std::basic_string whitespaces = TYTI_L(charT, " \n\v\f\r\t"); #ifdef WIN32 - std::function&)> is_platform_str = [](const std::basic_string& in) { - return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$WINDOWS"); - }; + std::function &)> is_platform_str = + [](const std::basic_string &in) + { + return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$WINDOWS"); + }; #elif __APPLE__ - // WIN32 stands for pc in general - std::function&)> is_platform_str = [](const std::basic_string& in) { - return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || in == TYTI_L(charT, "$OSX"); - }; + // WIN32 stands for pc in general + std::function &)> is_platform_str = + [](const std::basic_string &in) + { + return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || + in == TYTI_L(charT, "$OSX"); + }; #elif __linux__ - // WIN32 stands for pc in general - std::function&)> is_platform_str = [](const std::basic_string& in) { - return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || in == TYTI_L(charT, "$LINUX"); - }; + // WIN32 stands for pc in general + std::function &)> is_platform_str = + [](const std::basic_string &in) + { + return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || + in == TYTI_L(charT, "$LINUX"); + }; #else - std::function&)> is_platform_str = [](const std::basic_string& in) { - return false; - }; + std::function &)> is_platform_str = + [](const std::basic_string &in) { return false; }; #endif - if (opt.ignore_all_platform_conditionals) - is_platform_str = [](const std::basic_string&) { - return false; - }; + if (opt.ignore_all_platform_conditionals) + is_platform_str = [](const std::basic_string &) + { return false; }; - // function for skipping a comment block - // iter: iterator poition to the position after a '/' - auto skip_comments = [&comment_end_str](IterT iter, const IterT& last) -> IterT { - ++iter; - if (iter != last) - { - if (*iter == TYTI_L(charT, '/')) - { - // line comment, skip whole line - iter = std::find(iter + 1, last, TYTI_L(charT, '\n')); - } + // function for skipping a comment block + // iter: iterator poition to the position after a '/' + auto skip_comments = [&comment_end_str](IterT iter, + const IterT &last) -> IterT + { + ++iter; + if (iter == last) + return last; - if (*iter == '*') - { - // block comment, skip until next occurance of "*\" - iter = std::search(iter + 1, last, std::begin(comment_end_str), std::end(comment_end_str)); - iter += 2; - } - } - return iter; - }; + if (*iter == TYTI_L(charT, '/')) + { + // line comment, skip whole line + iter = std::find(iter + 1, last, TYTI_L(charT, '\n')); + if (iter == last) + return last; + } - auto end_quote = [](IterT iter, const IterT& last) -> IterT { - const auto begin = iter; - auto last_esc = iter; - do - { - ++iter; - iter = std::find(iter, last, TYTI_L(charT, '\"')); - if (iter == last) - break; + if (*iter == '*') + { + // block comment, skip until next occurance of "*\" + iter = std::search(iter + 1, last, std::begin(comment_end_str), + std::end(comment_end_str)); + if (std::distance(iter, last) <= 2) + return last; + iter += 2; + } - last_esc = std::prev(iter); - while (last_esc != begin && *last_esc == '\\') - --last_esc; - } while (!(std::distance(last_esc, iter) % 2)); - if (iter == last) - throw std::runtime_error{ "quote was opened but not closed." }; - return iter; - }; + return iter; + }; - auto end_word = [&whitespaces](IterT iter, const IterT& last) -> IterT { - const auto begin = iter; - auto last_esc = iter; - do - { - ++iter; - iter = std::find_first_of(iter, last, std::begin(whitespaces), std::end(whitespaces)); - if (iter == last) - break; + auto end_quote = [](IterT iter, const IterT &last) -> IterT + { + const auto begin = iter; + auto last_esc = iter; + if (iter == last) + throw std::runtime_error{"quote was opened but not closed."}; + do + { + ++iter; + iter = std::find(iter, last, TYTI_L(charT, '\"')); + if (iter == last) + break; - last_esc = std::prev(iter); - while (last_esc != begin && *last_esc == '\\') - --last_esc; - } while (!(std::distance(last_esc, iter) % 2)); - //if (iter == last) - // throw std::runtime_error{ "word wasnt properly ended" }; - return iter; - }; + last_esc = std::prev(iter); + while (last_esc != begin && *last_esc == '\\') + --last_esc; + } while (!(std::distance(last_esc, iter) % 2) && iter != last); + if (iter == last) + throw std::runtime_error{"quote was opened but not closed."}; + return iter; + }; - auto skip_whitespaces = [&whitespaces](IterT iter, const IterT& last) -> IterT { - iter = std::find_if_not(iter, last, [&whitespaces](charT c) { - // return true if whitespace - return std::any_of(std::begin(whitespaces), std::end(whitespaces), [c](charT pc) { return pc == c; }); - }); - return iter; - }; + auto end_word = [&whitespaces](IterT iter, const IterT &last) -> IterT + { + const auto begin = iter; + auto last_esc = iter; + if (iter == last) + throw std::runtime_error{"quote was opened but not closed."}; + do + { + ++iter; + iter = std::find_first_of(iter, last, std::begin(whitespaces), + std::end(whitespaces)); + if (iter == last) + break; - std::function&)> strip_escape_symbols = [](std::basic_string& s) { - auto quote_searcher = [&s](size_t pos) { return s.find(TYTI_L(charT, "\\\""), pos); }; - auto p = quote_searcher(0); - while (p != s.npos) - { - s.replace(p, 2, TYTI_L(charT, "\"")); - p = quote_searcher(p); - } - auto searcher = [&s](size_t pos) { return s.find(TYTI_L(charT, "\\\\"), pos); }; - p = searcher(0); - while (p != s.npos) - { - s.replace(p, 2, TYTI_L(charT, "\\")); - p = searcher(p); - } - }; + last_esc = std::prev(iter); + while (last_esc != begin && *last_esc == '\\') + --last_esc; + } while (!(std::distance(last_esc, iter) % 2) && iter != last); + if (iter == last) + throw std::runtime_error{"word wasnt properly ended"}; + return iter; + }; - if (!opt.strip_escape_symbols) - strip_escape_symbols = [](std::basic_string&) {}; + auto skip_whitespaces = [&whitespaces](IterT iter, + const IterT &last) -> IterT + { + if (iter == last) + return iter; + iter = std::find_if_not(iter, last, + [&whitespaces](charT c) + { + // return true if whitespace + return std::any_of(std::begin(whitespaces), + std::end(whitespaces), + [c](charT pc) + { return pc == c; }); + }); + return iter; + }; - auto conditional_fullfilled = [&skip_whitespaces, &is_platform_str](IterT& iter, const IterT& last) { - iter = skip_whitespaces(iter, last); - if (*iter == '[') - { - ++iter; - const auto end = std::find(iter, last, ']'); - const bool negate = *iter == '!'; - if (negate) - ++iter; - auto conditional = std::basic_string(iter, end); + std::function &)> strip_escape_symbols = + [](std::basic_string &s) + { + auto quote_searcher = [&s](size_t pos) + { return s.find(TYTI_L(charT, "\\\""), pos); }; + auto p = quote_searcher(0); + while (p != s.npos) + { + s.replace(p, 2, TYTI_L(charT, "\"")); + p = quote_searcher(p); + } + auto searcher = [&s](size_t pos) + { return s.find(TYTI_L(charT, "\\\\"), pos); }; + p = searcher(0); + while (p != s.npos) + { + s.replace(p, 2, TYTI_L(charT, "\\")); + p = searcher(p); + } + }; - const bool is_platform = is_platform_str(conditional); - iter = end + 1; + if (!opt.strip_escape_symbols) + strip_escape_symbols = [](std::basic_string &) {}; - return static_cast(is_platform ^ negate); - } - return true; - }; + auto conditional_fullfilled = + [&skip_whitespaces, &is_platform_str](IterT &iter, const IterT &last) + { + iter = skip_whitespaces(iter, last); + if (iter == last) + return true; + if (*iter == '[') + { + ++iter; + if (iter == last) + throw std::runtime_error("conditional not closed"); + const auto end = std::find(iter, last, ']'); + if (end == last) + throw std::runtime_error("conditional not closed"); + const bool negate = *iter == '!'; + if (negate) + ++iter; + auto conditional = std::basic_string(iter, end); - //read header - // first, quoted name - std::unique_ptr curObj = nullptr; - std::vector> roots; - std::stack> lvls; - auto curIter = first; + const bool is_platform = is_platform_str(conditional); + iter = end + 1; - while (curIter != last && *curIter != '\0') + return static_cast(is_platform ^ negate); + } + return true; + }; + + // read header + // first, quoted name + std::unique_ptr curObj = nullptr; + std::vector> roots; + std::stack> lvls; + auto curIter = first; + + while (curIter != last && *curIter != '\0') + { + // find first starting attrib/child, or ending + curIter = skip_whitespaces(curIter, last); + if (curIter == last || *curIter == '\0') + break; + if (*curIter == TYTI_L(charT, '/')) + { + curIter = skip_comments(curIter, last); + if (curIter == last || *curIter == '\0') + throw std::runtime_error("Unexpected eof"); + } + else if (*curIter != TYTI_L(charT, '}')) + { + // get key + const auto keyEnd = (*curIter == TYTI_L(charT, '\"')) + ? end_quote(curIter, last) + : end_word(curIter, last); + if (*curIter == TYTI_L(charT, '\"')) + ++curIter; + std::basic_string key(curIter, keyEnd); + strip_escape_symbols(key); + curIter = keyEnd + ((*keyEnd == TYTI_L(charT, '\"')) ? 1 : 0); + if (curIter == last) + throw std::runtime_error{"key opened, but never closed"}; + + curIter = skip_whitespaces(curIter, last); + + auto conditional = conditional_fullfilled(curIter, last); + if (!conditional) + continue; + if (curIter == last) + throw std::runtime_error{"key declared, but no value"}; + + while (*curIter == TYTI_L(charT, '/')) + { + + curIter = skip_comments(curIter, last); + if (curIter == last || *curIter == '}') + throw std::runtime_error{"key declared, but no value"}; + curIter = skip_whitespaces(curIter, last); + if (curIter == last || *curIter == '}') + throw std::runtime_error{"key declared, but no value"}; + } + // get value + if (*curIter != '{') + { + if (curIter == last) + throw std::runtime_error{"key declared, but no value"}; + const auto valueEnd = (*curIter == TYTI_L(charT, '\"')) + ? end_quote(curIter, last) + : end_word(curIter, last); + if (valueEnd == last) + throw std::runtime_error("No closed word"); + if (*curIter == TYTI_L(charT, '\"')) + ++curIter; + if (curIter == last) + throw std::runtime_error("No closed word"); + + auto value = std::basic_string(curIter, valueEnd); + strip_escape_symbols(value); + curIter = + valueEnd + ((*valueEnd == TYTI_L(charT, '\"')) ? 1 : 0); + + auto conditional = conditional_fullfilled(curIter, last); + if (!conditional) + continue; + + // process value + if (key != TYTI_L(charT, "#include") && + key != TYTI_L(charT, "#base")) { - //find first starting attrib/child, or ending - curIter = skip_whitespaces(curIter, last); - if (curIter == last || *curIter == '\0') - break; - if (*curIter == TYTI_L(charT, '/')) + if (curObj) { - curIter = skip_comments(curIter, last); - } - else if (*curIter != TYTI_L(charT, '}')) - { - - // get key - const auto keyEnd = (*curIter == TYTI_L(charT, '\"')) ? end_quote(curIter, last) : end_word(curIter, last); - if (*curIter == TYTI_L(charT, '\"')) - ++curIter; - std::basic_string key(curIter, keyEnd); - strip_escape_symbols(key); - curIter = keyEnd + ((*keyEnd == TYTI_L(charT, '\"')) ? 1 : 0); - - curIter = skip_whitespaces(curIter, last); - - auto conditional = conditional_fullfilled(curIter, last); - if (!conditional) - continue; - - while (*curIter == TYTI_L(charT, '/')) - { - - curIter = skip_comments(curIter, last); - if (curIter == last || *curIter == '}') - throw std::runtime_error{ "key declared, but no value" }; - curIter = skip_whitespaces(curIter, last); - if (curIter == last || *curIter == '}') - throw std::runtime_error{ "key declared, but no value" }; - } - // get value - if (*curIter != '{') - { - const auto valueEnd = (*curIter == TYTI_L(charT, '\"')) ? end_quote(curIter, last) : end_word(curIter, last); - if (*curIter == TYTI_L(charT, '\"')) - ++curIter; - - auto value = std::basic_string(curIter, valueEnd); - strip_escape_symbols(value); - curIter = valueEnd + ((*valueEnd == TYTI_L(charT, '\"')) ? 1 : 0); - - auto conditional = conditional_fullfilled(curIter, last); - if (!conditional) - continue; - - // process value - if (key != TYTI_L(charT, "#include") && key != TYTI_L(charT, "#base")) - { - if (curObj) - { - curObj->add_attribute(std::move(key), std::move(value)); - } - else - { - throw std::runtime_error{ "unexpected key without object" }; - } - } - else - { - if (!opt.ignore_includes && exclude_files.find(value) == exclude_files.end()) - { - exclude_files.insert(value); - std::basic_ifstream i(detail::string_converter(value)); - auto str = read_file(i); - auto file_objs = read_internal(str.begin(), str.end(), exclude_files, opt); - for (auto& n : file_objs) - { - if (curObj) - curObj->add_child(std::move(n)); - else - roots.push_back(std::move(n)); - } - exclude_files.erase(value); - } - } - } - else if (*curIter == '{') - { - if (curObj) - lvls.push(std::move(curObj)); - curObj = std::make_unique(); - curObj->set_name(std::move(key)); - ++curIter; - } - } - //end of new object - else if (curObj && *curIter == TYTI_L(charT, '}')) - { - if (!lvls.empty()) - { - //get object before - std::unique_ptr prev{ std::move(lvls.top()) }; - lvls.pop(); - - // add finished obj to obj before and release it from processing - prev->add_child(std::move(curObj)); - curObj = std::move(prev); - } - else - { - roots.push_back(std::move(curObj)); - curObj.reset(); - } - ++curIter; + curObj->add_attribute(std::move(key), std::move(value)); } else { - throw std::runtime_error{ "unexpected '}'" }; + throw std::runtime_error{ + "unexpected key without object"}; } } - if (curObj != nullptr || !lvls.empty()) + else { - throw std::runtime_error{ "object is not closed with '}'" }; + if (!opt.ignore_includes && + exclude_files.find(value) == exclude_files.end()) + { + exclude_files.insert(value); + std::basic_ifstream i( + detail::string_converter(value)); + auto str = read_file(i); + auto file_objs = read_internal( + str.begin(), str.end(), exclude_files, opt); + for (auto &n : file_objs) + { + if (curObj) + curObj->add_child(std::move(n)); + else + roots.push_back(std::move(n)); + } + exclude_files.erase(value); + } } - - return roots; } - - } // namespace detail - - /** \brief Read VDF formatted sequences defined by the range [first, last). - If the file is mailformatted, parser will try to read it until it can. - @param first begin iterator - @param end end iterator - - can thow: - - "std::runtime_error" if a parsing error occured - - "std::bad_alloc" if not enough memory coup be allocated - */ - template - OutputT read(IterT first, const IterT last, const Options& opt = Options{}) - { - auto exclude_files = std::unordered_set::value_type>>{}; - auto roots = detail::read_internal(first, last, exclude_files, opt); - - OutputT result; - if (roots.size() > 1) + else if (*curIter == '{') { - for (auto& i : roots) - result.add_child(std::move(i)); + if (curObj) + lvls.push(std::move(curObj)); + curObj = std::make_unique(); + curObj->set_name(std::move(key)); + ++curIter; } - else if (roots.size() == 1) - result = std::move(*roots[0]); - - return result; } - - /** \brief Read VDF formatted sequences defined by the range [first, last). - If the file is mailformatted, parser will try to read it until it can. - @param first begin iterator - @param end end iterator - @param ec output bool. 0 if ok, otherwise, holds an system error code - - Possible error codes: - std::errc::protocol_error: file is mailformatted - std::errc::not_enough_memory: not enough space - std::errc::invalid_argument: iterators throws e.g. out of range - */ - template - OutputT read(IterT first, IterT last, std::error_code& ec, const Options& opt = Options{}) NOEXCEPT - + // end of new object + else if (curObj && *curIter == TYTI_L(charT, '}')) { - ec.clear(); - OutputT r{}; - try + if (!lvls.empty()) { - r = read(first, last, opt); + // get object before + std::unique_ptr prev{std::move(lvls.top())}; + lvls.pop(); + + // add finished obj to obj before and release it from processing + prev->add_child(std::move(curObj)); + curObj = std::move(prev); } - catch (std::runtime_error&) + else { - ec = std::make_error_code(std::errc::protocol_error); + roots.push_back(std::move(curObj)); + curObj.reset(); } - catch (std::bad_alloc&) - { - ec = std::make_error_code(std::errc::not_enough_memory); - } - catch (...) - { - ec = std::make_error_code(std::errc::invalid_argument); - } - return r; + ++curIter; } - - /** \brief Read VDF formatted sequences defined by the range [first, last). - If the file is mailformatted, parser will try to read it until it can. - @param first begin iterator - @param end end iterator - @param ok output bool. true, if parser successed, false, if parser failed - */ - template - OutputT read(IterT first, const IterT last, bool* ok, const Options& opt = Options{}) NOEXCEPT + else { - std::error_code ec; - auto r = read(first, last, ec, opt); - if (ok) - *ok = !ec; - return r; + throw std::runtime_error{"unexpected '}'"}; } + } + if (curObj != nullptr || !lvls.empty()) + { + throw std::runtime_error{"object is not closed with '}'"}; + } - template - inline auto read(IterT first, const IterT last, bool* ok, const Options& opt = Options{}) NOEXCEPT -> basic_object::value_type> - { - return read::value_type>>(first, last, ok, opt); - } + return roots; +} - template - inline auto read(IterT first, IterT last, std::error_code& ec, const Options& opt = Options{}) NOEXCEPT - -> basic_object::value_type> - { - return read::value_type>>(first, last, ec, opt); - } +} // namespace detail - template - inline auto read(IterT first, const IterT last, const Options& opt = Options{}) - -> basic_object::value_type> - { - return read::value_type>>(first, last, opt); - } +/** \brief Read VDF formatted sequences defined by the range [first, last). +If the file is mailformatted, parser will try to read it until it can. +@param first begin iterator +@param end end iterator - /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. - throws "std::bad_alloc" if file buffer could not be allocated - */ - template - OutputT read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{}) - { - // cache the file - typedef typename iStreamT::char_type charT; - std::basic_string str = detail::read_file(inStream); +can thow: + - "std::runtime_error" if a parsing error occured + - "std::bad_alloc" if not enough memory coup be allocated +*/ +template +OutputT read(IterT first, const IterT last, const Options &opt = Options{}) +{ + auto exclude_files = std::unordered_set< + std::basic_string::value_type>>{}; + auto roots = + detail::read_internal(first, last, exclude_files, opt); - // parse it - return read(str.begin(), str.end(), ec, opt); - } + OutputT result; + if (roots.size() > 1) + { + for (auto &i : roots) + result.add_child(std::move(i)); + } + else if (roots.size() == 1) + result = std::move(*roots[0]); - template - inline basic_object read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{}) - { - return read>(inStream, ec, opt); - } + return result; +} - /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. - throws "std::bad_alloc" if file buffer could not be allocated - ok == false, if a parsing error occured - */ - template - OutputT read(iStreamT& inStream, bool* ok, const Options& opt = Options{}) - { - std::error_code ec; - const auto r = read(inStream, ec, opt); - if (ok) - *ok = !ec; - return r; - } +/** \brief Read VDF formatted sequences defined by the range [first, last). +If the file is mailformatted, parser will try to read it until it can. +@param first begin iterator +@param end end iterator +@param ec output bool. 0 if ok, otherwise, holds an system error code - template - inline basic_object read(iStreamT& inStream, bool* ok, const Options& opt = Options{}) - { - return read>(inStream, ok, opt); - } +Possible error codes: +std::errc::protocol_error: file is mailformatted +std::errc::not_enough_memory: not enough space +std::errc::invalid_argument: iterators throws e.g. out of range +*/ +template +OutputT read(IterT first, IterT last, std::error_code &ec, + const Options &opt = Options{}) NOEXCEPT - /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. - throws "std::bad_alloc" if file buffer could not be allocated - throws "std::runtime_error" if a parsing error occured - */ - template - OutputT read(iStreamT& inStream, const Options& opt) - { +{ + ec.clear(); + OutputT r{}; + try + { + r = read(first, last, opt); + } + catch (std::runtime_error &) + { + ec = std::make_error_code(std::errc::protocol_error); + } + catch (std::bad_alloc &) + { + ec = std::make_error_code(std::errc::not_enough_memory); + } + catch (...) + { + ec = std::make_error_code(std::errc::invalid_argument); + } + return r; +} - // cache the file - typedef typename iStreamT::char_type charT; - std::basic_string str = detail::read_file(inStream); - // parse it - return read(str.begin(), str.end(), opt); - } +/** \brief Read VDF formatted sequences defined by the range [first, last). +If the file is mailformatted, parser will try to read it until it can. +@param first begin iterator +@param end end iterator +@param ok output bool. true, if parser successed, false, if parser failed +*/ +template +OutputT read(IterT first, const IterT last, bool *ok, + const Options &opt = Options{}) NOEXCEPT +{ + std::error_code ec; + auto r = read(first, last, ec, opt); + if (ok) + *ok = !ec; + return r; +} - template - inline basic_object read(iStreamT& inStream, const Options& opt = Options{}) - { - return read>(inStream, opt); - } +template +inline auto read(IterT first, const IterT last, bool *ok, + const Options &opt = Options{}) NOEXCEPT + ->basic_object::value_type> +{ + return read::value_type>>( + first, last, ok, opt); +} - } // namespace vdf +template +inline auto read(IterT first, IterT last, std::error_code &ec, + const Options &opt = Options{}) NOEXCEPT + ->basic_object::value_type> +{ + return read::value_type>>( + first, last, ec, opt); +} + +template +inline auto read(IterT first, const IterT last, const Options &opt = Options{}) + -> basic_object::value_type> +{ + return read::value_type>>( + first, last, opt); +} + +/** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf + formatted data. throws "std::bad_alloc" if file buffer could not be allocated +*/ +template +OutputT read(iStreamT &inStream, std::error_code &ec, + const Options &opt = Options{}) +{ + // cache the file + typedef typename iStreamT::char_type charT; + std::basic_string str = detail::read_file(inStream); + + // parse it + return read(str.begin(), str.end(), ec, opt); +} + +template +inline basic_object +read(iStreamT &inStream, std::error_code &ec, const Options &opt = Options{}) +{ + return read>(inStream, ec, opt); +} + +/** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf + formatted data. throws "std::bad_alloc" if file buffer could not be allocated + ok == false, if a parsing error occured +*/ +template +OutputT read(iStreamT &inStream, bool *ok, const Options &opt = Options{}) +{ + std::error_code ec; + const auto r = read(inStream, ec, opt); + if (ok) + *ok = !ec; + return r; +} + +template +inline basic_object +read(iStreamT &inStream, bool *ok, const Options &opt = Options{}) +{ + return read>(inStream, ok, opt); +} + +/** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf + formatted data. throws "std::bad_alloc" if file buffer could not be allocated + throws "std::runtime_error" if a parsing error occured +*/ +template +OutputT read(iStreamT &inStream, const Options &opt) +{ + + // cache the file + typedef typename iStreamT::char_type charT; + std::basic_string str = detail::read_file(inStream); + // parse it + return read(str.begin(), str.end(), opt); +} + +template +inline basic_object +read(iStreamT &inStream, const Options &opt = Options{}) +{ + return read>(inStream, opt); +} + +} // namespace vdf } // namespace tyti #ifndef TYTI_NO_L_UNDEF #undef TYTI_L diff --git a/src/xrt/drivers/steamvr_lh/steamvr_lh.cpp b/src/xrt/drivers/steamvr_lh/steamvr_lh.cpp index 9891d1d9b..d35c08d20 100644 --- a/src/xrt/drivers/steamvr_lh/steamvr_lh.cpp +++ b/src/xrt/drivers/steamvr_lh/steamvr_lh.cpp @@ -79,9 +79,9 @@ find_steamvr_install() std::ifstream file(STEAM_INSTALL_DIR + "/steamapps/libraryfolders.vdf"); auto root = vdf::read(file); assert(root.name == "libraryfolders"); - for (auto &[_, child] : root.children) { + for (auto &[_, child] : root.childs) { U_LOG_D("Found library folder %s", child->attribs["path"].c_str()); - std::shared_ptr apps = child->children["apps"]; + std::shared_ptr apps = child->childs["apps"]; for (auto &[appid, _] : apps->attribs) { if (appid == STEAMVR_APPID) { return child->attribs["path"] + "/steamapps/common/SteamVR";