Compare commits
No commits in common. "19251a253f378461a605a402e5667e6a55fc450d" and "81732eab033f5ab0c82bfc69c269fd9517de4234" have entirely different histories.
19251a253f
...
81732eab03
28
.vscode/launch.json
vendored
28
.vscode/launch.json
vendored
@ -1,28 +0,0 @@
|
|||||||
{
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "(gdb) Launch",
|
|
||||||
"type": "cppdbg",
|
|
||||||
"request": "launch",
|
|
||||||
"program": "${workspaceFolder}/build/cellar",
|
|
||||||
"args": ["steamtest"],
|
|
||||||
"stopAtEntry": false,
|
|
||||||
"cwd": "${fileDirname}",
|
|
||||||
"environment": [],
|
|
||||||
"externalConsole": false,
|
|
||||||
"MIMode": "gdb",
|
|
||||||
"setupCommands": [
|
|
||||||
{
|
|
||||||
"description": "Enable pretty-printing for gdb",
|
|
||||||
"text": "-enable-pretty-printing",
|
|
||||||
"ignoreFailures": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"description": "Set Disassembly Flavor to Intel",
|
|
||||||
"text": "-gdb-set disassembly-flavor intel",
|
|
||||||
"ignoreFailures": true
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
@ -1,9 +1,9 @@
|
|||||||
cmake_minimum_required(VERSION 3.27.2)
|
cmake_minimum_required(VERSION 3.7.2)
|
||||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
|
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/Modules)
|
||||||
include(Colours)
|
include(Colours)
|
||||||
include(Platform)
|
include(Platform)
|
||||||
|
|
||||||
project(cellar VERSION 0.5 LANGUAGES CXX)
|
project(cellar CXX)
|
||||||
string(TIMESTAMP BUILD_DATE "%Y.%m.%d %H:%M:%S UTC" UTC)
|
string(TIMESTAMP BUILD_DATE "%Y.%m.%d %H:%M:%S UTC" UTC)
|
||||||
# local cmake modules
|
# local cmake modules
|
||||||
|
|
||||||
@ -14,9 +14,6 @@ SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib/cellar")
|
|||||||
# which point to directories outside the build tree to the install RPATH
|
# which point to directories outside the build tree to the install RPATH
|
||||||
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
SET(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE)
|
||||||
|
|
||||||
# for build time config
|
|
||||||
set(ENABLE_STEAM TRUE CACHE BOOL "Whether or not to support bottles managed by Steam.")
|
|
||||||
|
|
||||||
include(Git)
|
include(Git)
|
||||||
git_init()
|
git_init()
|
||||||
|
|
||||||
@ -40,33 +37,24 @@ http://tclap.sourceforge.net and put the headers in ./include
|
|||||||
(wink, nudge)")
|
(wink, nudge)")
|
||||||
endif(NOT TCLAP_FOUND)
|
endif(NOT TCLAP_FOUND)
|
||||||
|
|
||||||
#include(LavaTargets)
|
include(LavaTargets)
|
||||||
include(Binaries)
|
|
||||||
|
|
||||||
include_directories(include)
|
include_directories(include)
|
||||||
configure_file("${CMAKE_SOURCE_DIR}/include/cmake.hpp.in"
|
configure_file("${CMAKE_SOURCE_DIR}/include/cmake.hpp.in"
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/include/cmake.hpp")
|
"${CMAKE_CURRENT_BINARY_DIR}/include/cmake.hpp")
|
||||||
configure_file("${CMAKE_SOURCE_DIR}/Doxyfile.in"
|
|
||||||
"${CMAKE_CURRENT_BINARY_DIR}/Doxyfile")
|
|
||||||
include_directories("${CMAKE_CURRENT_BINARY_DIR}/include")
|
include_directories("${CMAKE_CURRENT_BINARY_DIR}/include")
|
||||||
set(src "${CMAKE_SOURCE_DIR}/src")
|
set(src "${CMAKE_SOURCE_DIR}/src")
|
||||||
|
|
||||||
set(libcellar_subdirs bottles config launch paths)
|
lava_create_gutlib(
|
||||||
if(ENABLE_STEAM)
|
SUBDIRS bottles config launch paths
|
||||||
list(APPEND libcellar_subdirs steam)
|
|
||||||
endif()
|
|
||||||
build_library(TARGET libcellar
|
|
||||||
SUBDIRS ${libcellar_subdirs}
|
|
||||||
DEPENDS cog)
|
DEPENDS cog)
|
||||||
set_target_properties(libcellar PROPERTIES PREFIX "")
|
|
||||||
|
|
||||||
build_executable(TARGET cellar
|
lava_create_executable(TARGET cellar
|
||||||
SUBDIRS core help
|
SUBDIRS core help
|
||||||
LIBRARIES libcellar Boost::filesystem Boost::system
|
LIBRARIES gutlib Boost::filesystem Boost::system
|
||||||
DEPENDS cog)
|
DEPENDS cog)
|
||||||
|
|
||||||
install(TARGETS cellar libcellar
|
install(TARGETS cellar gutlib
|
||||||
RUNTIME DESTINATION bin
|
RUNTIME DESTINATION bin
|
||||||
LIBRARY DESTINATION lib/cellar
|
LIBRARY DESTINATION lib/cellar
|
||||||
ARCHIVE DESTINATION share/cellar)
|
ARCHIVE DESTINATION share/cellar)
|
||||||
message(STATUS "If you have Doxygen installed, you can run it from this directory to generate documentation.")
|
|
2917
Doxyfile.in
2917
Doxyfile.in
File diff suppressed because it is too large
Load Diff
2
LICENSE
2
LICENSE
@ -1,7 +1,7 @@
|
|||||||
Unless otherwise specified, any and all code in this repository is
|
Unless otherwise specified, any and all code in this repository is
|
||||||
provided under the terms of the MIT license, as below:
|
provided under the terms of the MIT license, as below:
|
||||||
|
|
||||||
Copyright (c) 2017-2025 Nicole O'Connor
|
Copyright (c) 2017 Nicholas O'Connor
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
a copy of this software and associated documentation files (the
|
a copy of this software and associated documentation files (the
|
||||||
|
@ -1,82 +0,0 @@
|
|||||||
include(GenerateExportHeader)
|
|
||||||
|
|
||||||
link_directories(${CMAKE_CURRENT_BINARY_DIR})
|
|
||||||
set(BUILD_SHARED_LIBS ON)
|
|
||||||
if (MSVC)
|
|
||||||
add_compile_options("/utf-8")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
function(build_library)
|
|
||||||
set(multiValueArgs SUBDIRS DEPENDS LIBRARIES)
|
|
||||||
set(oneValueArgs TARGET)
|
|
||||||
cmake_parse_arguments(build_library "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
||||||
|
|
||||||
set(target ${build_library_TARGET})
|
|
||||||
set(targetsources)
|
|
||||||
foreach(subdir ${build_library_SUBDIRS})
|
|
||||||
set(found_files)
|
|
||||||
#file(GLOB_RECURSE found_files RELATIVE "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/src/${subdir}/*.cpp")
|
|
||||||
cog_sources("src/${subdir}/*.cpp" found_files)
|
|
||||||
set(targetsources ${targetsources} ${found_files})
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if (MSVC AND EXISTS "${CMAKE_SOURCE_DIR}/res/${target}.rc")
|
|
||||||
set(targetsources ${targetsources} "res/${target}.rc")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_library(${target} SHARED ${targetsources})
|
|
||||||
if (MSVC)
|
|
||||||
set_target_properties(${target} PROPERTIES PREFIX "")
|
|
||||||
set_target_properties(${target} PROPERTIES LINK_FLAGS "/MAP")
|
|
||||||
GENERATE_EXPORT_HEADER(${target}
|
|
||||||
BASE_NAME ${target}
|
|
||||||
EXPORT_MACRO_NAME ${target}_EXPORT
|
|
||||||
EXPORT_FILE_NAME include/${target}_Export.h
|
|
||||||
STATIC_DEFINE ${target}_BUILT_AS_STATIC)
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (build_library_LIBRARIES)
|
|
||||||
foreach (library ${build_library_LIBRARIES})
|
|
||||||
target_link_libraries(${target} ${library})
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (build_library_DEPENDS)
|
|
||||||
add_dependencies(${target} ${build_library_DEPENDS})
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
||||||
|
|
||||||
function(build_executable)
|
|
||||||
set(multiValueArgs SUBDIRS DEPENDS LIBRARIES)
|
|
||||||
set(oneValueArgs TARGET)
|
|
||||||
cmake_parse_arguments(build_executable "" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
|
|
||||||
|
|
||||||
set(target ${build_executable_TARGET})
|
|
||||||
set(targetsources)
|
|
||||||
foreach(subdir ${build_executable_SUBDIRS})
|
|
||||||
set(found_files)
|
|
||||||
#file(GLOB_RECURSE found_files RELATIVE "${CMAKE_SOURCE_DIR}" "${CMAKE_SOURCE_DIR}/src/${subdir}/*.cpp")
|
|
||||||
cog_sources("src/${subdir}/*.cpp" found_files)
|
|
||||||
set(targetsources ${targetsources} ${found_files})
|
|
||||||
endforeach()
|
|
||||||
|
|
||||||
if (MSVC AND EXISTS "${CMAKE_SOURCE_DIR}/res/${target}.rc")
|
|
||||||
set(targetsources ${targetsources} "res/${target}.rc")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
add_executable(${target} ${targetsources})
|
|
||||||
if (MSVC)
|
|
||||||
# tells MSVC to use int main() but still hide the console window
|
|
||||||
set_target_properties(${target} PROPERTIES LINK_FLAGS "/SUBSYSTEM:windows /ENTRY:mainCRTStartup")
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (build_executable_LIBRARIES)
|
|
||||||
foreach (library ${build_executable_LIBRARIES})
|
|
||||||
target_link_libraries(${target} ${library})
|
|
||||||
endforeach()
|
|
||||||
endif()
|
|
||||||
|
|
||||||
if (build_executable_DEPENDS)
|
|
||||||
add_dependencies(${target} ${build_executable_DEPENDS})
|
|
||||||
endif()
|
|
||||||
endfunction()
|
|
@ -1,4 +1,4 @@
|
|||||||
find_package(Python3 COMPONENTS Interpreter)
|
find_package(PythonInterp)
|
||||||
find_package(PythonModule)
|
find_package(PythonModule)
|
||||||
find_python_module(cogapp REQUIRED)
|
find_python_module(cogapp REQUIRED)
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ macro(cog_target)
|
|||||||
set(thisfile "${CMAKE_CURRENT_BINARY_DIR}/${outfile}")
|
set(thisfile "${CMAKE_CURRENT_BINARY_DIR}/${outfile}")
|
||||||
|
|
||||||
add_custom_command(OUTPUT "${thisfile}" PRE_BUILD
|
add_custom_command(OUTPUT "${thisfile}" PRE_BUILD
|
||||||
COMMAND ${Python3_EXECUTABLE} -m cogapp -d -o "${thisfile}" "${cogfile}"
|
COMMAND ${PYTHON_EXECUTABLE} -m cogapp -d -o "${thisfile}" "${cogfile}"
|
||||||
DEPENDS ${cogfile}
|
DEPENDS ${cogfile}
|
||||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||||
COMMENT "Greasing the cog for ${BoldCyan}${outfile}${ColourReset}")
|
COMMENT "Greasing the cog for ${BoldCyan}${outfile}${ColourReset}")
|
@ -1,4 +1,4 @@
|
|||||||
set(CMAKE_CXX_STANDARD 20)
|
set(CMAKE_CXX_STANDARD 11)
|
||||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
set(PLATFORM_FOREIGN_ENV FALSE)
|
set(PLATFORM_FOREIGN_ENV FALSE)
|
3
cogrc
3
cogrc
@ -1,6 +1,5 @@
|
|||||||
[version]
|
[version]
|
||||||
release_version=0.5
|
release_version=0.4
|
||||||
|
|
||||||
[defaults]
|
[defaults]
|
||||||
wine-path=wine
|
wine-path=wine
|
||||||
cellar-bottle-path=~/.local/share/cellar/bottles
|
|
@ -3,4 +3,3 @@
|
|||||||
#cmakedefine IS_GIT_REPO "@IS_GIT_REPO@"
|
#cmakedefine IS_GIT_REPO "@IS_GIT_REPO@"
|
||||||
#cmakedefine GIT_COMMIT_HASH "@GIT_COMMIT_HASH@"
|
#cmakedefine GIT_COMMIT_HASH "@GIT_COMMIT_HASH@"
|
||||||
#cmakedefine GIT_BRANCH "@GIT_BRANCH@"
|
#cmakedefine GIT_BRANCH "@GIT_BRANCH@"
|
||||||
#cmakedefine ENABLE_STEAM "@ENABLE_STEAM@"
|
|
@ -1,26 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "commands.hpp"
|
|
||||||
|
|
||||||
namespace cellar {
|
|
||||||
namespace steam {
|
|
||||||
extern std::vector<std::string> find_steam_libraries();
|
|
||||||
extern void test_command(int argc, std::vector<std::string> argv);
|
|
||||||
/*[[[cog
|
|
||||||
import cog
|
|
||||||
|
|
||||||
with open("src/steam/commands.txt") as commandsfile:
|
|
||||||
for line in commandsfile:
|
|
||||||
item = line.strip().split(" ")
|
|
||||||
cog.outl("extern void {0} (int, vector<string>);".format(item[1]))
|
|
||||||
]]]*/
|
|
||||||
//[[[end]]]
|
|
||||||
}
|
|
||||||
namespace commands {
|
|
||||||
extern std::map<std::string,cellar::commands::CommandFunction> steam_commands();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
|
|
||||||
#include "bottles.hpp"
|
|
||||||
|
|
||||||
namespace cellar {
|
|
||||||
namespace steam {
|
|
||||||
extern cellar::bottles::Bottle app_bottle(unsigned appid);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,855 +0,0 @@
|
|||||||
// source: https://github.com/TinyTinni/ValveFileVDF/releases/tag/v1.1.0
|
|
||||||
// MIT License
|
|
||||||
//
|
|
||||||
// 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 :
|
|
||||||
//
|
|
||||||
// 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.
|
|
||||||
|
|
||||||
#ifndef __TYTI_STEAM_VDF_PARSER_H__
|
|
||||||
#define __TYTI_STEAM_VDF_PARSER_H__
|
|
||||||
|
|
||||||
#include <algorithm>
|
|
||||||
#include <fstream>
|
|
||||||
#include <functional>
|
|
||||||
#include <iterator>
|
|
||||||
#include <map>
|
|
||||||
#include <memory>
|
|
||||||
#include <unordered_map>
|
|
||||||
#include <unordered_set>
|
|
||||||
#include <utility>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include <exception>
|
|
||||||
#include <system_error>
|
|
||||||
|
|
||||||
// for wstring support
|
|
||||||
#include <cwchar>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
// internal
|
|
||||||
#include <stack>
|
|
||||||
|
|
||||||
// VS < 2015 has only partial C++11 support
|
|
||||||
#if defined(_MSC_VER) && _MSC_VER < 1900
|
|
||||||
#ifndef CONSTEXPR
|
|
||||||
#define CONSTEXPR
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NOEXCEPT
|
|
||||||
#define NOEXCEPT
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#ifndef CONSTEXPR
|
|
||||||
#define CONSTEXPR constexpr
|
|
||||||
#define TYTI_UNDEF_CONSTEXPR
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifndef NOEXCEPT
|
|
||||||
#define NOEXCEPT noexcept
|
|
||||||
#define TYTI_UNDEF_NOEXCEPT
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace tyti
|
|
||||||
{
|
|
||||||
namespace vdf
|
|
||||||
{
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Helper functions selecting the right encoding (char/wchar_T)
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
template <typename T> struct literal_macro_help
|
|
||||||
{
|
|
||||||
static CONSTEXPR const char *result(const char *c, const wchar_t *) NOEXCEPT
|
|
||||||
{
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
static CONSTEXPR char result(const char c, const wchar_t) NOEXCEPT
|
|
||||||
{
|
|
||||||
return c;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <> struct literal_macro_help<wchar_t>
|
|
||||||
{
|
|
||||||
static CONSTEXPR const wchar_t *result(const char *,
|
|
||||||
const wchar_t *wc) NOEXCEPT
|
|
||||||
{
|
|
||||||
return wc;
|
|
||||||
}
|
|
||||||
static CONSTEXPR wchar_t result(const char, const wchar_t wc) NOEXCEPT
|
|
||||||
{
|
|
||||||
return wc;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#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 { return w; }
|
|
||||||
|
|
||||||
inline std::string string_converter(const std::wstring &w) NOEXCEPT
|
|
||||||
{
|
|
||||||
std::mbstate_t state = std::mbstate_t();
|
|
||||||
auto wstr = w.data();
|
|
||||||
// unsafe: ignores any error handling
|
|
||||||
// and disables warning that wcsrtombs_s should be used
|
|
||||||
#ifdef WIN32
|
|
||||||
#pragma warning(push)
|
|
||||||
#pragma warning(disable : 4996)
|
|
||||||
#endif
|
|
||||||
std::size_t len = 1 + std::wcsrtombs(nullptr, &wstr, 0, &state);
|
|
||||||
std::string mbstr(len, '\0');
|
|
||||||
std::wcsrtombs(&mbstr[0], &wstr, mbstr.size(), &state);
|
|
||||||
#ifdef WIN32
|
|
||||||
#pragma warning(pop)
|
|
||||||
#endif
|
|
||||||
return mbstr;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Writer helper functions
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
template <typename charT> class tabs
|
|
||||||
{
|
|
||||||
const size_t t;
|
|
||||||
|
|
||||||
public:
|
|
||||||
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'));
|
|
||||||
}
|
|
||||||
inline CONSTEXPR tabs operator+(size_t i) const NOEXCEPT
|
|
||||||
{
|
|
||||||
return tabs(t + i);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename oStreamT>
|
|
||||||
oStreamT &operator<<(oStreamT &s, const tabs<typename oStreamT::char_type> t)
|
|
||||||
{
|
|
||||||
s << t.print();
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename charT>
|
|
||||||
std::basic_string<charT> escape(std::basic_string<charT> in)
|
|
||||||
{
|
|
||||||
// std::replace_if(in.begin(), end.begin(), [](const charT))
|
|
||||||
for (size_t i = 0; i < in.size(); ++i)
|
|
||||||
{
|
|
||||||
if (in[i] == TYTI_L(charT, '\"') || in[i] == TYTI_L(charT, '\\'))
|
|
||||||
{
|
|
||||||
in.insert(i, TYTI_L(charT, "\\"));
|
|
||||||
++i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return in;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // end namespace detail
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Interface
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
/// 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 <typename CharT> struct basic_object
|
|
||||||
{
|
|
||||||
typedef CharT char_type;
|
|
||||||
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;
|
|
||||||
|
|
||||||
void add_attribute(std::basic_string<char_type> key,
|
|
||||||
std::basic_string<char_type> value)
|
|
||||||
{
|
|
||||||
attribs.emplace(std::move(key), std::move(value));
|
|
||||||
}
|
|
||||||
void add_child(std::unique_ptr<basic_object<char_type>> child)
|
|
||||||
{
|
|
||||||
std::shared_ptr<basic_object<char_type>> obj{child.release()};
|
|
||||||
childs.emplace(obj->name, obj);
|
|
||||||
}
|
|
||||||
void set_name(std::basic_string<char_type> n) { name = std::move(n); }
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename CharT> struct basic_multikey_object
|
|
||||||
{
|
|
||||||
typedef CharT char_type;
|
|
||||||
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_multikey_object<char_type>>>
|
|
||||||
childs;
|
|
||||||
|
|
||||||
void add_attribute(std::basic_string<char_type> key,
|
|
||||||
std::basic_string<char_type> value)
|
|
||||||
{
|
|
||||||
attribs.emplace(std::move(key), std::move(value));
|
|
||||||
}
|
|
||||||
void add_child(std::unique_ptr<basic_multikey_object<char_type>> child)
|
|
||||||
{
|
|
||||||
std::shared_ptr<basic_multikey_object<char_type>> obj{child.release()};
|
|
||||||
childs.emplace(obj->name, obj);
|
|
||||||
}
|
|
||||||
void set_name(std::basic_string<char_type> n) { name = std::move(n); }
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef basic_object<char> object;
|
|
||||||
typedef basic_object<wchar_t> wobject;
|
|
||||||
typedef basic_multikey_object<char> multikey_object;
|
|
||||||
typedef basic_multikey_object<wchar_t> wmultikey_object;
|
|
||||||
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct WriteOptions
|
|
||||||
{
|
|
||||||
bool escape_symbols;
|
|
||||||
WriteOptions() : escape_symbols(true) {}
|
|
||||||
};
|
|
||||||
|
|
||||||
// forward decls
|
|
||||||
// forward decl
|
|
||||||
template <typename OutputT, typename iStreamT>
|
|
||||||
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 <typename oStreamT, typename T>
|
|
||||||
void write(oStreamT &s, const T &r, const WriteOptions &opts = {},
|
|
||||||
const detail::tabs<typename oStreamT::char_type> tab =
|
|
||||||
detail::tabs<typename oStreamT::char_type>(0))
|
|
||||||
{
|
|
||||||
typedef typename oStreamT::char_type charT;
|
|
||||||
using namespace detail;
|
|
||||||
auto escapeFunction = [&opts](const std::basic_string<charT> &in)
|
|
||||||
{
|
|
||||||
if (opts.escape_symbols)
|
|
||||||
return escape(std::move(in));
|
|
||||||
return in;
|
|
||||||
};
|
|
||||||
|
|
||||||
s << tab << TYTI_L(charT, '"') << escapeFunction(r.name)
|
|
||||||
<< TYTI_L(charT, "\"\n") << tab << TYTI_L(charT, "{\n");
|
|
||||||
for (const auto &i : r.attribs)
|
|
||||||
s << tab + 1 << TYTI_L(charT, '"') << escapeFunction(i.first)
|
|
||||||
<< TYTI_L(charT, "\"\t\t\"") << escapeFunction(i.second)
|
|
||||||
<< TYTI_L(charT, "\"\n");
|
|
||||||
for (const auto &i : r.childs)
|
|
||||||
if (i.second)
|
|
||||||
write(s, *i.second, opts, tab + 1);
|
|
||||||
s << tab << TYTI_L(charT, "}\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace detail
|
|
||||||
{
|
|
||||||
template <typename iStreamT>
|
|
||||||
std::basic_string<typename iStreamT::char_type> read_file(iStreamT &inStream)
|
|
||||||
{
|
|
||||||
// cache the file
|
|
||||||
typedef typename iStreamT::char_type charT;
|
|
||||||
std::basic_string<charT> str;
|
|
||||||
inStream.seekg(0, std::ios::end);
|
|
||||||
str.resize(static_cast<size_t>(inStream.tellg()));
|
|
||||||
if (str.empty())
|
|
||||||
return str;
|
|
||||||
|
|
||||||
inStream.seekg(0, std::ios::beg);
|
|
||||||
inStream.read(&str[0], static_cast<std::streamsize>(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
|
|
||||||
|
|
||||||
can thow:
|
|
||||||
- "std::runtime_error" if a parsing error occured
|
|
||||||
- "std::bad_alloc" if not enough memory coup be allocated
|
|
||||||
*/
|
|
||||||
template <typename OutputT, typename IterT>
|
|
||||||
std::vector<std::unique_ptr<OutputT>> read_internal(
|
|
||||||
IterT first, const IterT last,
|
|
||||||
std::unordered_set<
|
|
||||||
std::basic_string<typename std::iterator_traits<IterT>::value_type>>
|
|
||||||
&exclude_files,
|
|
||||||
const Options &opt)
|
|
||||||
{
|
|
||||||
static_assert(std::is_default_constructible<OutputT>::value,
|
|
||||||
"Output Type must be default constructible (provide "
|
|
||||||
"constructor without arguments)");
|
|
||||||
static_assert(std::is_move_constructible<OutputT>::value,
|
|
||||||
"Output Type must be move constructible");
|
|
||||||
|
|
||||||
typedef typename std::iterator_traits<IterT>::value_type charT;
|
|
||||||
|
|
||||||
const std::basic_string<charT> comment_end_str = TYTI_L(charT, "*/");
|
|
||||||
const std::basic_string<charT> whitespaces = TYTI_L(charT, " \n\v\f\r\t");
|
|
||||||
|
|
||||||
#ifdef WIN32
|
|
||||||
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");
|
|
||||||
};
|
|
||||||
#elif __APPLE__
|
|
||||||
// WIN32 stands for pc in general
|
|
||||||
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, "$POSIX") ||
|
|
||||||
in == TYTI_L(charT, "$OSX");
|
|
||||||
};
|
|
||||||
|
|
||||||
#elif __linux__
|
|
||||||
// WIN32 stands for pc in general
|
|
||||||
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, "$POSIX") ||
|
|
||||||
in == TYTI_L(charT, "$LINUX");
|
|
||||||
};
|
|
||||||
#else
|
|
||||||
std::function<bool(const std::basic_string<charT> &)> is_platform_str =
|
|
||||||
[](const std::basic_string<charT> &in) { return false; };
|
|
||||||
#endif
|
|
||||||
|
|
||||||
if (opt.ignore_all_platform_conditionals)
|
|
||||||
is_platform_str = [](const std::basic_string<charT> &)
|
|
||||||
{ 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)
|
|
||||||
return last;
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
return iter;
|
|
||||||
};
|
|
||||||
|
|
||||||
auto end_quote = [opt](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);
|
|
||||||
if (opt.strip_escape_symbols)
|
|
||||||
{
|
|
||||||
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 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;
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
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;
|
|
||||||
};
|
|
||||||
|
|
||||||
std::function<void(std::basic_string<charT> &)> strip_escape_symbols =
|
|
||||||
[](std::basic_string<charT> &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 + 1);
|
|
||||||
}
|
|
||||||
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 + 1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!opt.strip_escape_symbols)
|
|
||||||
strip_escape_symbols = [](std::basic_string<charT> &) {};
|
|
||||||
|
|
||||||
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<charT>(iter, end);
|
|
||||||
|
|
||||||
const bool is_platform = is_platform_str(conditional);
|
|
||||||
iter = end + 1;
|
|
||||||
|
|
||||||
return static_cast<bool>(is_platform ^ negate);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
};
|
|
||||||
|
|
||||||
// read header
|
|
||||||
// first, quoted name
|
|
||||||
std::unique_ptr<OutputT> curObj = nullptr;
|
|
||||||
std::vector<std::unique_ptr<OutputT>> roots;
|
|
||||||
std::stack<std::unique_ptr<OutputT>> 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<charT> 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);
|
|
||||||
|
|
||||||
if (!conditional_fullfilled(curIter, last))
|
|
||||||
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<charT>(curIter, valueEnd);
|
|
||||||
strip_escape_symbols(value);
|
|
||||||
curIter =
|
|
||||||
valueEnd + ((*valueEnd == TYTI_L(charT, '\"')) ? 1 : 0);
|
|
||||||
|
|
||||||
if (!conditional_fullfilled(curIter, last))
|
|
||||||
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<charT> i(
|
|
||||||
detail::string_converter(value));
|
|
||||||
auto str = read_file(i);
|
|
||||||
auto file_objs = read_internal<OutputT>(
|
|
||||||
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<OutputT>();
|
|
||||||
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<OutputT> 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;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw std::runtime_error{"unexpected '}'"};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (curObj != nullptr || !lvls.empty())
|
|
||||||
{
|
|
||||||
throw std::runtime_error{"object is not closed with '}'"};
|
|
||||||
}
|
|
||||||
|
|
||||||
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 <typename OutputT, typename IterT>
|
|
||||||
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 roots =
|
|
||||||
detail::read_internal<OutputT>(first, last, exclude_files, 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]);
|
|
||||||
|
|
||||||
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 <typename OutputT, typename IterT>
|
|
||||||
OutputT read(IterT first, IterT last, std::error_code &ec,
|
|
||||||
const Options &opt = Options{}) NOEXCEPT
|
|
||||||
|
|
||||||
{
|
|
||||||
ec.clear();
|
|
||||||
OutputT r{};
|
|
||||||
try
|
|
||||||
{
|
|
||||||
r = read<OutputT>(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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \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>
|
|
||||||
OutputT read(IterT first, const IterT last, bool *ok,
|
|
||||||
const Options &opt = Options{}) NOEXCEPT
|
|
||||||
{
|
|
||||||
std::error_code ec;
|
|
||||||
auto r = read<OutputT>(first, last, ec, opt);
|
|
||||||
if (ok)
|
|
||||||
*ok = !ec;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
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>
|
|
||||||
{
|
|
||||||
return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(
|
|
||||||
first, last, ok, opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename IterT>
|
|
||||||
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>
|
|
||||||
{
|
|
||||||
return read<basic_object<typename std::iterator_traits<IterT>::value_type>>(
|
|
||||||
first, last, ec, opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename IterT>
|
|
||||||
inline auto read(IterT first, const IterT last, const Options &opt = Options{})
|
|
||||||
-> basic_object<typename std::iterator_traits<IterT>::value_type>
|
|
||||||
{
|
|
||||||
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. throws "std::bad_alloc" if file buffer could not be allocated
|
|
||||||
*/
|
|
||||||
template <typename OutputT, typename iStreamT>
|
|
||||||
OutputT read(iStreamT &inStream, std::error_code &ec,
|
|
||||||
const Options &opt = Options{})
|
|
||||||
{
|
|
||||||
// cache the file
|
|
||||||
typedef typename iStreamT::char_type charT;
|
|
||||||
std::basic_string<charT> str = detail::read_file(inStream);
|
|
||||||
|
|
||||||
// parse it
|
|
||||||
return read<OutputT>(str.begin(), str.end(), ec, opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename iStreamT>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \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>
|
|
||||||
OutputT read(iStreamT &inStream, bool *ok, const Options &opt = Options{})
|
|
||||||
{
|
|
||||||
std::error_code ec;
|
|
||||||
const auto r = read<OutputT>(inStream, ec, opt);
|
|
||||||
if (ok)
|
|
||||||
*ok = !ec;
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename iStreamT>
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** \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 <typename OutputT, typename iStreamT>
|
|
||||||
OutputT read(iStreamT &inStream, const Options &opt)
|
|
||||||
{
|
|
||||||
|
|
||||||
// cache the file
|
|
||||||
typedef typename iStreamT::char_type charT;
|
|
||||||
std::basic_string<charT> str = detail::read_file(inStream);
|
|
||||||
// parse it
|
|
||||||
return read<OutputT>(str.begin(), str.end(), opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename iStreamT>
|
|
||||||
inline basic_object<typename iStreamT::char_type>
|
|
||||||
read(iStreamT &inStream, const Options &opt = Options{})
|
|
||||||
{
|
|
||||||
return read<basic_object<typename iStreamT::char_type>>(inStream, opt);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace vdf
|
|
||||||
} // namespace tyti
|
|
||||||
#ifndef TYTI_NO_L_UNDEF
|
|
||||||
#undef TYTI_L
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TYTI_UNDEF_CONSTEXPR
|
|
||||||
#undef CONSTEXPR
|
|
||||||
#undef TYTI_NO_L_UNDEF
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#ifdef TYTI_UNDEF_NOTHROW
|
|
||||||
#undef NOTHROW
|
|
||||||
#undef TYTI_UNDEF_NOTHROW
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif //__TYTI_STEAM_VDF_PARSER_H__
|
|
@ -3,7 +3,8 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
#include "bottles.hpp"
|
#include "bottles.hpp"
|
||||||
#include "cellar.hpp"
|
#include "cellar.hpp"
|
||||||
@ -19,9 +20,9 @@ void cellar::bottles::switch_active_bottle(int argc, vector<string> argv) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string homepath = getenv("HOME");
|
string homepath = getenv("HOME"); // /home/nick
|
||||||
string bottlepath = homepath + "/.wine";
|
string bottlepath = homepath + "/.wine"; // /home/nick/.wine
|
||||||
string targetpath = homepath + "/.local/share/cellar/bottles/" + argv[1];
|
string targetpath = homepath + "/.local/share/cellar/bottles/" + argv[1]; // /home/nick/.wine.example
|
||||||
|
|
||||||
file_status targetstatus = symlink_status(targetpath);
|
file_status targetstatus = symlink_status(targetpath);
|
||||||
if (!exists(targetstatus)) {
|
if (!exists(targetstatus)) {
|
||||||
|
@ -5,7 +5,8 @@
|
|||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
|
|
||||||
#include "bottles.hpp"
|
#include "bottles.hpp"
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
#include "bottles.hpp"
|
#include "bottles.hpp"
|
||||||
#include "cellar.hpp"
|
#include "cellar.hpp"
|
||||||
#include "config.hpp"
|
|
||||||
#include "internal/config.hpp"
|
#include "internal/config.hpp"
|
||||||
#include "output.hpp"
|
#include "output.hpp"
|
||||||
#include "version.hpp"
|
#include "version.hpp"
|
||||||
@ -47,15 +46,7 @@ void cellar::config::config_command(int argc, vector<string> argv) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
string myval;
|
string myval = active_bottle.get_config(key);
|
||||||
if (global) {
|
|
||||||
if (config::global_config.find(key) != config::global_config.end()) {
|
|
||||||
myval = config::global_config[key];
|
|
||||||
} else if (config::compiled_config.find(key) != config::compiled_config.end()) {
|
|
||||||
myval = config::compiled_config[key];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else { myval = active_bottle.get_config(key); }
|
|
||||||
|
|
||||||
if (myval != "") {
|
if (myval != "") {
|
||||||
output::statement(key + ": " + myval);
|
output::statement(key + ": " + myval);
|
||||||
@ -69,14 +60,7 @@ void cellar::config::config_command(int argc, vector<string> argv) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
string newvalue = value;
|
string newvalue = value;
|
||||||
string oldvalue;
|
string oldvalue = active_bottle.get_config(key);
|
||||||
if (global) {
|
|
||||||
if (config::global_config.find(key) != config::global_config.end()) {
|
|
||||||
oldvalue = config::global_config[key];
|
|
||||||
} else if (config::compiled_config.find(key) != config::compiled_config.end()) {
|
|
||||||
oldvalue = config::compiled_config[key];
|
|
||||||
}
|
|
||||||
} else { oldvalue = active_bottle.get_config(key); }
|
|
||||||
|
|
||||||
if (active_bottle.set_config(key, newvalue)) {
|
if (active_bottle.set_config(key, newvalue)) {
|
||||||
output::statement(key + ": " + newvalue + " (was " + oldvalue + ")");
|
output::statement(key + ": " + newvalue + " (was " + oldvalue + ")");
|
||||||
|
@ -2,14 +2,15 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include <boost/filesystem.hpp>
|
#include <boost/filesystem/operations.hpp>
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
#include "cellar.hpp"
|
#include "cellar.hpp"
|
||||||
#include "fs.hpp"
|
#include "fs.hpp"
|
||||||
#include "output.hpp"
|
#include "output.hpp"
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
//using namespace boost;
|
using namespace boost;
|
||||||
using namespace cellar;
|
using namespace cellar;
|
||||||
using namespace fs;
|
using namespace fs;
|
||||||
|
|
||||||
@ -21,10 +22,10 @@ void cbnull_remove(string src) { return; }
|
|||||||
|
|
||||||
vector<string> fs::listdir(string path) {
|
vector<string> fs::listdir(string path) {
|
||||||
vector<string> result;
|
vector<string> result;
|
||||||
boost::filesystem::path cwd(path);
|
filesystem::path cwd(path);
|
||||||
boost::filesystem::directory_iterator iter_end;
|
filesystem::directory_iterator iter_end;
|
||||||
|
|
||||||
for (boost::filesystem::directory_iterator iter_cwd(cwd); iter_cwd != iter_end; ++iter_cwd) {
|
for (filesystem::directory_iterator iter_cwd(cwd); iter_cwd != iter_end; ++iter_cwd) {
|
||||||
string item = iter_cwd->path().filename().native();
|
string item = iter_cwd->path().filename().native();
|
||||||
|
|
||||||
result.push_back(item);
|
result.push_back(item);
|
||||||
@ -33,10 +34,10 @@ vector<string> fs::listdir(string path) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool fs::recursive_copy(string src, string dst, CopyCallbackFunc callback) {
|
bool fs::recursive_copy(string src, string dst, CopyCallbackFunc callback) {
|
||||||
if (!boost::filesystem::exists(dst)) {
|
if (!filesystem::exists(dst)) {
|
||||||
if (dryrun) { output::statement("mkdir: " + dst); }
|
if (dryrun) { output::statement("mkdir: " + dst); }
|
||||||
else {
|
else {
|
||||||
bool success = boost::filesystem::create_directory(dst);
|
bool success = filesystem::create_directory(dst);
|
||||||
if (!success) { return false; }
|
if (!success) { return false; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -45,17 +46,17 @@ bool fs::recursive_copy(string src, string dst, CopyCallbackFunc callback) {
|
|||||||
string itemabs = src + "/" + itemrel;
|
string itemabs = src + "/" + itemrel;
|
||||||
string targetabs = dst + "/" + itemrel;
|
string targetabs = dst + "/" + itemrel;
|
||||||
|
|
||||||
auto itemstat = boost::filesystem::symlink_status(itemabs);
|
auto itemstat = filesystem::symlink_status(itemabs);
|
||||||
|
|
||||||
if (boost::filesystem::is_directory(itemstat)) { recursive_copy(itemabs, targetabs, callback); }
|
if (filesystem::is_directory(itemstat)) { recursive_copy(itemabs, targetabs, callback); }
|
||||||
else if (boost::filesystem::is_symlink(itemstat)) {
|
else if (filesystem::is_symlink(itemstat)) {
|
||||||
auto symlinkpath = boost::filesystem::read_symlink(itemabs);
|
auto symlinkpath = filesystem::read_symlink(itemabs);
|
||||||
if (dryrun) { output::statement("symlink: " + symlinkpath.native() + " => " + targetabs); }
|
if (dryrun) { output::statement("symlink: " + symlinkpath.native() + " => " + targetabs); }
|
||||||
else { boost::filesystem::create_symlink(symlinkpath, targetabs); }
|
else { filesystem::create_symlink(symlinkpath, targetabs); }
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (dryrun) { output::statement("copy: " + itemabs + " => " + targetabs); }
|
if (dryrun) { output::statement("copy: " + itemabs + " => " + targetabs); }
|
||||||
else { boost::filesystem::copy(itemabs, targetabs); }
|
else { filesystem::copy(itemabs, targetabs); }
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(itemabs, targetabs);
|
callback(itemabs, targetabs);
|
||||||
@ -67,24 +68,24 @@ bool fs::recursive_copy(string src, string dst, CopyCallbackFunc callback) {
|
|||||||
bool fs::recursive_copy(string src, string dst) { return recursive_copy(src, dst, cbnull_copy); }
|
bool fs::recursive_copy(string src, string dst) { return recursive_copy(src, dst, cbnull_copy); }
|
||||||
|
|
||||||
bool fs::recursive_remove(string target, RemoveCallbackFunc callback) {
|
bool fs::recursive_remove(string target, RemoveCallbackFunc callback) {
|
||||||
if (!boost::filesystem::exists(target)) { return false; }
|
if (!filesystem::exists(target)) { return false; }
|
||||||
|
|
||||||
for (string itemrel : listdir(target)) {
|
for (string itemrel : listdir(target)) {
|
||||||
string itemabs = target + "/" + itemrel;
|
string itemabs = target + "/" + itemrel;
|
||||||
|
|
||||||
auto itemstat = boost::filesystem::symlink_status(itemabs);
|
auto itemstat = filesystem::symlink_status(itemabs);
|
||||||
|
|
||||||
if (boost::filesystem::is_directory(itemstat)) { recursive_remove(itemabs, callback); }
|
if (filesystem::is_directory(itemstat)) { recursive_remove(itemabs, callback); }
|
||||||
else {
|
else {
|
||||||
if (dryrun) { output::error("rm: " + itemabs); }
|
if (dryrun) { output::error("rm: " + itemabs); }
|
||||||
else { boost::filesystem::remove(itemabs); }
|
else { filesystem::remove(itemabs); }
|
||||||
}
|
}
|
||||||
|
|
||||||
callback(itemabs);
|
callback(itemabs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dryrun) { output::error("rm: " + target); }
|
if (dryrun) { output::error("rm: " + target); }
|
||||||
else { boost::filesystem::remove(target); }
|
else { filesystem::remove(target); }
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -2,4 +2,3 @@ core
|
|||||||
config
|
config
|
||||||
bottles
|
bottles
|
||||||
launch
|
launch
|
||||||
steam
|
|
@ -1 +0,0 @@
|
|||||||
steamtest test_command Test Steam related thing.
|
|
@ -1,33 +0,0 @@
|
|||||||
#include <cstdlib>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "vdf_parser.hpp"
|
|
||||||
|
|
||||||
#include "bottles.hpp"
|
|
||||||
#include "steam.hpp"
|
|
||||||
#include "internal/steam.hpp"
|
|
||||||
|
|
||||||
using namespace tyti;
|
|
||||||
|
|
||||||
std::vector<std::string> cellar::steam::find_steam_libraries() {
|
|
||||||
std::stringstream sstr_steam_library_config;
|
|
||||||
sstr_steam_library_config << std::getenv("HOME");
|
|
||||||
sstr_steam_library_config << "/.steam/root/config/libraryfolders.vdf";
|
|
||||||
std::string str_steam_library_config = sstr_steam_library_config.str();
|
|
||||||
|
|
||||||
std::ifstream fd_steam_library_config(str_steam_library_config);
|
|
||||||
auto hnd_steam_library_config = vdf::read(fd_steam_library_config);
|
|
||||||
|
|
||||||
std::vector<std::string> result;
|
|
||||||
|
|
||||||
for (auto hnd_library_def : hnd_steam_library_config.childs) {
|
|
||||||
std::string str_index = hnd_library_def.first;
|
|
||||||
auto hnd_library = hnd_library_def.second;
|
|
||||||
result.push_back(hnd_library->attribs["path"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
@ -1,49 +0,0 @@
|
|||||||
#include <cstdlib>
|
|
||||||
#include <filesystem>
|
|
||||||
#include <format>
|
|
||||||
#include <fstream>
|
|
||||||
#include <sstream>
|
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
#include "vdf_parser.hpp"
|
|
||||||
|
|
||||||
#include "bottles.hpp"
|
|
||||||
#include "output.hpp"
|
|
||||||
#include "steam.hpp"
|
|
||||||
#include "internal/steam.hpp"
|
|
||||||
|
|
||||||
using namespace tyti; // vdf
|
|
||||||
|
|
||||||
void cellar::steam::test_command(int argc, std::vector<std::string> argv) {
|
|
||||||
for (std::string str_path_library : cellar::steam::find_steam_libraries()) {
|
|
||||||
output::statement(str_path_library);
|
|
||||||
|
|
||||||
std::filesystem::path pth_library(str_path_library);
|
|
||||||
std::filesystem::path pth_steam_cellar = pth_library / "steamapps/compatdata";
|
|
||||||
for (auto const& itm_appid : std::filesystem::directory_iterator(pth_steam_cellar)) {
|
|
||||||
auto pth_appid = itm_appid.path();
|
|
||||||
if (std::filesystem::is_directory(pth_appid / "pfx") && ! std::filesystem::is_empty(pth_appid / "pfx")) {
|
|
||||||
// \/ string-to-unsigned-long
|
|
||||||
std::string str_appid = pth_appid.filename().string(); // should become, e.g. 1124300
|
|
||||||
auto pth_appmanifest = pth_library / ("steamapps/appmanifest_" + str_appid + ".acf");
|
|
||||||
if (! std::filesystem::exists(pth_appmanifest) ) { continue; }
|
|
||||||
std::string str_gamename = "";
|
|
||||||
|
|
||||||
std::ifstream fd_appmanifest(pth_appmanifest);
|
|
||||||
auto hnd_appmanifest = vdf::read(fd_appmanifest);
|
|
||||||
for (auto hnd_appmanifest_def : hnd_appmanifest.attribs) {
|
|
||||||
std::string str_index = hnd_appmanifest_def.first;
|
|
||||||
std::string str_value = hnd_appmanifest_def.second;
|
|
||||||
|
|
||||||
if (str_index == "name") {
|
|
||||||
str_gamename = str_value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
output::warning("Steam App #" + str_appid + " (" + str_gamename + ")");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
x
Reference in New Issue
Block a user