Simon Zeni 2024-06-28 14:49:43 -04:00 committed by Marge Bot
parent f29275ced2
commit a628e14dd0
5 changed files with 709 additions and 874 deletions

View file

@ -19,7 +19,7 @@
set -e set -e
# Comma-delimited list of words for codespell to not try to correct. # 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" IGNORE_REGEX="\b(pEvent|inout|Kimera)\b"
SCRIPTDIR=$(cd "$(dirname "$0")" && pwd) SCRIPTDIR=$(cd "$(dirname "$0")" && pwd)

21
src/external/valve-file-vdf/LICENSE vendored Normal file
View file

@ -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.

View file

@ -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<tyti::vdf::object> 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<vdf::multikey_object>(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<CHAR> key, std::basic_string<CHAR> value);
void add_child(std::unique_ptr< MYCLASS > child);
void set_name(std::basic_string<CHAR> 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<counter> num = tyti::vdf::read<counter>(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<typename T>
basic_object<T>
{
std::basic_string<char_type> name;
std::unordered_map<std::basic_string<char_type>, std::basic_string<char_type> > attribs;
std::unordered_map<std::basic_string<char_type>, std::shared_ptr< basic_object<char_type> > > childs;
};
typedef basic_object<char> object;
typedef basic_object<wchar_t> wobject
// output object with multikey support
template<typename T>
basic_multikey_object<T>
{
std::basic_string<char_type> name;
std::unordered_multimap<std::basic_string<char_type>, std::basic_string<char_type> > attribs;
std::unordered_multimap<std::basic_string<char_type>, std::shared_ptr< basic_object<char_type> > > childs;
};
typedef basic_multikey_object<char> multikey_object;
typedef basic_multikey_object<wchar_t> 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<ytpename OutputT, typename iStreamT>
std::vector<OutputT> read(iStreamT& inStream, const Options &opt = Options{});
template<typename iStreamT>
std::vector<basic_object<typename iStreamT::char_type>> 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<typename OutputT, typename iStreamT>
std::vector<OutputT> read(iStreamT& inStream, bool* ok, const Options &opt = Options{});
template<typename iStreamT>
std::vector<basic_object<typename iStreamT::char_type>> 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<typename OutputT, typename iStreamT>
std::vector<OutputT> read(iStreamT& inStream, std::error_code& ec, const Options &opt = Options{});
template<typename iStreamT>
std::vector<basic_object<iStreamT::char_type>> 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<typename OutputT, typename IterT>
std::vector<OutputT> read(IterT first, IterT last, const Options &opt = Options{});
template<typename IterT>
std::vector<basic_object<typename std::iterator_traits<IterT>::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<typename OutputT, typename IterT>
std::vector<OutputT> read(IterT first, IterT last, bool* ok, const Options &opt = Options{}) noexcept;
template<typename IterT>
std::vector<basic_object<typename std::iterator_traits<IterT>::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<typename OutputT, typename IterT>
std::vector<OutputT> read(IterT first, IterT last, std::error_code& ec, const Options &opt = Options{}) noexcept;
template<typename IterT>
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;
/////////////////////////////////////////////////////////////////////////////
// Writer functions
/// writes given obj into out in vdf style
/// Output is prettyfied, using tabs
template<typename oStreamT, typename T>
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.

View file

@ -9,8 +9,8 @@
// copies of the Software, and to permit persons to whom the Software is // copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions : // furnished to do so, subject to the following conditions :
// //
//The above copyright notice and this permission notice shall be included in all // The above copyright notice and this permission notice shall be included in
//copies or substantial portions of the Software. // all copies or substantial portions of the Software.
// //
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
@ -23,19 +23,19 @@
#ifndef __TYTI_STEAM_VDF_PARSER_H__ #ifndef __TYTI_STEAM_VDF_PARSER_H__
#define __TYTI_STEAM_VDF_PARSER_H__ #define __TYTI_STEAM_VDF_PARSER_H__
#include <map>
#include <vector>
#include <unordered_map>
#include <utility>
#include <fstream>
#include <memory>
#include <unordered_set>
#include <algorithm> #include <algorithm>
#include <iterator> #include <fstream>
#include <functional> #include <functional>
#include <iterator>
#include <map>
#include <memory>
#include <unordered_map>
#include <unordered_set>
#include <utility>
#include <vector>
#include <system_error>
#include <exception> #include <exception>
#include <system_error>
// for wstring support // for wstring support
#include <locale> #include <locale>
@ -76,8 +76,7 @@ namespace tyti
// Helper functions selecting the right encoding (char/wchar_T) // Helper functions selecting the right encoding (char/wchar_T)
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
template <typename T> template <typename T> struct literal_macro_help
struct literal_macro_help
{ {
static CONSTEXPR const char *result(const char *c, const wchar_t *) NOEXCEPT static CONSTEXPR const char *result(const char *c, const wchar_t *) NOEXCEPT
{ {
@ -89,10 +88,10 @@ namespace tyti
} }
}; };
template <> template <> struct literal_macro_help<wchar_t>
struct literal_macro_help<wchar_t>
{ {
static CONSTEXPR const wchar_t* result(const char*, const wchar_t* wc) NOEXCEPT static CONSTEXPR const wchar_t *result(const char *,
const wchar_t *wc) NOEXCEPT
{ {
return wc; return wc;
} }
@ -101,26 +100,28 @@ namespace tyti
return wc; return wc;
} }
}; };
#define TYTI_L(type, text) vdf::detail::literal_macro_help<type>::result(text, L##text) #define TYTI_L(type, text) \
vdf::detail::literal_macro_help<type>::result(text, L##text)
inline std::string string_converter(const std::string& w) NOEXCEPT inline std::string string_converter(const std::string &w) NOEXCEPT { return w; }
{
return w;
}
// utility wrapper to adapt locale-bound facets for wstring/wbuffer convert // utility wrapper to adapt locale-bound facets for wstring/wbuffer convert
// from cppreference // from cppreference
template <class Facet> template <class Facet> struct deletable_facet : Facet
struct deletable_facet : Facet
{ {
template <class... Args> template <class... Args>
deletable_facet(Args &&... args) : Facet(std::forward<Args>(args)...) {} deletable_facet(Args &&...args) : Facet(std::forward<Args>(args)...)
{
}
~deletable_facet() {} ~deletable_facet() {}
}; };
inline std::string string_converter(const std::wstring& w) //todo: use us-locale inline std::string
string_converter(const std::wstring &w) // todo: use us-locale
{ {
std::wstring_convert<deletable_facet<std::codecvt<wchar_t, char, std::mbstate_t>>> conv1; std::wstring_convert<
deletable_facet<std::codecvt<wchar_t, char, std::mbstate_t>>>
conv1;
return conv1.to_bytes(w); return conv1.to_bytes(w);
} }
@ -128,14 +129,16 @@ namespace tyti
// Writer helper functions // Writer helper functions
/////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////
template <typename charT> template <typename charT> class tabs
class tabs
{ {
const size_t t; const size_t t;
public: public:
explicit CONSTEXPR tabs(size_t i) NOEXCEPT : t(i) {} explicit CONSTEXPR tabs(size_t i) NOEXCEPT : t(i) {}
std::basic_string<charT> print() const { return std::basic_string<charT>(t, TYTI_L(charT, '\t')); } std::basic_string<charT> print() const
{
return std::basic_string<charT>(t, TYTI_L(charT, '\t'));
}
inline CONSTEXPR tabs operator+(size_t i) const NOEXCEPT inline CONSTEXPR tabs operator+(size_t i) const NOEXCEPT
{ {
return tabs(t + i); return tabs(t + i);
@ -156,51 +159,54 @@ namespace tyti
/// 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 /// basic object node. Every object has a name and can contains attributes saved
template <typename CharT> /// as key_value pairs or childrens
struct basic_object template <typename CharT> struct basic_object
{ {
typedef CharT char_type; typedef CharT char_type;
std::basic_string<char_type> name; std::basic_string<char_type> name;
std::unordered_map<std::basic_string<char_type>, std::basic_string<char_type>> attribs; std::unordered_map<std::basic_string<char_type>,
std::unordered_map<std::basic_string<char_type>, std::shared_ptr<basic_object<char_type>>> children; std::basic_string<char_type>>
attribs;
std::unordered_map<std::basic_string<char_type>,
std::shared_ptr<basic_object<char_type>>>
childs;
void add_attribute(std::basic_string<char_type> key, std::basic_string<char_type> value) void add_attribute(std::basic_string<char_type> key,
std::basic_string<char_type> value)
{ {
attribs.emplace(std::move(key), std::move(value)); attribs.emplace(std::move(key), std::move(value));
} }
void add_child(std::unique_ptr<basic_object<char_type>> child) void add_child(std::unique_ptr<basic_object<char_type>> child)
{ {
std::shared_ptr<basic_object<char_type>> obj{child.release()}; std::shared_ptr<basic_object<char_type>> obj{child.release()};
children.emplace(obj->name, obj); childs.emplace(obj->name, obj);
}
void set_name(std::basic_string<char_type> n)
{
name = std::move(n);
} }
void set_name(std::basic_string<char_type> n) { name = std::move(n); }
}; };
template <typename CharT> template <typename CharT> struct basic_multikey_object
struct basic_multikey_object
{ {
typedef CharT char_type; typedef CharT char_type;
std::basic_string<char_type> name; std::basic_string<char_type> name;
std::unordered_multimap<std::basic_string<char_type>, std::basic_string<char_type>> attribs; std::unordered_multimap<std::basic_string<char_type>,
std::unordered_multimap<std::basic_string<char_type>, std::shared_ptr<basic_multikey_object<char_type>>> children; std::basic_string<char_type>>
attribs;
std::unordered_multimap<std::basic_string<char_type>,
std::shared_ptr<basic_multikey_object<char_type>>>
childs;
void add_attribute(std::basic_string<char_type> key, std::basic_string<char_type> value) void add_attribute(std::basic_string<char_type> key,
std::basic_string<char_type> value)
{ {
attribs.emplace(std::move(key), std::move(value)); attribs.emplace(std::move(key), std::move(value));
} }
void add_child(std::unique_ptr<basic_multikey_object<char_type>> child) void add_child(std::unique_ptr<basic_multikey_object<char_type>> child)
{ {
std::shared_ptr<basic_multikey_object<char_type>> obj{child.release()}; std::shared_ptr<basic_multikey_object<char_type>> obj{child.release()};
children.emplace(obj->name, obj); childs.emplace(obj->name, obj);
}
void set_name(std::basic_string<char_type> n)
{
name = std::move(n);
} }
void set_name(std::basic_string<char_type> n) { name = std::move(n); }
}; };
typedef basic_object<char> object; typedef basic_object<char> object;
@ -214,7 +220,11 @@ namespace tyti
bool ignore_all_platform_conditionals; bool ignore_all_platform_conditionals;
bool ignore_includes; 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 decls
@ -227,14 +237,17 @@ namespace tyti
*/ */
template <typename oStreamT, typename T> template <typename oStreamT, typename T>
void write(oStreamT &s, const T &r, void write(oStreamT &s, const T &r,
const detail::tabs<typename oStreamT::char_type> tab = detail::tabs<typename oStreamT::char_type>(0)) const detail::tabs<typename oStreamT::char_type> tab =
detail::tabs<typename oStreamT::char_type>(0))
{ {
typedef typename oStreamT::char_type charT; typedef typename oStreamT::char_type charT;
using namespace detail; using namespace detail;
s << tab << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tab << TYTI_L(charT, "{\n"); s << tab << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tab
<< TYTI_L(charT, "{\n");
for (const auto &i : r.attribs) 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"); s << tab + 1 << TYTI_L(charT, '"') << i.first
for (const auto& i : r.children) << TYTI_L(charT, "\"\t\t\"") << i.second << TYTI_L(charT, "\"\n");
for (const auto &i : r.childs)
if (i.second) if (i.second)
write(s, *i.second, tab + 1); write(s, *i.second, tab + 1);
s << tab << TYTI_L(charT, "}\n"); s << tab << TYTI_L(charT, "}\n");
@ -270,12 +283,16 @@ namespace tyti
- "std::bad_alloc" if not enough memory coup be allocated - "std::bad_alloc" if not enough memory coup be allocated
*/ */
template <typename OutputT, typename IterT> template <typename OutputT, typename IterT>
std::vector<std::unique_ptr<OutputT>> read_internal(IterT first, const IterT last, std::vector<std::unique_ptr<OutputT>> read_internal(
std::unordered_set<std::basic_string<typename std::iterator_traits<IterT>::value_type>>& exclude_files, IterT first, const IterT last,
std::unordered_set<
std::basic_string<typename std::iterator_traits<IterT>::value_type>>
&exclude_files,
const Options &opt) const Options &opt)
{ {
static_assert(std::is_default_constructible<OutputT>::value, static_assert(std::is_default_constructible<OutputT>::value,
"Output Type must be default constructible (provide constructor without arguments)"); "Output Type must be default constructible (provide "
"constructor without arguments)");
static_assert(std::is_move_constructible<OutputT>::value, static_assert(std::is_move_constructible<OutputT>::value,
"Output Type must be move constructible"); "Output Type must be move constructible");
@ -285,56 +302,73 @@ namespace tyti
const std::basic_string<charT> whitespaces = TYTI_L(charT, " \n\v\f\r\t"); const std::basic_string<charT> whitespaces = TYTI_L(charT, " \n\v\f\r\t");
#ifdef WIN32 #ifdef WIN32
std::function<bool(const std::basic_string<charT>&)> is_platform_str = [](const std::basic_string<charT>& in) { std::function<bool(const std::basic_string<charT> &)> is_platform_str =
[](const std::basic_string<charT> &in)
{
return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$WINDOWS"); return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$WINDOWS");
}; };
#elif __APPLE__ #elif __APPLE__
// WIN32 stands for pc in general // WIN32 stands for pc in general
std::function<bool(const std::basic_string<charT>&)> is_platform_str = [](const std::basic_string<charT>& in) { std::function<bool(const std::basic_string<charT> &)> is_platform_str =
return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || in == TYTI_L(charT, "$OSX"); [](const std::basic_string<charT> &in)
{
return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") ||
in == TYTI_L(charT, "$OSX");
}; };
#elif __linux__ #elif __linux__
// WIN32 stands for pc in general // WIN32 stands for pc in general
std::function<bool(const std::basic_string<charT>&)> is_platform_str = [](const std::basic_string<charT>& in) { std::function<bool(const std::basic_string<charT> &)> is_platform_str =
return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || in == TYTI_L(charT, "$LINUX"); [](const std::basic_string<charT> &in)
{
return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") ||
in == TYTI_L(charT, "$LINUX");
}; };
#else #else
std::function<bool(const std::basic_string<charT>&)> is_platform_str = [](const std::basic_string<charT>& in) { std::function<bool(const std::basic_string<charT> &)> is_platform_str =
return false; [](const std::basic_string<charT> &in) { return false; };
};
#endif #endif
if (opt.ignore_all_platform_conditionals) if (opt.ignore_all_platform_conditionals)
is_platform_str = [](const std::basic_string<charT>&) { is_platform_str = [](const std::basic_string<charT> &)
return false; { return false; };
};
// function for skipping a comment block // function for skipping a comment block
// iter: iterator poition to the position after a '/' // iter: iterator poition to the position after a '/'
auto skip_comments = [&comment_end_str](IterT iter, const IterT& last) -> IterT { auto skip_comments = [&comment_end_str](IterT iter,
++iter; const IterT &last) -> IterT
if (iter != last)
{ {
++iter;
if (iter == last)
return last;
if (*iter == TYTI_L(charT, '/')) if (*iter == TYTI_L(charT, '/'))
{ {
// line comment, skip whole line // line comment, skip whole line
iter = std::find(iter + 1, last, TYTI_L(charT, '\n')); iter = std::find(iter + 1, last, TYTI_L(charT, '\n'));
if (iter == last)
return last;
} }
if (*iter == '*') if (*iter == '*')
{ {
// block comment, skip until next occurance of "*\" // block comment, skip until next occurance of "*\"
iter = std::search(iter + 1, last, std::begin(comment_end_str), std::end(comment_end_str)); 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; iter += 2;
} }
}
return iter; return iter;
}; };
auto end_quote = [](IterT iter, const IterT& last) -> IterT { auto end_quote = [](IterT iter, const IterT &last) -> IterT
{
const auto begin = iter; const auto begin = iter;
auto last_esc = iter; auto last_esc = iter;
if (iter == last)
throw std::runtime_error{"quote was opened but not closed."};
do do
{ {
++iter; ++iter;
@ -345,48 +379,65 @@ namespace tyti
last_esc = std::prev(iter); last_esc = std::prev(iter);
while (last_esc != begin && *last_esc == '\\') while (last_esc != begin && *last_esc == '\\')
--last_esc; --last_esc;
} while (!(std::distance(last_esc, iter) % 2)); } while (!(std::distance(last_esc, iter) % 2) && iter != last);
if (iter == last) if (iter == last)
throw std::runtime_error{"quote was opened but not closed."}; throw std::runtime_error{"quote was opened but not closed."};
return iter; return iter;
}; };
auto end_word = [&whitespaces](IterT iter, const IterT& last) -> IterT { auto end_word = [&whitespaces](IterT iter, const IterT &last) -> IterT
{
const auto begin = iter; const auto begin = iter;
auto last_esc = iter; auto last_esc = iter;
if (iter == last)
throw std::runtime_error{"quote was opened but not closed."};
do do
{ {
++iter; ++iter;
iter = std::find_first_of(iter, last, std::begin(whitespaces), std::end(whitespaces)); iter = std::find_first_of(iter, last, std::begin(whitespaces),
std::end(whitespaces));
if (iter == last) if (iter == last)
break; break;
last_esc = std::prev(iter); last_esc = std::prev(iter);
while (last_esc != begin && *last_esc == '\\') while (last_esc != begin && *last_esc == '\\')
--last_esc; --last_esc;
} while (!(std::distance(last_esc, iter) % 2)); } while (!(std::distance(last_esc, iter) % 2) && iter != last);
//if (iter == last) if (iter == last)
// throw std::runtime_error{ "word wasnt properly ended" }; throw std::runtime_error{"word wasnt properly ended"};
return iter; return iter;
}; };
auto skip_whitespaces = [&whitespaces](IterT iter, const IterT& last) -> IterT { auto skip_whitespaces = [&whitespaces](IterT iter,
iter = std::find_if_not(iter, last, [&whitespaces](charT c) { const IterT &last) -> IterT
{
if (iter == last)
return iter;
iter = std::find_if_not(iter, last,
[&whitespaces](charT c)
{
// return true if whitespace // return true if whitespace
return std::any_of(std::begin(whitespaces), std::end(whitespaces), [c](charT pc) { return pc == c; }); return std::any_of(std::begin(whitespaces),
std::end(whitespaces),
[c](charT pc)
{ return pc == c; });
}); });
return iter; return iter;
}; };
std::function<void(std::basic_string<charT>&)> strip_escape_symbols = [](std::basic_string<charT>& s) { std::function<void(std::basic_string<charT> &)> strip_escape_symbols =
auto quote_searcher = [&s](size_t pos) { return s.find(TYTI_L(charT, "\\\""), pos); }; [](std::basic_string<charT> &s)
{
auto quote_searcher = [&s](size_t pos)
{ return s.find(TYTI_L(charT, "\\\""), pos); };
auto p = quote_searcher(0); auto p = quote_searcher(0);
while (p != s.npos) while (p != s.npos)
{ {
s.replace(p, 2, TYTI_L(charT, "\"")); s.replace(p, 2, TYTI_L(charT, "\""));
p = quote_searcher(p); p = quote_searcher(p);
} }
auto searcher = [&s](size_t pos) { return s.find(TYTI_L(charT, "\\\\"), pos); }; auto searcher = [&s](size_t pos)
{ return s.find(TYTI_L(charT, "\\\\"), pos); };
p = searcher(0); p = searcher(0);
while (p != s.npos) while (p != s.npos)
{ {
@ -398,12 +449,20 @@ namespace tyti
if (!opt.strip_escape_symbols) if (!opt.strip_escape_symbols)
strip_escape_symbols = [](std::basic_string<charT> &) {}; strip_escape_symbols = [](std::basic_string<charT> &) {};
auto conditional_fullfilled = [&skip_whitespaces, &is_platform_str](IterT& iter, const IterT& last) { auto conditional_fullfilled =
[&skip_whitespaces, &is_platform_str](IterT &iter, const IterT &last)
{
iter = skip_whitespaces(iter, last); iter = skip_whitespaces(iter, last);
if (iter == last)
return true;
if (*iter == '[') if (*iter == '[')
{ {
++iter; ++iter;
if (iter == last)
throw std::runtime_error("conditional not closed");
const auto end = std::find(iter, last, ']'); const auto end = std::find(iter, last, ']');
if (end == last)
throw std::runtime_error("conditional not closed");
const bool negate = *iter == '!'; const bool negate = *iter == '!';
if (negate) if (negate)
++iter; ++iter;
@ -433,23 +492,30 @@ namespace tyti
if (*curIter == TYTI_L(charT, '/')) if (*curIter == TYTI_L(charT, '/'))
{ {
curIter = skip_comments(curIter, last); curIter = skip_comments(curIter, last);
if (curIter == last || *curIter == '\0')
throw std::runtime_error("Unexpected eof");
} }
else if (*curIter != TYTI_L(charT, '}')) else if (*curIter != TYTI_L(charT, '}'))
{ {
// get key // get key
const auto keyEnd = (*curIter == TYTI_L(charT, '\"')) ? end_quote(curIter, last) : end_word(curIter, last); const auto keyEnd = (*curIter == TYTI_L(charT, '\"'))
? end_quote(curIter, last)
: end_word(curIter, last);
if (*curIter == TYTI_L(charT, '\"')) if (*curIter == TYTI_L(charT, '\"'))
++curIter; ++curIter;
std::basic_string<charT> key(curIter, keyEnd); std::basic_string<charT> key(curIter, keyEnd);
strip_escape_symbols(key); strip_escape_symbols(key);
curIter = keyEnd + ((*keyEnd == TYTI_L(charT, '\"')) ? 1 : 0); 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); curIter = skip_whitespaces(curIter, last);
auto conditional = conditional_fullfilled(curIter, last); auto conditional = conditional_fullfilled(curIter, last);
if (!conditional) if (!conditional)
continue; continue;
if (curIter == last)
throw std::runtime_error{"key declared, but no value"};
while (*curIter == TYTI_L(charT, '/')) while (*curIter == TYTI_L(charT, '/'))
{ {
@ -464,20 +530,30 @@ namespace tyti
// get value // get value
if (*curIter != '{') if (*curIter != '{')
{ {
const auto valueEnd = (*curIter == TYTI_L(charT, '\"')) ? end_quote(curIter, last) : end_word(curIter, last); 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, '\"')) if (*curIter == TYTI_L(charT, '\"'))
++curIter; ++curIter;
if (curIter == last)
throw std::runtime_error("No closed word");
auto value = std::basic_string<charT>(curIter, valueEnd); auto value = std::basic_string<charT>(curIter, valueEnd);
strip_escape_symbols(value); strip_escape_symbols(value);
curIter = valueEnd + ((*valueEnd == TYTI_L(charT, '\"')) ? 1 : 0); curIter =
valueEnd + ((*valueEnd == TYTI_L(charT, '\"')) ? 1 : 0);
auto conditional = conditional_fullfilled(curIter, last); auto conditional = conditional_fullfilled(curIter, last);
if (!conditional) if (!conditional)
continue; continue;
// process value // process value
if (key != TYTI_L(charT, "#include") && key != TYTI_L(charT, "#base")) if (key != TYTI_L(charT, "#include") &&
key != TYTI_L(charT, "#base"))
{ {
if (curObj) if (curObj)
{ {
@ -485,17 +561,21 @@ namespace tyti
} }
else else
{ {
throw std::runtime_error{ "unexpected key without object" }; throw std::runtime_error{
"unexpected key without object"};
} }
} }
else else
{ {
if (!opt.ignore_includes && exclude_files.find(value) == exclude_files.end()) if (!opt.ignore_includes &&
exclude_files.find(value) == exclude_files.end())
{ {
exclude_files.insert(value); exclude_files.insert(value);
std::basic_ifstream<charT> i(detail::string_converter(value)); std::basic_ifstream<charT> i(
detail::string_converter(value));
auto str = read_file(i); auto str = read_file(i);
auto file_objs = read_internal<OutputT>(str.begin(), str.end(), exclude_files, opt); auto file_objs = read_internal<OutputT>(
str.begin(), str.end(), exclude_files, opt);
for (auto &n : file_objs) for (auto &n : file_objs)
{ {
if (curObj) if (curObj)
@ -563,8 +643,10 @@ namespace tyti
template <typename OutputT, typename IterT> template <typename OutputT, typename IterT>
OutputT read(IterT first, const IterT last, const Options &opt = Options{}) OutputT read(IterT first, const IterT last, const Options &opt = Options{})
{ {
auto exclude_files = std::unordered_set<std::basic_string<typename std::iterator_traits<IterT>::value_type>>{}; auto exclude_files = std::unordered_set<
auto roots = detail::read_internal<OutputT>(first, last, exclude_files, opt); std::basic_string<typename std::iterator_traits<IterT>::value_type>>{};
auto roots =
detail::read_internal<OutputT>(first, last, exclude_files, opt);
OutputT result; OutputT result;
if (roots.size() > 1) if (roots.size() > 1)
@ -590,7 +672,8 @@ namespace tyti
std::errc::invalid_argument: iterators throws e.g. out of range std::errc::invalid_argument: iterators throws e.g. out of range
*/ */
template <typename OutputT, typename IterT> template <typename OutputT, typename IterT>
OutputT read(IterT first, IterT last, std::error_code& ec, const Options& opt = Options{}) NOEXCEPT OutputT read(IterT first, IterT last, std::error_code &ec,
const Options &opt = Options{}) NOEXCEPT
{ {
ec.clear(); ec.clear();
@ -621,7 +704,8 @@ namespace tyti
@param ok output bool. true, if parser successed, false, if parser failed @param ok output bool. true, if parser successed, false, if parser failed
*/ */
template <typename OutputT, typename IterT> template <typename OutputT, typename IterT>
OutputT read(IterT first, const IterT last, bool* ok, const Options& opt = Options{}) NOEXCEPT OutputT read(IterT first, const IterT last, bool *ok,
const Options &opt = Options{}) NOEXCEPT
{ {
std::error_code ec; std::error_code ec;
auto r = read<OutputT>(first, last, ec, opt); auto r = read<OutputT>(first, last, ec, opt);
@ -631,30 +715,37 @@ namespace tyti
} }
template <typename IterT> template <typename IterT>
inline auto read(IterT first, const IterT last, bool* ok, const Options& opt = Options{}) NOEXCEPT -> basic_object<typename std::iterator_traits<IterT>::value_type> inline auto read(IterT first, const IterT last, bool *ok,
const Options &opt = Options{}) NOEXCEPT
->basic_object<typename std::iterator_traits<IterT>::value_type>
{ {
return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(first, last, ok, opt); return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(
first, last, ok, opt);
} }
template <typename IterT> template <typename IterT>
inline auto read(IterT first, IterT last, std::error_code& ec, const Options& opt = Options{}) NOEXCEPT inline auto read(IterT first, IterT last, std::error_code &ec,
const Options &opt = Options{}) NOEXCEPT
->basic_object<typename std::iterator_traits<IterT>::value_type> ->basic_object<typename std::iterator_traits<IterT>::value_type>
{ {
return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(first, last, ec, opt); return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(
first, last, ec, opt);
} }
template <typename IterT> template <typename IterT>
inline auto read(IterT first, const IterT last, const Options &opt = Options{}) inline auto read(IterT first, const IterT last, const Options &opt = Options{})
-> basic_object<typename std::iterator_traits<IterT>::value_type> -> basic_object<typename std::iterator_traits<IterT>::value_type>
{ {
return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(first, last, opt); return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(
first, last, opt);
} }
/** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf
throws "std::bad_alloc" if file buffer could not be allocated formatted data. throws "std::bad_alloc" if file buffer could not be allocated
*/ */
template <typename OutputT, typename iStreamT> template <typename OutputT, typename iStreamT>
OutputT read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{}) OutputT read(iStreamT &inStream, std::error_code &ec,
const Options &opt = Options{})
{ {
// cache the file // cache the file
typedef typename iStreamT::char_type charT; typedef typename iStreamT::char_type charT;
@ -665,13 +756,14 @@ namespace tyti
} }
template <typename iStreamT> template <typename iStreamT>
inline basic_object<typename iStreamT::char_type> read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{}) inline basic_object<typename iStreamT::char_type>
read(iStreamT &inStream, std::error_code &ec, const Options &opt = Options{})
{ {
return read<basic_object<typename iStreamT::char_type>>(inStream, ec, opt); return read<basic_object<typename iStreamT::char_type>>(inStream, ec, opt);
} }
/** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf
throws "std::bad_alloc" if file buffer could not be allocated formatted data. throws "std::bad_alloc" if file buffer could not be allocated
ok == false, if a parsing error occured ok == false, if a parsing error occured
*/ */
template <typename OutputT, typename iStreamT> template <typename OutputT, typename iStreamT>
@ -685,13 +777,14 @@ namespace tyti
} }
template <typename iStreamT> template <typename iStreamT>
inline basic_object<typename iStreamT::char_type> read(iStreamT& inStream, bool* ok, const Options& opt = Options{}) inline basic_object<typename iStreamT::char_type>
read(iStreamT &inStream, bool *ok, const Options &opt = Options{})
{ {
return read<basic_object<typename iStreamT::char_type>>(inStream, ok, opt); return read<basic_object<typename iStreamT::char_type>>(inStream, ok, opt);
} }
/** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf
throws "std::bad_alloc" if file buffer could not be allocated formatted data. throws "std::bad_alloc" if file buffer could not be allocated
throws "std::runtime_error" if a parsing error occured throws "std::runtime_error" if a parsing error occured
*/ */
template <typename OutputT, typename iStreamT> template <typename OutputT, typename iStreamT>
@ -706,7 +799,8 @@ namespace tyti
} }
template <typename iStreamT> template <typename iStreamT>
inline basic_object<typename iStreamT::char_type> read(iStreamT& inStream, const Options& opt = Options{}) inline basic_object<typename iStreamT::char_type>
read(iStreamT &inStream, const Options &opt = Options{})
{ {
return read<basic_object<typename iStreamT::char_type>>(inStream, opt); return read<basic_object<typename iStreamT::char_type>>(inStream, opt);
} }

View file

@ -79,9 +79,9 @@ find_steamvr_install()
std::ifstream file(STEAM_INSTALL_DIR + "/steamapps/libraryfolders.vdf"); std::ifstream file(STEAM_INSTALL_DIR + "/steamapps/libraryfolders.vdf");
auto root = vdf::read(file); auto root = vdf::read(file);
assert(root.name == "libraryfolders"); 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()); U_LOG_D("Found library folder %s", child->attribs["path"].c_str());
std::shared_ptr<vdf::object> apps = child->children["apps"]; std::shared_ptr<vdf::object> apps = child->childs["apps"];
for (auto &[appid, _] : apps->attribs) { for (auto &[appid, _] : apps->attribs) {
if (appid == STEAMVR_APPID) { if (appid == STEAMVR_APPID) {
return child->attribs["path"] + "/steamapps/common/SteamVR"; return child->attribs["path"] + "/steamapps/common/SteamVR";