Compare commits
	
		
			22 Commits
		
	
	
		
			v0.4
			...
			d11b690708
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| d11b690708 | |||
| 8dbb226251 | |||
| 870ec7345b | |||
| c633c96ad7 | |||
| 713bf30181 | |||
| ea9514dba0 | |||
| f53813d3c9 | |||
| b2b2b362be | |||
| b32259c56d | |||
| 3fc9c88230 | |||
| 19251a253f | |||
| 9ad87ba79b | |||
| 83b7de3a5f | |||
| 0f679dc345 | |||
| ce8c26e58c | |||
| 2d620be9d8 | |||
| 2de0b66e4e | |||
| b2c3508e8f | |||
| d1960506ac | |||
| 1f9a78956b | |||
| 81732eab03 | |||
| c73cf7d67e | 
							
								
								
									
										28
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								.vscode/launch.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,28 @@
 | 
			
		||||
{
 | 
			
		||||
    "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
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										93
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										93
									
								
								.vscode/settings.json
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,93 @@
 | 
			
		||||
{
 | 
			
		||||
    "files.associations": {
 | 
			
		||||
        "*.wsgi": "python",
 | 
			
		||||
        "*.s": "nasm",
 | 
			
		||||
        "*.cog": "cpp",
 | 
			
		||||
        "algorithm": "cpp",
 | 
			
		||||
        "cmath": "cpp",
 | 
			
		||||
        "any": "cpp",
 | 
			
		||||
        "array": "cpp",
 | 
			
		||||
        "atomic": "cpp",
 | 
			
		||||
        "hash_map": "cpp",
 | 
			
		||||
        "strstream": "cpp",
 | 
			
		||||
        "bit": "cpp",
 | 
			
		||||
        "*.tcc": "cpp",
 | 
			
		||||
        "bitset": "cpp",
 | 
			
		||||
        "cctype": "cpp",
 | 
			
		||||
        "cfenv": "cpp",
 | 
			
		||||
        "charconv": "cpp",
 | 
			
		||||
        "chrono": "cpp",
 | 
			
		||||
        "clocale": "cpp",
 | 
			
		||||
        "codecvt": "cpp",
 | 
			
		||||
        "compare": "cpp",
 | 
			
		||||
        "complex": "cpp",
 | 
			
		||||
        "concepts": "cpp",
 | 
			
		||||
        "condition_variable": "cpp",
 | 
			
		||||
        "coroutine": "cpp",
 | 
			
		||||
        "csetjmp": "cpp",
 | 
			
		||||
        "csignal": "cpp",
 | 
			
		||||
        "cstdarg": "cpp",
 | 
			
		||||
        "cstddef": "cpp",
 | 
			
		||||
        "cstdint": "cpp",
 | 
			
		||||
        "cstdio": "cpp",
 | 
			
		||||
        "cstdlib": "cpp",
 | 
			
		||||
        "cstring": "cpp",
 | 
			
		||||
        "ctime": "cpp",
 | 
			
		||||
        "cwchar": "cpp",
 | 
			
		||||
        "cwctype": "cpp",
 | 
			
		||||
        "deque": "cpp",
 | 
			
		||||
        "forward_list": "cpp",
 | 
			
		||||
        "list": "cpp",
 | 
			
		||||
        "map": "cpp",
 | 
			
		||||
        "set": "cpp",
 | 
			
		||||
        "string": "cpp",
 | 
			
		||||
        "unordered_map": "cpp",
 | 
			
		||||
        "unordered_set": "cpp",
 | 
			
		||||
        "vector": "cpp",
 | 
			
		||||
        "exception": "cpp",
 | 
			
		||||
        "functional": "cpp",
 | 
			
		||||
        "iterator": "cpp",
 | 
			
		||||
        "memory": "cpp",
 | 
			
		||||
        "memory_resource": "cpp",
 | 
			
		||||
        "numeric": "cpp",
 | 
			
		||||
        "optional": "cpp",
 | 
			
		||||
        "random": "cpp",
 | 
			
		||||
        "ratio": "cpp",
 | 
			
		||||
        "regex": "cpp",
 | 
			
		||||
        "source_location": "cpp",
 | 
			
		||||
        "string_view": "cpp",
 | 
			
		||||
        "system_error": "cpp",
 | 
			
		||||
        "tuple": "cpp",
 | 
			
		||||
        "type_traits": "cpp",
 | 
			
		||||
        "utility": "cpp",
 | 
			
		||||
        "format": "cpp",
 | 
			
		||||
        "fstream": "cpp",
 | 
			
		||||
        "future": "cpp",
 | 
			
		||||
        "initializer_list": "cpp",
 | 
			
		||||
        "iomanip": "cpp",
 | 
			
		||||
        "iosfwd": "cpp",
 | 
			
		||||
        "iostream": "cpp",
 | 
			
		||||
        "istream": "cpp",
 | 
			
		||||
        "limits": "cpp",
 | 
			
		||||
        "mutex": "cpp",
 | 
			
		||||
        "new": "cpp",
 | 
			
		||||
        "numbers": "cpp",
 | 
			
		||||
        "ostream": "cpp",
 | 
			
		||||
        "ranges": "cpp",
 | 
			
		||||
        "semaphore": "cpp",
 | 
			
		||||
        "shared_mutex": "cpp",
 | 
			
		||||
        "span": "cpp",
 | 
			
		||||
        "sstream": "cpp",
 | 
			
		||||
        "stdexcept": "cpp",
 | 
			
		||||
        "stdfloat": "cpp",
 | 
			
		||||
        "stop_token": "cpp",
 | 
			
		||||
        "streambuf": "cpp",
 | 
			
		||||
        "text_encoding": "cpp",
 | 
			
		||||
        "thread": "cpp",
 | 
			
		||||
        "cinttypes": "cpp",
 | 
			
		||||
        "typeindex": "cpp",
 | 
			
		||||
        "typeinfo": "cpp",
 | 
			
		||||
        "valarray": "cpp",
 | 
			
		||||
        "variant": "cpp"
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@@ -1,9 +1,9 @@
 | 
			
		||||
cmake_minimum_required(VERSION 3.7.2)
 | 
			
		||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake/Modules)
 | 
			
		||||
cmake_minimum_required(VERSION 3.27.2)
 | 
			
		||||
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_SOURCE_DIR}/cmake)
 | 
			
		||||
include(Colours)
 | 
			
		||||
include(Platform)
 | 
			
		||||
 | 
			
		||||
project(cellar CXX)
 | 
			
		||||
project(cellar VERSION 0.5 LANGUAGES CXX)
 | 
			
		||||
string(TIMESTAMP BUILD_DATE "%Y.%m.%d %H:%M:%S UTC" UTC)
 | 
			
		||||
# local cmake modules
 | 
			
		||||
 | 
			
		||||
@@ -14,6 +14,9 @@ SET(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib/cellar")
 | 
			
		||||
# which point to directories outside the build tree to the install RPATH
 | 
			
		||||
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)
 | 
			
		||||
git_init()
 | 
			
		||||
 | 
			
		||||
@@ -37,24 +40,32 @@ http://tclap.sourceforge.net and put the headers in ./include
 | 
			
		||||
(wink, nudge)")
 | 
			
		||||
endif(NOT TCLAP_FOUND)
 | 
			
		||||
 | 
			
		||||
include(LavaTargets)
 | 
			
		||||
include(Binaries)
 | 
			
		||||
 | 
			
		||||
include_directories(include)
 | 
			
		||||
configure_file("${CMAKE_SOURCE_DIR}/include/cmake.hpp.in"
 | 
			
		||||
                "${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")
 | 
			
		||||
set(src "${CMAKE_SOURCE_DIR}/src")
 | 
			
		||||
 | 
			
		||||
lava_create_gutlib(
 | 
			
		||||
        SUBDIRS bottles config launch paths
 | 
			
		||||
set(libcellar_subdirs bottles config launch paths)
 | 
			
		||||
if(ENABLE_STEAM)
 | 
			
		||||
        list(APPEND libcellar_subdirs steam)
 | 
			
		||||
endif()
 | 
			
		||||
build_library(TARGET libcellar
 | 
			
		||||
        SUBDIRS ${libcellar_subdirs}
 | 
			
		||||
        DEPENDS cog)
 | 
			
		||||
set_target_properties(libcellar PROPERTIES PREFIX "") # prevent "liblibcellar"
 | 
			
		||||
 | 
			
		||||
lava_create_executable(TARGET cellar
 | 
			
		||||
build_executable(TARGET cellar
 | 
			
		||||
        SUBDIRS core help
 | 
			
		||||
        LIBRARIES gutlib Boost::filesystem Boost::system
 | 
			
		||||
        LIBRARIES libcellar Boost::filesystem Boost::system
 | 
			
		||||
        DEPENDS cog)
 | 
			
		||||
 | 
			
		||||
install(TARGETS cellar gutlib 
 | 
			
		||||
install(TARGETS cellar libcellar 
 | 
			
		||||
        RUNTIME DESTINATION bin
 | 
			
		||||
        LIBRARY DESTINATION lib/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
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2917
									
								
								Doxyfile.in
									
									
									
									
									
										Normal file
									
								
							
										
											
												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
 | 
			
		||||
provided under the terms of the MIT license, as below:
 | 
			
		||||
 | 
			
		||||
Copyright (c) 2017 Nicholas O'Connor
 | 
			
		||||
Copyright (c) 2017-2025 Nicole O'Connor
 | 
			
		||||
 | 
			
		||||
Permission is hereby granted, free of charge, to any person obtaining
 | 
			
		||||
a copy of this software and associated documentation files (the
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								README.md
									
									
									
									
									
								
							@@ -2,6 +2,12 @@
 | 
			
		||||
### bottle management tool for WINE connoisseurs
 | 
			
		||||
*(this software is considered unfinished, use at own risk)*
 | 
			
		||||
 | 
			
		||||
## Dependencies
 | 
			
		||||
* Boost
 | 
			
		||||
* Cog for code generation (`pip install cogapp`)
 | 
			
		||||
* TCLAP (either system-wide or included in `include`)
 | 
			
		||||
* Ronn for man page
 | 
			
		||||
 | 
			
		||||
## Installation
 | 
			
		||||
 | 
			
		||||
    $ mkdir build && cd build
 | 
			
		||||
@@ -9,25 +15,23 @@
 | 
			
		||||
    $ make -j4
 | 
			
		||||
    $ sudo make install
 | 
			
		||||
 | 
			
		||||
cellar also supports the use of clang as your compiler and/or ninja as your make system, for those of you who have opinions on such things.
 | 
			
		||||
 | 
			
		||||
## Quick Usage Primer
 | 
			
		||||
 | 
			
		||||
    $ cellar create steam
 | 
			
		||||
    $ cellar -b steam winetricks vcrun2012
 | 
			
		||||
    $ cellar create foobar2000
 | 
			
		||||
    $ cellar -b foobar2000 winetricks vcrun2012
 | 
			
		||||
 | 
			
		||||
    # without the -b argument, cellar assumes you want to deal with ~/.wine
 | 
			
		||||
    # you can manage which bottle that points to with this command
 | 
			
		||||
    $ cellar activate steam
 | 
			
		||||
    $ cellar activate foobar2000
 | 
			
		||||
 | 
			
		||||
    # arguments passed to "cellar launch" are passed to wine
 | 
			
		||||
    $ cellar launch /mnt/windows/Steam/Steam.exe
 | 
			
		||||
    $ cellar launch /mnt/windows/foobar2000/foobar2000.exe
 | 
			
		||||
 | 
			
		||||
## Features
 | 
			
		||||
* **Corking**: Saves a bottle's configuration, including any pressed installers (see below) or installed winetricks, to a directory, then removes the WINE bottle from disk.
 | 
			
		||||
  You can then easily rebuild that WINE bottle later by uncorking it, which will automatically rebuild the WINE bottle with your active (or specified) version of WINE, as
 | 
			
		||||
  well as install any saved winetricks or run any pressed installers.
 | 
			
		||||
* **Pressed Installers**: Saves a copy of an installer to `~/.cellar`, writes it down in the bottle configuration, then runs it within your WINE bottle. If you choose to
 | 
			
		||||
* **Pressed Installers**: Saves a copy of an installer to `~/.local/share/cellar`, writes it down in the bottle configuration, then runs it within your WINE bottle. If you choose to
 | 
			
		||||
  cork this bottle later, this installer will automatically be run after uncorking. If the installer comes with "unattended install" arguments, it's recommended you press
 | 
			
		||||
  those in too.
 | 
			
		||||
* **Easy WINE and bottle management**: Need a specific bottle for a specific program? `cellar -b bottlename <command>`. Does the bottle need to run a specific instance of
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										82
									
								
								cmake/Binaries.cmake
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										82
									
								
								cmake/Binaries.cmake
									
									
									
									
									
										Executable file
									
								
							@@ -0,0 +1,82 @@
 | 
			
		||||
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(PythonInterp)
 | 
			
		||||
find_package(Python3 COMPONENTS Interpreter)
 | 
			
		||||
find_package(PythonModule)
 | 
			
		||||
find_python_module(cogapp REQUIRED)
 | 
			
		||||
 | 
			
		||||
@@ -31,7 +31,7 @@ macro(cog_target)
 | 
			
		||||
            set(thisfile "${CMAKE_CURRENT_BINARY_DIR}/${outfile}")
 | 
			
		||||
    
 | 
			
		||||
            add_custom_command(OUTPUT "${thisfile}" PRE_BUILD
 | 
			
		||||
                    COMMAND ${PYTHON_EXECUTABLE} -m cogapp -d -o "${thisfile}" "${cogfile}"
 | 
			
		||||
                    COMMAND ${Python3_EXECUTABLE} -m cogapp -d -o "${thisfile}" "${cogfile}"
 | 
			
		||||
                    DEPENDS ${cogfile}
 | 
			
		||||
                    WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
 | 
			
		||||
                    COMMENT "Greasing the cog for ${BoldCyan}${outfile}${ColourReset}")
 | 
			
		||||
@@ -1,94 +0,0 @@
 | 
			
		||||
link_directories(${CMAKE_CURRENT_BINARY_DIR})
 | 
			
		||||
 | 
			
		||||
function(lava_create_library)
 | 
			
		||||
        set(multiValueArgs SUBDIRS DEPENDS LIBRARIES)
 | 
			
		||||
        set(oneValueArgs TARGET)
 | 
			
		||||
        cmake_parse_arguments(lava_create_library "" "${oneValueArgs}"
 | 
			
		||||
                "${multiValueArgs}" ${ARGN})
 | 
			
		||||
 | 
			
		||||
        set(target ${lava_create_library_TARGET})
 | 
			
		||||
 | 
			
		||||
        set(targetsources)
 | 
			
		||||
        foreach(subdir ${lava_create_library_SUBDIRS})
 | 
			
		||||
                cog_sources("src/${subdir}/*.cpp" subdirsources)
 | 
			
		||||
 | 
			
		||||
                foreach(source ${subdirsources})
 | 
			
		||||
                        set(targetsources ${targetsources} ${source})
 | 
			
		||||
                endforeach(source)
 | 
			
		||||
        endforeach(subdir)
 | 
			
		||||
 | 
			
		||||
        add_library(${target} SHARED ${targetsources})
 | 
			
		||||
	if(MINGW)
 | 
			
		||||
	    # MinGW (or CMake?) does a stupid, redundant thing where it names all output DLLs
 | 
			
		||||
        # "libblah.dll" and it's kinda stupid and redundant
 | 
			
		||||
	    # this removes the "lib" prefix because removing ".dll" causes things to break
 | 
			
		||||
	    set_target_properties(${target} PROPERTIES PREFIX "")
 | 
			
		||||
	endif()
 | 
			
		||||
        if(lava_create_library_LIBRARIES)
 | 
			
		||||
            foreach(library ${lava_create_library_LIBRARIES})
 | 
			
		||||
                target_link_libraries(${target} "${library}")
 | 
			
		||||
            endforeach()
 | 
			
		||||
        endif()
 | 
			
		||||
        if(lava_create_library_DEPENDS)
 | 
			
		||||
            add_dependencies(${target} ${lava_create_library_DEPENDS})
 | 
			
		||||
        endif()
 | 
			
		||||
endfunction(lava_create_library)
 | 
			
		||||
 | 
			
		||||
function(lava_create_gutlib)
 | 
			
		||||
        set(multiValueArgs SUBDIRS DEPENDS LIBRARIES LIBRARYVARS)
 | 
			
		||||
        cmake_parse_arguments(lava_create_gutlib "" ""
 | 
			
		||||
                "${multiValueArgs}" ${ARGN})
 | 
			
		||||
 | 
			
		||||
        foreach(subdir ${lava_create_gutlib_SUBDIRS})
 | 
			
		||||
                cog_sources("src/${subdir}/*.cpp" subdirsources)
 | 
			
		||||
 | 
			
		||||
                foreach(source ${subdirsources})
 | 
			
		||||
                        set(targetsources ${targetsources} ${source})
 | 
			
		||||
                endforeach(source)
 | 
			
		||||
        endforeach(subdir)
 | 
			
		||||
 | 
			
		||||
        add_library(gutlib SHARED ${targetsources})
 | 
			
		||||
        set_target_properties(gutlib PROPERTIES OUTPUT_NAME ${CMAKE_PROJECT_NAME})
 | 
			
		||||
	if(MINGW)
 | 
			
		||||
	    # MinGW (or CMake?) does a stupid, redundant thing where it names all output DLLs
 | 
			
		||||
        # "libblah.dll" and it's kinda stupid and redundant
 | 
			
		||||
	    # this removes the "lib" prefix because removing ".dll" causes things to break
 | 
			
		||||
	    set_target_properties(gutlib PROPERTIES PREFIX "")
 | 
			
		||||
	endif()
 | 
			
		||||
        if(lava_create_gutlib_LIBRARIES)
 | 
			
		||||
            foreach(library ${lava_create_gutlib_LIBRARIES})
 | 
			
		||||
               target_link_libraries(gutlib "${library}")
 | 
			
		||||
            endforeach()
 | 
			
		||||
        endif()
 | 
			
		||||
        if(lava_create_gutlib_DEPENDS)
 | 
			
		||||
            add_dependencies(gutlib ${lava_create_gutlib_DEPENDS})
 | 
			
		||||
        endif()
 | 
			
		||||
endfunction(lava_create_gutlib)
 | 
			
		||||
 | 
			
		||||
function(lava_create_executable)
 | 
			
		||||
        set(multiValueArgs SUBDIRS LIBRARIES DEPENDS)
 | 
			
		||||
        set(oneValueArgs TARGET)
 | 
			
		||||
        cmake_parse_arguments(lava_create_executable "" "${oneValueArgs}"
 | 
			
		||||
                "${multiValueArgs}" ${ARGN})
 | 
			
		||||
 | 
			
		||||
        set(target ${lava_create_executable_TARGET})
 | 
			
		||||
 | 
			
		||||
        set(targetsources)
 | 
			
		||||
        foreach(subdir ${lava_create_executable_SUBDIRS})
 | 
			
		||||
                cog_sources("src/${subdir}/*.cpp" subdirsources)
 | 
			
		||||
 | 
			
		||||
                foreach(source ${subdirsources})
 | 
			
		||||
                        set(targetsources ${targetsources} ${source})
 | 
			
		||||
                endforeach(source)
 | 
			
		||||
        endforeach(subdir)
 | 
			
		||||
 | 
			
		||||
        add_executable(${target} ${targetsources})
 | 
			
		||||
        if(lava_create_executable_LIBRARIES)
 | 
			
		||||
            foreach(library ${lava_create_executable_LIBRARIES})
 | 
			
		||||
                target_link_libraries(${target} "${library}")
 | 
			
		||||
            endforeach()
 | 
			
		||||
        endif()
 | 
			
		||||
        if(lava_create_executable_DEPENDS)
 | 
			
		||||
            add_dependencies(${target} ${lava_create_executable_DEPENDS})
 | 
			
		||||
        endif()
 | 
			
		||||
endfunction(lava_create_executable)
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
set(CMAKE_CXX_STANDARD 11)
 | 
			
		||||
set(CMAKE_CXX_STANDARD 20)
 | 
			
		||||
set(CMAKE_CXX_STANDARD_REQUIRED ON)
 | 
			
		||||
 | 
			
		||||
set(PLATFORM_FOREIGN_ENV FALSE)
 | 
			
		||||
							
								
								
									
										3
									
								
								cogrc
									
									
									
									
									
								
							
							
						
						
									
										3
									
								
								cogrc
									
									
									
									
									
								
							@@ -1,5 +1,6 @@
 | 
			
		||||
[version]
 | 
			
		||||
release_version=0.4
 | 
			
		||||
release_version=0.5
 | 
			
		||||
 | 
			
		||||
[defaults]
 | 
			
		||||
wine-path=wine
 | 
			
		||||
cellar-bottle-path=~/.local/share/cellar/bottles
 | 
			
		||||
@@ -34,4 +34,4 @@ TODO: Generate this.
 | 
			
		||||
 | 
			
		||||
## COPYRIGHT
 | 
			
		||||
 | 
			
		||||
Copyright © 2017-2019 Nicholas O'Connor. Provided under the terms of the MIT license: https://opensource.org/licenses/MIT
 | 
			
		||||
Copyright © 2017-2025 Nicole O'Connor. Provided under the terms of the MIT license: https://opensource.org/licenses/MIT
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,5 @@
 | 
			
		||||
/* This file contains string definitions for ANSI color sequences
 | 
			
		||||
 * it was written by Nicole O'Connor and is available  to all 
 | 
			
		||||
 * it was written by Nicole O'Connor and is available to all 
 | 
			
		||||
 * under the MIT license.
 | 
			
		||||
 *
 | 
			
		||||
 * Usage:
 | 
			
		||||
 
 | 
			
		||||
@@ -20,6 +20,12 @@ namespace cellar {
 | 
			
		||||
            bottle_labelled,
 | 
			
		||||
            bottle_symlink
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Bottles are the internal name for WINE prefixes. In addition to standard WINE contents,
 | 
			
		||||
         * bottles contain a configuration file managed by cellar, which labels the bottle as well
 | 
			
		||||
         * as providing configuration settings.
 | 
			
		||||
         */
 | 
			
		||||
        class Bottle {
 | 
			
		||||
            public:
 | 
			
		||||
                // public members
 | 
			
		||||
 
 | 
			
		||||
@@ -3,3 +3,4 @@
 | 
			
		||||
#cmakedefine IS_GIT_REPO "@IS_GIT_REPO@"
 | 
			
		||||
#cmakedefine GIT_COMMIT_HASH "@GIT_COMMIT_HASH@"
 | 
			
		||||
#cmakedefine GIT_BRANCH "@GIT_BRANCH@"
 | 
			
		||||
#cmakedefine ENABLE_STEAM "@ENABLE_STEAM@"
 | 
			
		||||
							
								
								
									
										26
									
								
								include/internal/steam.hpp.cog
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								include/internal/steam.hpp.cog
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,26 @@
 | 
			
		||||
#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();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								include/steam.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								include/steam.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#include "bottles.hpp"
 | 
			
		||||
 | 
			
		||||
namespace cellar {
 | 
			
		||||
    namespace steam {
 | 
			
		||||
        extern cellar::bottles::Bottle app_bottle(unsigned appid);
 | 
			
		||||
        extern std::map<std::string, cellar::bottles::Bottle> get_app_bottles();
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										855
									
								
								include/vdf_parser.hpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										855
									
								
								include/vdf_parser.hpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,855 @@
 | 
			
		||||
// 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__
 | 
			
		||||
							
								
								
									
										15
									
								
								packaging/debian/control
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								packaging/debian/control
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,15 @@
 | 
			
		||||
## incomplete: need to double check syntax of [Build-]Depends and add libboost-{file,}system-dev to it, among other packages
 | 
			
		||||
Source: cellar
 | 
			
		||||
Maintainer: Nicole O'Connor <nicole@otl-hga.net>
 | 
			
		||||
Section: main
 | 
			
		||||
Priority: optional
 | 
			
		||||
Standards-Version: 4.6.2
 | 
			
		||||
Build-Depends: debhelper-compat (= 13)
 | 
			
		||||
 | 
			
		||||
Package: cellar
 | 
			
		||||
Architecture: amd64
 | 
			
		||||
Depends: ${shlibs:Depends} ${misc:Depends}
 | 
			
		||||
Description: WINE bottle management tool
 | 
			
		||||
    Helps users manage multiple instances of WINE across
 | 
			
		||||
    multiple configuration prefixes (or, bottles). Comes
 | 
			
		||||
    with some advanced features.
 | 
			
		||||
							
								
								
									
										11
									
								
								packaging/rpm/cellar.spec
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								packaging/rpm/cellar.spec
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
# VERY INCOMPLETE
 | 
			
		||||
Name:       cellar
 | 
			
		||||
Version:    0.4
 | 
			
		||||
Release:    1%{?dist}
 | 
			
		||||
Summary:    WINE bottle management tool
 | 
			
		||||
 | 
			
		||||
License:    MIT
 | 
			
		||||
URL:        https://vcs.otl-hga.net/nicole/cellar/
 | 
			
		||||
Source0:    https://vcs.otl-hga.net/nicole/cellar/archive/v0.4.tar.gz
 | 
			
		||||
 | 
			
		||||
BuildRequires:  build-essential cmake python3-pip 
 | 
			
		||||
@@ -3,8 +3,7 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <boost/filesystem/operations.hpp>
 | 
			
		||||
#include <boost/filesystem/path.hpp>
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
 | 
			
		||||
#include "bottles.hpp"
 | 
			
		||||
#include "cellar.hpp"
 | 
			
		||||
@@ -20,9 +19,9 @@ void cellar::bottles::switch_active_bottle(int argc, vector<string> argv) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    string homepath = getenv("HOME");                // /home/nick
 | 
			
		||||
    string bottlepath = homepath + "/.wine";         // /home/nick/.wine
 | 
			
		||||
    string targetpath = homepath + "/.local/share/cellar/bottles/" + argv[1];  // /home/nick/.wine.example
 | 
			
		||||
    string homepath = getenv("HOME");
 | 
			
		||||
    string bottlepath = homepath + "/.wine";
 | 
			
		||||
    string targetpath = cellar::bottles::resolve_bottle(argv[1]);
 | 
			
		||||
 | 
			
		||||
    file_status targetstatus = symlink_status(targetpath);
 | 
			
		||||
    if (!exists(targetstatus)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,6 @@ void cellar::bottles::print_active_bottle(int argc, vector<string> argv) {
 | 
			
		||||
        } else {
 | 
			
		||||
            outstr << active_bottle.canonical_path;
 | 
			
		||||
            cellar_managed = false;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -5,14 +5,17 @@
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <boost/filesystem/operations.hpp>
 | 
			
		||||
#include <boost/filesystem/path.hpp>
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
#include "nlohmann/json.hpp"
 | 
			
		||||
 | 
			
		||||
#include "bottles.hpp"
 | 
			
		||||
#include "cmake.hpp"
 | 
			
		||||
#include "internal/bottles.hpp"
 | 
			
		||||
#include "fs.hpp"
 | 
			
		||||
#include "output.hpp"
 | 
			
		||||
#ifdef ENABLE_STEAM
 | 
			
		||||
#include "steam.hpp"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace cellar;
 | 
			
		||||
@@ -21,6 +24,10 @@ using namespace cellar::bottles;
 | 
			
		||||
using CommandFunction = cellar::commands::CommandFunction;
 | 
			
		||||
using json = nlohmann::json;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Construct an empty bottle object.
 | 
			
		||||
 * This empty bottle object can then be used to help create new ones.
 | 
			
		||||
 */
 | 
			
		||||
Bottle::Bottle() {
 | 
			
		||||
    // define a null bottle
 | 
			
		||||
    // strings handle themselves
 | 
			
		||||
@@ -28,6 +35,11 @@ Bottle::Bottle() {
 | 
			
		||||
    type = bottle_anonymous;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Construct a bottle object from at a given path.
 | 
			
		||||
 * 
 | 
			
		||||
 * @param patharg The path to load a bottle from.
 | 
			
		||||
 */
 | 
			
		||||
Bottle::Bottle(string patharg) {
 | 
			
		||||
    output::statement("loading bottle from " + patharg, true);
 | 
			
		||||
    config = json({});
 | 
			
		||||
@@ -55,6 +67,14 @@ Bottle::Bottle(string patharg) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Lists all bottles cellar manages or is otherwise aware of.
 | 
			
		||||
 * This includes bottles managed by other tools like Steam or Lutris,
 | 
			
		||||
 * assuming support for those tools is compiled into cellar. (Steam
 | 
			
		||||
 * support is present; Lutris support is planned for the future.)
 | 
			
		||||
 * 
 | 
			
		||||
 * @return map<string, Bottle> All bottles. Bottles managed by other tools have prefixed keys, e.g. Steam bottles use "steam:"
 | 
			
		||||
 */
 | 
			
		||||
map<string, Bottle> cellar::bottles::get_bottles() {
 | 
			
		||||
	map<string, Bottle> result;
 | 
			
		||||
 | 
			
		||||
@@ -67,21 +87,48 @@ map<string, Bottle> cellar::bottles::get_bottles() {
 | 
			
		||||
	    result[item] = output;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef ENABLE_STEAM
 | 
			
		||||
    map<string, Bottle> bottles_proton = cellar::steam::get_app_bottles();
 | 
			
		||||
    for (auto item : bottles_proton) {
 | 
			
		||||
        result.insert_or_assign(item.first, item.second);
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Takes an input that refers to a bottle and returns a path to that bottle.
 | 
			
		||||
 * This input can be any of the following:
 | 
			
		||||
 *  * A bottle name as managed by cellar
 | 
			
		||||
 *  * A prefixed bottle name, in the format `<tool>:<identifier>`. (e.g. `steam:78000`)
 | 
			
		||||
 *  * An absolute or relative path, in which case it is returned with no modification.
 | 
			
		||||
 *  * A path relative to (and starting with ~). cellar will throw a warning in verbose mode,
 | 
			
		||||
 *    since usually the shell expands this for us, but will do a naive replacement on its own
 | 
			
		||||
 *    and try to prevent confused users.
 | 
			
		||||
 * 
 | 
			
		||||
 * @param bottlechoice Input, usually referring to a specific bottle known to cellar.
 | 
			
		||||
 * @return string Path to referenced bottle.
 | 
			
		||||
 */
 | 
			
		||||
string cellar::bottles::resolve_bottle(string bottlechoice) {
 | 
			
		||||
    string result;
 | 
			
		||||
    if (bottlechoice.substr(0,1) == "/" || bottlechoice.substr(0,1) == ".") { // absolute or relative path
 | 
			
		||||
        result = bottlechoice;
 | 
			
		||||
    } else if (bottlechoice.substr(0,1) == "~") { // "absolute" path in home directory, not expanded by the shell for some reason (i've seen some shit)
 | 
			
		||||
        // this is a naive replacement and will fail if the user tries something like ~nick/.wine
 | 
			
		||||
        // this is a naive replacement and will fail if the user tries something like ~nicole/.wine
 | 
			
		||||
        // i'm figuring at that point if you're doing that, you'll also recognize if your shell
 | 
			
		||||
        // isn't actually expanding your path...
 | 
			
		||||
        bottlechoice.replace(0,1,getenv("HOME"));
 | 
			
		||||
        // or at least you'll think to use verbose mode to make sure it's loading the right directory
 | 
			
		||||
        output::warning("your shell didn't expand your given path properly, doing a naive replacement", true);
 | 
			
		||||
        result = bottlechoice;
 | 
			
		||||
#ifdef ENABLE_STEAM
 | 
			
		||||
    } else if (bottlechoice.substr(0,6) == "steam:") { // steam bottles
 | 
			
		||||
        string str_appid = bottlechoice.substr(6);
 | 
			
		||||
        unsigned long uint_appid = std::stoul(str_appid);
 | 
			
		||||
        auto steambottle = cellar::steam::app_bottle(uint_appid);
 | 
			
		||||
        result = steambottle.path;
 | 
			
		||||
#endif
 | 
			
		||||
    } else {
 | 
			
		||||
        string homepath = getenv("HOME");
 | 
			
		||||
        string fullbottlepath = homepath + "/.local/share/cellar/bottles" + bottlechoice;
 | 
			
		||||
@@ -90,6 +137,9 @@ string cellar::bottles::resolve_bottle(string bottlechoice) {
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Prints bottles. Used as a command in CLI.
 | 
			
		||||
 */
 | 
			
		||||
void cellar::bottles::print_bottles(int argc, vector<string> argv) {
 | 
			
		||||
    map<string, Bottle> bottles = get_bottles();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@ void cellar::bottles::create_bottle(int argc, vector<string> argv) {
 | 
			
		||||
    if (bottlechoice.substr(0,1) == "/" || bottlechoice.substr(0,1) == ".") { // absolute or relative path
 | 
			
		||||
        fullbottlepath = bottlechoice;
 | 
			
		||||
    } else if (bottlechoice.substr(0,1) == "~") { // "absolute" path in home directory, not expanded by the shell for some reason (i've seen some shit)
 | 
			
		||||
        // this is a naive replacement and will fail if the user tries something like ~nick/.wine
 | 
			
		||||
        // this is a naive replacement and will fail if the user tries something like ~nicole/.wine
 | 
			
		||||
        // i'm figuring at that point if you're doing that, you'll also recognize if your shell
 | 
			
		||||
        // isn't actually expanding your path...
 | 
			
		||||
        bottlechoice.replace(0,1,getenv("HOME"));
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,7 @@
 | 
			
		||||
 | 
			
		||||
#include "bottles.hpp"
 | 
			
		||||
#include "cellar.hpp"
 | 
			
		||||
#include "config.hpp"
 | 
			
		||||
#include "internal/config.hpp"
 | 
			
		||||
#include "output.hpp"
 | 
			
		||||
#include "version.hpp"
 | 
			
		||||
@@ -46,7 +47,15 @@ void cellar::config::config_command(int argc, vector<string> argv) {
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        string myval = active_bottle.get_config(key);
 | 
			
		||||
        string myval;
 | 
			
		||||
        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 != "") {
 | 
			
		||||
            output::statement(key + ": " + myval);
 | 
			
		||||
@@ -60,7 +69,14 @@ void cellar::config::config_command(int argc, vector<string> argv) {
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        string newvalue = value;
 | 
			
		||||
        string oldvalue = active_bottle.get_config(key);
 | 
			
		||||
        string oldvalue;
 | 
			
		||||
        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)) {
 | 
			
		||||
            output::statement(key + ": " + newvalue + " (was " + oldvalue + ")");
 | 
			
		||||
 
 | 
			
		||||
@@ -2,15 +2,14 @@
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include <boost/filesystem/operations.hpp>
 | 
			
		||||
#include <boost/filesystem/path.hpp>
 | 
			
		||||
#include <boost/filesystem.hpp>
 | 
			
		||||
 | 
			
		||||
#include "cellar.hpp"
 | 
			
		||||
#include "fs.hpp"
 | 
			
		||||
#include "output.hpp"
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
using namespace boost;
 | 
			
		||||
//using namespace boost;
 | 
			
		||||
using namespace cellar;
 | 
			
		||||
using namespace fs;
 | 
			
		||||
 | 
			
		||||
@@ -22,10 +21,10 @@ void cbnull_remove(string src) { return; }
 | 
			
		||||
 | 
			
		||||
vector<string> fs::listdir(string path) {
 | 
			
		||||
	vector<string> result;
 | 
			
		||||
    filesystem::path cwd(path);
 | 
			
		||||
    filesystem::directory_iterator iter_end;
 | 
			
		||||
    boost::filesystem::path cwd(path);
 | 
			
		||||
    boost::filesystem::directory_iterator iter_end;
 | 
			
		||||
 | 
			
		||||
    for (filesystem::directory_iterator iter_cwd(cwd); iter_cwd != iter_end; ++iter_cwd) {
 | 
			
		||||
    for (boost::filesystem::directory_iterator iter_cwd(cwd); iter_cwd != iter_end; ++iter_cwd) {
 | 
			
		||||
        string item = iter_cwd->path().filename().native();
 | 
			
		||||
		
 | 
			
		||||
		result.push_back(item);
 | 
			
		||||
@@ -34,10 +33,10 @@ vector<string> fs::listdir(string path) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool fs::recursive_copy(string src, string dst, CopyCallbackFunc callback) {
 | 
			
		||||
    if (!filesystem::exists(dst)) {
 | 
			
		||||
    if (!boost::filesystem::exists(dst)) {
 | 
			
		||||
        if (dryrun) { output::statement("mkdir:   " + dst); }
 | 
			
		||||
        else {
 | 
			
		||||
            bool success = filesystem::create_directory(dst);
 | 
			
		||||
            bool success = boost::filesystem::create_directory(dst);
 | 
			
		||||
            if (!success) { return false; }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -46,17 +45,17 @@ bool fs::recursive_copy(string src, string dst, CopyCallbackFunc callback) {
 | 
			
		||||
        string itemabs = src + "/" + itemrel;
 | 
			
		||||
        string targetabs = dst + "/" + itemrel;
 | 
			
		||||
 | 
			
		||||
        auto itemstat = filesystem::symlink_status(itemabs);
 | 
			
		||||
        auto itemstat = boost::filesystem::symlink_status(itemabs);
 | 
			
		||||
 | 
			
		||||
        if (filesystem::is_directory(itemstat)) { recursive_copy(itemabs, targetabs, callback); }
 | 
			
		||||
        else if (filesystem::is_symlink(itemstat)) { 
 | 
			
		||||
            auto symlinkpath = filesystem::read_symlink(itemabs);
 | 
			
		||||
        if (boost::filesystem::is_directory(itemstat)) { recursive_copy(itemabs, targetabs, callback); }
 | 
			
		||||
        else if (boost::filesystem::is_symlink(itemstat)) { 
 | 
			
		||||
            auto symlinkpath = boost::filesystem::read_symlink(itemabs);
 | 
			
		||||
            if (dryrun) { output::statement("symlink: " + symlinkpath.native() + " => " + targetabs); }
 | 
			
		||||
            else { filesystem::create_symlink(symlinkpath, targetabs); }
 | 
			
		||||
            else { boost::filesystem::create_symlink(symlinkpath, targetabs); }
 | 
			
		||||
        }
 | 
			
		||||
        else { 
 | 
			
		||||
            if (dryrun) { output::statement("copy:    " + itemabs + " => " + targetabs); }
 | 
			
		||||
            else { filesystem::copy(itemabs, targetabs); }
 | 
			
		||||
            else { boost::filesystem::copy(itemabs, targetabs); }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        callback(itemabs, targetabs);
 | 
			
		||||
@@ -68,24 +67,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_remove(string target, RemoveCallbackFunc callback) {
 | 
			
		||||
    if (!filesystem::exists(target)) { return false; }
 | 
			
		||||
    if (!boost::filesystem::exists(target)) { return false; }
 | 
			
		||||
 | 
			
		||||
    for (string itemrel : listdir(target)) {
 | 
			
		||||
        string itemabs = target + "/" + itemrel;
 | 
			
		||||
 | 
			
		||||
        auto itemstat = filesystem::symlink_status(itemabs);
 | 
			
		||||
        auto itemstat = boost::filesystem::symlink_status(itemabs);
 | 
			
		||||
 | 
			
		||||
        if (filesystem::is_directory(itemstat)) { recursive_remove(itemabs, callback); }
 | 
			
		||||
        if (boost::filesystem::is_directory(itemstat)) { recursive_remove(itemabs, callback); }
 | 
			
		||||
        else {
 | 
			
		||||
            if (dryrun) { output::error("rm:      " + itemabs); } 
 | 
			
		||||
            else { filesystem::remove(itemabs); }
 | 
			
		||||
            else { boost::filesystem::remove(itemabs); }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        callback(itemabs);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (dryrun) { output::error("rm:      " + target); }
 | 
			
		||||
    else { filesystem::remove(target); }
 | 
			
		||||
    else { boost::filesystem::remove(target); }
 | 
			
		||||
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
core
 | 
			
		||||
config
 | 
			
		||||
bottles
 | 
			
		||||
launch
 | 
			
		||||
launch
 | 
			
		||||
							
								
								
									
										66
									
								
								src/steam/bottles.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/steam/bottles.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,66 @@
 | 
			
		||||
#include <filesystem>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <map>
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
#include "vdf_parser.hpp"
 | 
			
		||||
 | 
			
		||||
#include "bottles.hpp"
 | 
			
		||||
#include "steam.hpp"
 | 
			
		||||
#include "internal/steam.hpp"
 | 
			
		||||
 | 
			
		||||
using namespace tyti; //vdf
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Returns all app bottles managed by Steam.
 | 
			
		||||
 * 
 | 
			
		||||
 * @return std::map<std::string, cellar::bottles::Bottle> Steam managed bottles. Keys are "steam:<appid>".
 | 
			
		||||
 */
 | 
			
		||||
std::map<std::string, cellar::bottles::Bottle> cellar::steam::get_app_bottles() {
 | 
			
		||||
    std::map<std::string, cellar::bottles::Bottle> result;
 | 
			
		||||
 | 
			
		||||
    for (std::string str_path_library : cellar::steam::find_steam_libraries()) {
 | 
			
		||||
        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")) {
 | 
			
		||||
                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;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                auto curbottle = cellar::bottles::Bottle((pth_appid / "pfx").string());
 | 
			
		||||
                curbottle.set_config("name", str_gamename);
 | 
			
		||||
                curbottle.save_config();
 | 
			
		||||
                result[std::string("steam:" + pth_appid.filename().string())] = curbottle;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return result;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
cellar::bottles::Bottle cellar::steam::app_bottle(unsigned appid) {
 | 
			
		||||
    string str_appid = std::to_string(appid);
 | 
			
		||||
    string str_prefix = std::string("steam:") + str_appid;
 | 
			
		||||
    auto steambottles = get_app_bottles();
 | 
			
		||||
    if (steambottles.find(str_prefix) == steambottles.end()) {
 | 
			
		||||
        throw std::range_error("steam is not currently managing a valid prefix for " + std::to_string(appid));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return steambottles.at(str_prefix);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										39
									
								
								src/steam/paths.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/steam/paths.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,39 @@
 | 
			
		||||
#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;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * @brief Reads Steam library settings and returns a list of Steam library paths.
 | 
			
		||||
 * Returns an empty vector if it can't read ~/.steam/root/config/libraryfolders.vdf.
 | 
			
		||||
 * 
 | 
			
		||||
 * @return std::vector<std::string> Steam library paths.
 | 
			
		||||
 */
 | 
			
		||||
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::vector<std::string> result = {};
 | 
			
		||||
 | 
			
		||||
    std::ifstream fd_steam_library_config(str_steam_library_config);
 | 
			
		||||
    if (fd_steam_library_config.fail()) { return result; } // return empty if something went wrong (should cover most problems)
 | 
			
		||||
    auto hnd_steam_library_config = vdf::read(fd_steam_library_config);
 | 
			
		||||
 | 
			
		||||
    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;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user