From 5dfb5f3b88542a72a55d000498deef25c8441e0f Mon Sep 17 00:00:00 2001 From: gamingdoom <37276884+gamingdoom@users.noreply.github.com> Date: Mon, 16 Oct 2023 22:13:30 -0700 Subject: [PATCH] works but linux build is broken and this is an old version of the source --- .vscode/launch.json | 36 ++ .vscode/settings.json | 94 ++++- .vscode/tasks.json | 28 ++ CMakeLists.txt | 7 +- include/Network/network.hpp | 9 + include/linuxfixes.h | 19 + include/vdf_parser.hpp | 730 ++++++++++++++++++++++++++++++++++ src/Compressor.cpp | 7 +- src/Discord.cpp | 5 + src/GameStart.cpp | 47 ++- src/Network/Core.cpp | 51 ++- src/Network/DNS.cpp | 11 +- src/Network/GlobalHandler.cpp | 17 +- src/Network/Resources.cpp | 24 +- src/Network/VehicleData.cpp | 18 + src/Network/VehicleEvent.cpp | 21 +- src/Security/BeamNG.cpp | 33 ++ src/Security/Login.cpp | 2 +- src/Startup.cpp | 64 +++ 19 files changed, 1200 insertions(+), 23 deletions(-) create mode 100644 .vscode/launch.json create mode 100644 .vscode/tasks.json create mode 100644 include/linuxfixes.h create mode 100644 include/vdf_parser.hpp diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..dd67071 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,36 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "(gdb) Launch", + "type": "cppdbg", + "request": "launch", + // Resolved by CMake Tools: + "program": "${command:cmake.launchTargetPath}", + "args": [], + "stopAtEntry": false, + "cwd": "${workspaceFolder}", + "environment": [ + { + // add the directory where our target was built to the PATHs + // it gets resolved by CMake Tools: + "name": "PATH", + "value": "${env:PATH}:${command:cmake.getLaunchTargetDirectory}" + }, + { + "name": "OTHER_VALUE", + "value": "Something something" + } + ], + "console": "externalTerminal", + "MIMode": "gdb", + "setupCommands": [ + { + "description": "Enable pretty-printing for gdb", + "text": "-enable-pretty-printing", + "ignoreFailures": true + } + ] + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index c9a6001..0b993e5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,95 @@ { - "dotnet.defaultSolution": "disable" + "dotnet.defaultSolution": "disable", + "files.associations": { + "condition_variable": "cpp", + "thread": "cpp", + "cctype": "cpp", + "clocale": "cpp", + "cmath": "cpp", + "csetjmp": "cpp", + "csignal": "cpp", + "cstdarg": "cpp", + "cstddef": "cpp", + "cstdio": "cpp", + "cstdlib": "cpp", + "cstring": "cpp", + "ctime": "cpp", + "cwchar": "cpp", + "cwctype": "cpp", + "any": "cpp", + "array": "cpp", + "atomic": "cpp", + "hash_map": "cpp", + "hash_set": "cpp", + "strstream": "cpp", + "bit": "cpp", + "*.tcc": "cpp", + "bitset": "cpp", + "cfenv": "cpp", + "charconv": "cpp", + "chrono": "cpp", + "cinttypes": "cpp", + "codecvt": "cpp", + "compare": "cpp", + "complex": "cpp", + "concepts": "cpp", + "coroutine": "cpp", + "cstdint": "cpp", + "deque": "cpp", + "forward_list": "cpp", + "list": "cpp", + "map": "cpp", + "set": "cpp", + "string": "cpp", + "unordered_map": "cpp", + "unordered_set": "cpp", + "vector": "cpp", + "exception": "cpp", + "algorithm": "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", + "scoped_allocator": "cpp", + "semaphore": "cpp", + "shared_mutex": "cpp", + "span": "cpp", + "sstream": "cpp", + "stdexcept": "cpp", + "stdfloat": "cpp", + "stop_token": "cpp", + "streambuf": "cpp", + "typeindex": "cpp", + "typeinfo": "cpp", + "valarray": "cpp", + "variant": "cpp", + "__nullptr": "cpp", + "ios": "cpp", + "*.ipp": "cpp", + "locale": "cpp" + } } \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..05054c5 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "tasks": [ + { + "type": "cppbuild", + "label": "C/C++: g++ build active file", + "command": "/usr/bin/g++", + "args": [ + "-fdiagnostics-color=always", + "-g", + "${file}", + "-o", + "${fileDirname}/${fileBasenameNoExtension}" + ], + "options": { + "cwd": "${fileDirname}" + }, + "problemMatcher": [ + "$gcc" + ], + "group": { + "kind": "build", + "isDefault": true + }, + "detail": "Task generated by Debugger." + } + ], + "version": "2.0.0" +} \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 8ab57b7..8d6c084 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,7 +11,7 @@ set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG") -file(GLOB source_files "src/*.cpp" "src/*/*.cpp" "src/*/*.hpp" "include/*.h" "include/*/*.h" "include/*/*/*.h") +file(GLOB source_files "src/*.cpp" "src/*/*.cpp" "src/*/*.hpp" "include/*.h" "include/*/*.h" "include/*/*/*.h" "include/*.hpp" "include/*/*.hpp" "include/*/*/*.hpp") add_executable(${PROJECT_NAME} ${source_files}) set_target_properties(${PROJECT_NAME} PROPERTIES OUTPUT_NAME "BeamMP-Launcher") @@ -25,6 +25,11 @@ if (WIN32) link_directories(${VcpkgRoot}/lib) target_link_libraries(${PROJECT_NAME} PRIVATE ${VcpkgRoot}/lib/discord-rpc.lib ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto ws2_32) +elseif (LINUX) + find_package(ZLIB REQUIRED) + find_package(OpenSSL REQUIRED) + target_link_libraries(${PROJECT_NAME} PRIVATE + ZLIB::ZLIB OpenSSL::SSL OpenSSL::Crypto -g) else(WIN32) #MINGW add_definitions("-D_WIN32_WINNT=0x0600") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Os -s --static") diff --git a/include/Network/network.hpp b/include/Network/network.hpp index eea935a..d5643f9 100755 --- a/include/Network/network.hpp +++ b/include/Network/network.hpp @@ -8,7 +8,16 @@ #pragma once +#include #include +#include + +#ifdef __linux__ +#include +#include "linuxfixes.h" +#endif + + void NetReset(); extern bool Dev; diff --git a/include/linuxfixes.h b/include/linuxfixes.h new file mode 100644 index 0000000..c3fc21d --- /dev/null +++ b/include/linuxfixes.h @@ -0,0 +1,19 @@ +#ifndef _LINUXFIXES_H +#define _LINUXFIXES_H + +#include + +// Translate windows sockets stuff to linux sockets +#define SOCKET uint64_t +#define SOCKADDR sockaddr +#define SOCKADDR_IN sockaddr_in +#define WSAGetLastError() errno +#define closesocket close +#define SD_BOTH SHUT_RDWR +// We dont need wsacleanup +#define WSACleanup() +#define SOCKET_ERROR -1 + +#define ZeroMemory(mem, len) memset(mem, 0, len) + +#endif \ No newline at end of file diff --git a/include/vdf_parser.hpp b/include/vdf_parser.hpp new file mode 100644 index 0000000..23af579 --- /dev/null +++ b/include/vdf_parser.hpp @@ -0,0 +1,730 @@ +//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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +//for wstring support +#include +#include + +// internal +#include + +//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 + struct literal_macro_help + { + static CONSTEXPR const char* result(const char* c, const wchar_t*) NOEXCEPT + { + return c; + } + static CONSTEXPR const char result(const char c, const wchar_t) NOEXCEPT + { + return c; + } + }; + + template <> + struct literal_macro_help + { + static CONSTEXPR const wchar_t* result(const char*, const wchar_t* wc) NOEXCEPT + { + return wc; + } + static CONSTEXPR const wchar_t result(const char, const wchar_t wc) NOEXCEPT + { + return wc; + } + }; +#define TYTI_L(type, text) vdf::detail::literal_macro_help::result(text, L##text) + + inline std::string string_converter(const std::string& w) NOEXCEPT + { + return w; + } + + // utility wrapper to adapt locale-bound facets for wstring/wbuffer convert + // from cppreference + template + struct deletable_facet : Facet + { + template + deletable_facet(Args &&... args) : Facet(std::forward(args)...) {} + ~deletable_facet() {} + }; + + inline std::string string_converter(const std::wstring& w) //todo: use us-locale + { + std::wstring_convert>> conv1; + return conv1.to_bytes(w); + } + + /////////////////////////////////////////////////////////////////////////// + // Writer helper functions + /////////////////////////////////////////////////////////////////////////// + + template + class tabs + { + const size_t t; + + public: + explicit CONSTEXPR tabs(size_t i) NOEXCEPT : t(i) {} + std::basic_string print() const { return std::basic_string(t, TYTI_L(charT, '\t')); } + inline CONSTEXPR tabs operator+(size_t i) const NOEXCEPT + { + return tabs(t + i); + } + }; + + template + oStreamT& operator<<(oStreamT& s, const tabs t) + { + s << t.print(); + return s; + } + } // 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 + struct basic_object + { + typedef CharT char_type; + std::basic_string name; + std::unordered_map, std::basic_string> attribs; + std::unordered_map, std::shared_ptr>> childs; + + void add_attribute(std::basic_string key, std::basic_string value) + { + attribs.emplace(std::move(key), std::move(value)); + } + void add_child(std::unique_ptr> child) + { + std::shared_ptr> obj{ child.release() }; + childs.emplace(obj->name, obj); + } + void set_name(std::basic_string n) + { + name = std::move(n); + } + }; + + template + struct basic_multikey_object + { + typedef CharT char_type; + std::basic_string name; + std::unordered_multimap, std::basic_string> attribs; + std::unordered_multimap, std::shared_ptr>> childs; + + void add_attribute(std::basic_string key, std::basic_string value) + { + attribs.emplace(std::move(key), std::move(value)); + } + void add_child(std::unique_ptr> child) + { + std::shared_ptr> obj{ child.release() }; + childs.emplace(obj->name, obj); + } + void set_name(std::basic_string n) + { + name = std::move(n); + } + }; + + typedef basic_object object; + typedef basic_object wobject; + typedef basic_multikey_object multikey_object; + typedef basic_multikey_object wmultikey_object; + + struct Options + { + bool strip_escape_symbols; + bool ignore_all_platform_conditionals; + bool ignore_includes; + + Options() : strip_escape_symbols(true), ignore_all_platform_conditionals(false), ignore_includes(false) {} + }; + + //forward decls + //forward decl + template + OutputT read(iStreamT& inStream, const Options& opt = Options{}); + + /** \brief writes given object tree in vdf format to given stream. + Output is prettyfied, using tabs + */ + template + void write(oStreamT& s, const T& r, + const detail::tabs tab = detail::tabs(0)) + { + typedef typename oStreamT::char_type charT; + using namespace detail; + s << tab << TYTI_L(charT, '"') << r.name << TYTI_L(charT, "\"\n") << tab << TYTI_L(charT, "{\n"); + for (const auto& i : r.attribs) + s << tab + 1 << TYTI_L(charT, '"') << i.first << TYTI_L(charT, "\"\t\t\"") << i.second << TYTI_L(charT, "\"\n"); + for (const auto& i : r.childs) + if (i.second) + write(s, *i.second, tab + 1); + s << tab << TYTI_L(charT, "}\n"); + } + + namespace detail + { + template + std::basic_string read_file(iStreamT& inStream) + { + // cache the file + typedef typename iStreamT::char_type charT; + std::basic_string str; + inStream.seekg(0, std::ios::end); + str.resize(static_cast(inStream.tellg())); + if (str.empty()) + return str; + + inStream.seekg(0, std::ios::beg); + inStream.read(&str[0], str.size()); + return str; + } + + /** \brief Read VDF formatted sequences defined by the range [first, last). + If the file is mailformatted, parser will try to read it until it can. + @param first begin iterator + @param end end iterator + @param exclude_files list of files which cant be included anymore. + prevents circular includes + + can thow: + - "std::runtime_error" if a parsing error occured + - "std::bad_alloc" if not enough memory coup be allocated + */ + template + std::vector> read_internal(IterT first, const IterT last, + std::unordered_set::value_type>>& exclude_files, + const Options& opt) + { + static_assert(std::is_default_constructible::value, + "Output Type must be default constructible (provide constructor without arguments)"); + static_assert(std::is_move_constructible::value, + "Output Type must be move constructible"); + + typedef typename std::iterator_traits::value_type charT; + + const std::basic_string comment_end_str = TYTI_L(charT, "*/"); + const std::basic_string whitespaces = TYTI_L(charT, " \n\v\f\r\t"); + +#ifdef WIN32 + std::function&)> is_platform_str = [](const std::basic_string& in) { + return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$WINDOWS"); + }; +#elif __APPLE__ + // WIN32 stands for pc in general + std::function&)> is_platform_str = [](const std::basic_string& in) { + return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || in == TYTI_L(charT, "$OSX"); + }; + +#elif __linux__ + // WIN32 stands for pc in general + std::function&)> is_platform_str = [](const std::basic_string& in) { + return in == TYTI_L(charT, "$WIN32") || in == TYTI_L(charT, "$POSIX") || in == TYTI_L(charT, "$LINUX"); + }; +#else + std::function&)> is_platform_str = [](const std::basic_string& in) { + return false; + }; +#endif + + if (opt.ignore_all_platform_conditionals) + is_platform_str = [](const std::basic_string&) { + return false; + }; + + // function for skipping a comment block + // iter: iterator poition to the position after a '/' + auto skip_comments = [&comment_end_str](IterT iter, const IterT& last) -> IterT { + ++iter; + if (iter != last) + { + if (*iter == TYTI_L(charT, '/')) + { + // line comment, skip whole line + iter = std::find(iter + 1, last, TYTI_L(charT, '\n')); + } + + if (*iter == '*') + { + // block comment, skip until next occurance of "*\" + iter = std::search(iter + 1, last, std::begin(comment_end_str), std::end(comment_end_str)); + iter += 2; + } + } + return iter; + }; + + auto end_quote = [](IterT iter, const IterT& last) -> IterT { + const auto begin = iter; + auto last_esc = iter; + do + { + ++iter; + iter = std::find(iter, last, TYTI_L(charT, '\"')); + if (iter == last) + break; + + last_esc = std::prev(iter); + while (last_esc != begin && *last_esc == '\\') + --last_esc; + } while (!(std::distance(last_esc, iter) % 2)); + if (iter == last) + throw std::runtime_error{ "quote was opened but not closed." }; + return iter; + }; + + auto end_word = [&whitespaces](IterT iter, const IterT& last) -> IterT { + const auto begin = iter; + auto last_esc = iter; + do + { + ++iter; + iter = std::find_first_of(iter, last, std::begin(whitespaces), std::end(whitespaces)); + if (iter == last) + break; + + last_esc = std::prev(iter); + while (last_esc != begin && *last_esc == '\\') + --last_esc; + } while (!(std::distance(last_esc, iter) % 2)); + //if (iter == last) + // throw std::runtime_error{ "word wasnt properly ended" }; + return iter; + }; + + auto skip_whitespaces = [&whitespaces](IterT iter, const IterT& last) -> IterT { + iter = std::find_if_not(iter, last, [&whitespaces](charT c) { + // return true if whitespace + return std::any_of(std::begin(whitespaces), std::end(whitespaces), [c](charT pc) { return pc == c; }); + }); + return iter; + }; + + std::function&)> strip_escape_symbols = [](std::basic_string& s) { + auto quote_searcher = [&s](size_t pos) { return s.find(TYTI_L(charT, "\\\""), pos); }; + auto p = quote_searcher(0); + while (p != s.npos) + { + s.replace(p, 2, TYTI_L(charT, "\"")); + p = quote_searcher(p); + } + auto searcher = [&s](size_t pos) { return s.find(TYTI_L(charT, "\\\\"), pos); }; + p = searcher(0); + while (p != s.npos) + { + s.replace(p, 2, TYTI_L(charT, "\\")); + p = searcher(p); + } + }; + + if (!opt.strip_escape_symbols) + strip_escape_symbols = [](std::basic_string&) {}; + + auto conditional_fullfilled = [&skip_whitespaces, &is_platform_str](IterT& iter, const IterT& last) { + iter = skip_whitespaces(iter, last); + if (*iter == '[') + { + ++iter; + const auto end = std::find(iter, last, ']'); + const bool negate = *iter == '!'; + if (negate) + ++iter; + auto conditional = std::basic_string(iter, end); + + const bool is_platform = is_platform_str(conditional); + iter = end + 1; + + return static_cast(is_platform ^ negate); + } + return true; + }; + + //read header + // first, quoted name + std::unique_ptr curObj = nullptr; + std::vector> roots; + std::stack> lvls; + auto curIter = first; + + while (curIter != last && *curIter != '\0') + { + //find first starting attrib/child, or ending + curIter = skip_whitespaces(curIter, last); + if (curIter == last || *curIter == '\0') + break; + if (*curIter == TYTI_L(charT, '/')) + { + curIter = skip_comments(curIter, last); + } + else if (*curIter != TYTI_L(charT, '}')) + { + + // get key + const auto keyEnd = (*curIter == TYTI_L(charT, '\"')) ? end_quote(curIter, last) : end_word(curIter, last); + if (*curIter == TYTI_L(charT, '\"')) + ++curIter; + std::basic_string key(curIter, keyEnd); + strip_escape_symbols(key); + curIter = keyEnd + ((*keyEnd == TYTI_L(charT, '\"')) ? 1 : 0); + + curIter = skip_whitespaces(curIter, last); + + auto conditional = conditional_fullfilled(curIter, last); + if (!conditional) + continue; + + while (*curIter == TYTI_L(charT, '/')) + { + + curIter = skip_comments(curIter, last); + if (curIter == last || *curIter == '}') + throw std::runtime_error{ "key declared, but no value" }; + curIter = skip_whitespaces(curIter, last); + if (curIter == last || *curIter == '}') + throw std::runtime_error{ "key declared, but no value" }; + } + // get value + if (*curIter != '{') + { + const auto valueEnd = (*curIter == TYTI_L(charT, '\"')) ? end_quote(curIter, last) : end_word(curIter, last); + if (*curIter == TYTI_L(charT, '\"')) + ++curIter; + + auto value = std::basic_string(curIter, valueEnd); + strip_escape_symbols(value); + curIter = valueEnd + ((*valueEnd == TYTI_L(charT, '\"')) ? 1 : 0); + + auto conditional = conditional_fullfilled(curIter, last); + if (!conditional) + continue; + + // process value + if (key != TYTI_L(charT, "#include") && key != TYTI_L(charT, "#base")) + { + if (curObj) + { + curObj->add_attribute(std::move(key), std::move(value)); + } + else + { + throw std::runtime_error{ "unexpected key without object" }; + } + } + else + { + if (!opt.ignore_includes && exclude_files.find(value) == exclude_files.end()) + { + exclude_files.insert(value); + std::basic_ifstream i(detail::string_converter(value)); + auto str = read_file(i); + auto file_objs = read_internal(str.begin(), str.end(), exclude_files, opt); + for (auto& n : file_objs) + { + if (curObj) + curObj->add_child(std::move(n)); + else + roots.push_back(std::move(n)); + } + exclude_files.erase(value); + } + } + } + else if (*curIter == '{') + { + if (curObj) + lvls.push(std::move(curObj)); + curObj = std::make_unique(); + curObj->set_name(std::move(key)); + ++curIter; + } + } + //end of new object + else if (curObj && *curIter == TYTI_L(charT, '}')) + { + if (!lvls.empty()) + { + //get object before + std::unique_ptr prev{ std::move(lvls.top()) }; + lvls.pop(); + + // add finished obj to obj before and release it from processing + prev->add_child(std::move(curObj)); + curObj = std::move(prev); + } + else + { + roots.push_back(std::move(curObj)); + curObj.reset(); + } + ++curIter; + } + 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 + OutputT read(IterT first, const IterT last, const Options& opt = Options{}) + { + auto exclude_files = std::unordered_set::value_type>>{}; + auto roots = detail::read_internal(first, last, exclude_files, opt); + + OutputT result; + if (roots.size() > 1) + { + 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 + OutputT read(IterT first, IterT last, std::error_code& ec, const Options& opt = Options{}) NOEXCEPT + + { + ec.clear(); + OutputT r{}; + try + { + r = read(first, last, opt); + } + catch (std::runtime_error&) + { + ec = std::make_error_code(std::errc::protocol_error); + } + catch (std::bad_alloc&) + { + ec = std::make_error_code(std::errc::not_enough_memory); + } + catch (...) + { + ec = std::make_error_code(std::errc::invalid_argument); + } + return r; + } + + /** \brief Read VDF formatted sequences defined by the range [first, last). + If the file is mailformatted, parser will try to read it until it can. + @param first begin iterator + @param end end iterator + @param ok output bool. true, if parser successed, false, if parser failed + */ + template + OutputT read(IterT first, const IterT last, bool* ok, const Options& opt = Options{}) NOEXCEPT + { + std::error_code ec; + auto r = read(first, last, ec, opt); + if (ok) + *ok = !ec; + return r; + } + + template + inline auto read(IterT first, const IterT last, bool* ok, const Options& opt = Options{}) NOEXCEPT -> basic_object::value_type> + { + return read::value_type>>(first, last, ok, opt); + } + + template + inline auto read(IterT first, IterT last, std::error_code& ec, const Options& opt = Options{}) NOEXCEPT + -> basic_object::value_type> + { + return read::value_type>>(first, last, ec, opt); + } + + template + inline auto read(IterT first, const IterT last, const Options& opt = Options{}) + -> basic_object::value_type> + { + return read::value_type>>(first, last, opt); + } + + /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. + throws "std::bad_alloc" if file buffer could not be allocated + */ + template + OutputT read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{}) + { + // cache the file + typedef typename iStreamT::char_type charT; + std::basic_string str = detail::read_file(inStream); + + // parse it + return read(str.begin(), str.end(), ec, opt); + } + + template + inline basic_object read(iStreamT& inStream, std::error_code& ec, const Options& opt = Options{}) + { + return read>(inStream, ec, opt); + } + + /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. + throws "std::bad_alloc" if file buffer could not be allocated + ok == false, if a parsing error occured + */ + template + OutputT read(iStreamT& inStream, bool* ok, const Options& opt = Options{}) + { + std::error_code ec; + const auto r = read(inStream, ec, opt); + if (ok) + *ok = !ec; + return r; + } + + template + inline basic_object read(iStreamT& inStream, bool* ok, const Options& opt = Options{}) + { + return read>(inStream, ok, opt); + } + + /** \brief Loads a stream (e.g. filestream) into the memory and parses the vdf formatted data. + throws "std::bad_alloc" if file buffer could not be allocated + throws "std::runtime_error" if a parsing error occured + */ + template + OutputT read(iStreamT& inStream, const Options& opt) + { + + // cache the file + typedef typename iStreamT::char_type charT; + std::basic_string str = detail::read_file(inStream); + // parse it + return read(str.begin(), str.end(), opt); + } + + template + inline basic_object read(iStreamT& inStream, const Options& opt = Options{}) + { + return read>(inStream, opt); + } + + } // namespace vdf +} // namespace tyti +#ifndef TYTI_NO_L_UNDEF +#undef TYTI_L +#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__ diff --git a/src/Compressor.cpp b/src/Compressor.cpp index 6ee00ef..436dccc 100755 --- a/src/Compressor.cpp +++ b/src/Compressor.cpp @@ -8,6 +8,9 @@ #include #include +#ifdef __linux__ +#include +#endif #define Biggest 30000 std::string Comp(std::string Data){ @@ -27,7 +30,7 @@ std::string Comp(std::string Data){ deflateEnd(&defstream); int TO = defstream.total_out; std::string Ret(TO,0); - memcpy_s(&Ret[0],TO,C,TO); + memcpy(&Ret[0],C,TO); delete [] C; return Ret; } @@ -48,7 +51,7 @@ std::string DeComp(std::string Compressed){ inflateEnd(&infstream); int TO = infstream.total_out; std::string Ret(TO,0); - memcpy_s(&Ret[0],TO,C,TO); + memcpy(&Ret[0],C,TO); delete [] C; return Ret; } \ No newline at end of file diff --git a/src/Discord.cpp b/src/Discord.cpp index 62c28b4..d5babd7 100755 --- a/src/Discord.cpp +++ b/src/Discord.cpp @@ -5,6 +5,9 @@ /// /// Created by Anonymous275 on 7/16/2020 /// + +#ifndef __linux__ + #include "Discord/discord_rpc.h" #include "Logger.h" #include @@ -109,3 +112,5 @@ void Discord_Main(){ } if(DiscordInfo == nullptr)ErrorAboard();*/ } + +#endif \ No newline at end of file diff --git a/src/GameStart.cpp b/src/GameStart.cpp index 2583349..ccd2d7c 100755 --- a/src/GameStart.cpp +++ b/src/GameStart.cpp @@ -6,13 +6,25 @@ /// Created by Anonymous275 on 7/19/2020 /// -#include + +#if defined(_WIN32) #include +#elif defined(__linux__) +#include "vdf_parser.hpp" +#include +#include +#include +#endif + +#include +#include +#include #include "Startup.h" #include "Logger.h" #include unsigned long GamePID = 0; +#if defined(_WIN32) std::string QueryKey(HKEY hKey,int ID); std::string GetGamePath(){ static std::string Path; @@ -40,7 +52,21 @@ std::string GetGamePath(){ Path += Ver + "\\"; return Path; } +#elif defined(__linux__) +std::string GetGamePath(){ + // Right now only steam is supported + struct passwd *pw = getpwuid(getuid()); + std::string homeDir = pw->pw_dir; + + std::string Path = homeDir + "/.local/share/BeamNG.drive/"; + std::string Ver = CheckVer(GetGameDir()); + Ver = Ver.substr(0,Ver.find('.',Ver.find('.')+1)); + Path += Ver + "/"; + return Path; +} +#endif +#if defined(_WIN32) void StartGame(std::string Dir){ BOOL bSuccess = FALSE; PROCESS_INFORMATION pi; @@ -61,6 +87,25 @@ void StartGame(std::string Dir){ std::this_thread::sleep_for(std::chrono::seconds(5)); exit(2); } +#elif defined(__linux__) +void StartGame(std::string Dir){ + int status; + pid_t pid = fork(); + if (pid >= 0){ + if (pid == 0){ + execl((Dir + "/BinLinux/BeamNG.drive.x64").c_str(), "", NULL); + } else if (pid > 0){ + waitpid(pid, &status, 0); + error("Game Closed! launcher closing soon"); + } + } else { + error("Failed to Launch the game! launcher closing soon"); + } + std::this_thread::sleep_for(std::chrono::seconds(5)); + exit(2); +} +#endif + void InitGame(const std::string& Dir){ if(!Dev){ std::thread Game(StartGame, Dir); diff --git a/src/Network/Core.cpp b/src/Network/Core.cpp index 4256f58..46197d0 100755 --- a/src/Network/Core.cpp +++ b/src/Network/Core.cpp @@ -9,8 +9,18 @@ #include "Security/Init.h" #include "Http.h" +#if defined(_WIN32) #include #include + +#elif defined(__linux__) +#include +#include +#include +#include +#include +#endif + #include "Startup.h" #include "Logger.h" #include @@ -177,13 +187,18 @@ void localRes(){ } void CoreMain() { debug("Core Network on start!"); - WSADATA wsaData; SOCKET LSocket,CSocket; struct addrinfo *res = nullptr; struct addrinfo hints{}; - int iRes = WSAStartup(514, &wsaData); //2.2 + int iRes; + #ifdef _WIN32 + WSADATA wsaData; + iRes = WSAStartup(514, &wsaData); //2.2 if (iRes)debug("WSAStartup failed with error: " + std::to_string(iRes)); + #endif + ZeroMemory(&hints, sizeof(hints)); + hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; hints.ai_protocol = IPPROTO_TCP; @@ -231,24 +246,40 @@ void CoreMain() { KillSocket(LSocket); WSACleanup(); } + +#if defined(_WIN32) int Handle(EXCEPTION_POINTERS *ep){ char* hex = new char[100]; - sprintf_s(hex,100, "%lX", ep->ExceptionRecord->ExceptionCode); + sprintf(hex,100, "%lX", ep->ExceptionRecord->ExceptionCode); except("(Core) Code : " + std::string(hex)); delete [] hex; return 1; } - +#endif [[noreturn]] void CoreNetwork(){ while(true) { -#ifndef __MINGW32__ +// #ifndef __MINGW32__ +// __try{ +// #endif +// CoreMain(); +// #ifndef __MINGW32__ +// }__except(Handle(GetExceptionInformation())){} +// #endif + #if not defined(__MINGW32__) __try{ -#endif - CoreMain(); -#ifndef __MINGW32__ + #endif + + CoreMain(); + + #if not defined(__MINGW32__) and not defined(__linux__) }__except(Handle(GetExceptionInformation())){} -#endif - std::this_thread::sleep_for(std::chrono::seconds(1)); + #elif not defined(__MINGW32__) and defined(__linux__) + } catch(...){ + except("(Core) Code : " + std::string(strerror(errno))); + } + #endif + + std::this_thread::sleep_for(std::chrono::seconds(1)); } } diff --git a/src/Network/DNS.cpp b/src/Network/DNS.cpp index eff5dcf..5407533 100755 --- a/src/Network/DNS.cpp +++ b/src/Network/DNS.cpp @@ -7,18 +7,27 @@ /// #include +#if defined(_WIN32) #include +#elif defined(__linux__) +#include "linuxfixes.h" +#include +#include +#endif #include "Logger.h" std::string GetAddr(const std::string&IP){ if(IP.find_first_not_of("0123456789.") == -1)return IP; - WSADATA wsaData; hostent *host; + #ifdef _WIN32 + WSADATA wsaData; if(WSAStartup(514, &wsaData) != 0){ error("WSA Startup Failed!"); WSACleanup(); return ""; } + #endif + host = gethostbyname(IP.c_str()); if(!host){ error("DNS lookup failed! on " + IP); diff --git a/src/Network/GlobalHandler.cpp b/src/Network/GlobalHandler.cpp index 1216f31..9d551d4 100755 --- a/src/Network/GlobalHandler.cpp +++ b/src/Network/GlobalHandler.cpp @@ -6,8 +6,19 @@ /// Created by Anonymous275 on 7/25/2020 /// #include "Network/network.hpp" +#if defined(_WIN32) #include #include +#elif defined(__linux__) +#include "linuxfixes.h" +#include +#include +#include +#include +#include +#include +#endif + #include "Logger.h" #include #include @@ -117,12 +128,16 @@ SOCKET SetupListener(){ if(GSocket != -1)return GSocket; struct addrinfo *result = nullptr; struct addrinfo hints{}; + int iRes; + #ifdef _WIN32 WSADATA wsaData; - int iRes = WSAStartup(514, &wsaData); //2.2 + iRes = WSAStartup(514, &wsaData); //2.2 if (iRes != 0) { error("(Proxy) WSAStartup failed with error: " + std::to_string(iRes)); return -1; } + #endif + ZeroMemory(&hints, sizeof(hints)); hints.ai_family = AF_INET; hints.ai_socktype = SOCK_STREAM; diff --git a/src/Network/Resources.cpp b/src/Network/Resources.cpp index cc873cc..27977ee 100755 --- a/src/Network/Resources.cpp +++ b/src/Network/Resources.cpp @@ -7,7 +7,18 @@ /// #include "Network/network.hpp" + +#if defined(_WIN32) #include +#elif defined(__linux__) +#include +#include +#include +#include +#include +#include +#endif + #include #include "Startup.h" #include "Logger.h" @@ -38,7 +49,12 @@ std::vector Split(const std::string& String,const std::string& deli void CheckForDir(){ if(!fs::exists("Resources")){ + // Could we just use fs::create_directory instead? + #if defined(_WIN32) _wmkdir(L"Resources"); + #elif defined(__linux__) + fs::create_directory(L"Resources"); + #endif } } void WaitForConfirm(){ @@ -202,12 +218,16 @@ std::string MultiDownload(SOCKET MSock,SOCKET DSock, uint64_t Size, const std::s ///omg yes very ugly my god but i was in a rush will revisit std::string Ret(Size,0); - memcpy_s(&Ret[0],MSize,MData,MSize); + memcpy(&Ret[0],MData,MSize); delete[]MData; - memcpy_s(&Ret[MSize],DSize,DData,DSize); + memcpy(&Ret[MSize],DData,DSize); delete[]DData; + // std::string Ret = std::string(MData) + std::string(DData); + // delete []MData; + // delete []DData; + return Ret; } diff --git a/src/Network/VehicleData.cpp b/src/Network/VehicleData.cpp index 6624aca..888e985 100755 --- a/src/Network/VehicleData.cpp +++ b/src/Network/VehicleData.cpp @@ -8,7 +8,18 @@ #include "Zlib/Compressor.h" #include "Network/network.hpp" +#if defined(_WIN32) #include +#elif defined(__linux__) +#include +#include +#include +#include +#include +#include +#include "linuxfixes.h" +#endif + #include "Logger.h" #include #include @@ -44,7 +55,11 @@ void UDPParser(std::string Packet){ } void UDPRcv(){ sockaddr_in FromServer{}; + #if defined(_WIN32) int clientLength = sizeof(FromServer); + #elif defined(__linux__) + socklen_t clientLength = sizeof(FromServer); + #endif ZeroMemory(&FromServer, clientLength); std::string Ret(10240,0); if(UDPSock == -1)return; @@ -53,11 +68,14 @@ void UDPRcv(){ UDPParser(Ret.substr(0,Rcv)); } void UDPClientMain(const std::string& IP,int Port){ + #ifdef _WIN32 WSADATA data; if (WSAStartup(514, &data)){ error("Can't start Winsock!"); return; } + #endif + delete ToServer; ToServer = new sockaddr_in; ToServer->sin_family = AF_INET; diff --git a/src/Network/VehicleEvent.cpp b/src/Network/VehicleEvent.cpp index b35d701..258158e 100755 --- a/src/Network/VehicleEvent.cpp +++ b/src/Network/VehicleEvent.cpp @@ -10,11 +10,20 @@ #include #include "Logger.h" #include -#include #include -#include "Network/network.hpp" +#if defined(_WIN32) +#include +#elif defined(__linux__) +#include +#include +#include +#include +#include +#include +#endif +#include "Network/network.hpp" int LastPort; std::string LastIP; @@ -116,10 +125,12 @@ std::string TCPRcv(SOCKET Sock){ void TCPClientMain(const std::string& IP,int Port){ LastIP = IP; LastPort = Port; - WSADATA wsaData; SOCKADDR_IN ServerAddr; int RetCode; + #ifdef _WIN32 + WSADATA wsaData; WSAStartup(514, &wsaData); //2.2 + #endif TCPSock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); if(TCPSock == -1){ @@ -127,6 +138,7 @@ void TCPClientMain(const std::string& IP,int Port){ WSACleanup(); return; } + ServerAddr.sin_family = AF_INET; ServerAddr.sin_port = htons(Port); inet_pton(AF_INET, IP.c_str(), &ServerAddr.sin_addr); @@ -152,6 +164,9 @@ void TCPClientMain(const std::string& IP,int Port){ if(KillSocket(TCPSock) != 0) debug("(TCP) Cannot close socket. Error code: " + std::to_string(WSAGetLastError())); + #ifdef _WIN32 if(WSACleanup() != 0) debug("(TCP) Client: WSACleanup() failed!..."); + #endif + } diff --git a/src/Security/BeamNG.cpp b/src/Security/BeamNG.cpp index 0d2bef9..f8d664d 100755 --- a/src/Security/BeamNG.cpp +++ b/src/Security/BeamNG.cpp @@ -7,7 +7,14 @@ /// #include +#if defined(_WIN32) #include +#elif defined(__linux__) +#include "vdf_parser.hpp" +#include +#include +#include +#endif #include "Logger.h" #include #include @@ -46,8 +53,13 @@ void SteamExit(int code){ }*/ std::string GetGameDir(){ //if(TraceBack != 4)Exit(0); + #if defined(_WIN32) return GameDir.substr(0,GameDir.find_last_of('\\')); + #elif defined(__linux__) + return GameDir.substr(0,GameDir.find_last_of('/')); + #endif } +#ifdef _WIN32 LONG OpenKey(HKEY root,const char* path,PHKEY hKey){ return RegOpenKeyEx(root, reinterpret_cast(path), 0, KEY_READ, hKey); } @@ -129,6 +141,8 @@ std::string QueryKey(HKEY hKey,int ID){ delete [] buffer; return ""; } +#endif + namespace fs = std::filesystem; bool NameValid(const std::string& N){ @@ -286,6 +300,7 @@ void LegitimacyCheck(){ }else lowExit(2); K2.clear(); RegCloseKey(hKey);*/ + #if defined(_WIN32) std::string Result; std::string K3 = R"(Software\BeamNG\BeamNG.drive)"; HKEY hKey; @@ -301,9 +316,27 @@ void LegitimacyCheck(){ Result.clear(); RegCloseKey(hKey); //if(TraceBack < 3)exit(-1); + #elif defined(__linux__) + struct passwd *pw = getpwuid(getuid()); + std::string homeDir = pw->pw_dir; + // Right now only steam is supported + std::ifstream libraryFolders(homeDir + "/.steam/root/steamapps/libraryfolders.vdf"); + auto root = tyti::vdf::read(libraryFolders); + + for (auto folderInfo: root.childs){ + if (std::filesystem::exists(folderInfo.second->attribs["path"] + "/steamapps/common/BeamNG.drive/")){ + GameDir = folderInfo.second->attribs["path"] + "/steamapps/common/BeamNG.drive/"; + break; + } + } + #endif } std::string CheckVer(const std::string &dir){ + #if defined(_WIN32) std::string temp,Path = dir + "\\integrity.json"; + #elif defined(__linux__) + std::string temp,Path = dir + "/integrity.json"; + #endif std::ifstream f(Path.c_str(), std::ios::binary); int Size = int(std::filesystem::file_size(Path)); std::string vec(Size,0); diff --git a/src/Security/Login.cpp b/src/Security/Login.cpp index 5a58df2..d803598 100755 --- a/src/Security/Login.cpp +++ b/src/Security/Login.cpp @@ -47,7 +47,7 @@ std::string Login(const std::string& fields){ return ""; } info("Attempting to authenticate..."); - std::string Buffer = HTTP::Post("https://auth.beammp.com/userlogin", fields); + std::string Buffer = HTTP::Post("https://auth.beammp.com/userlogin", fields) + "\n"; json::Document d; d.Parse(Buffer.c_str()); if(Buffer == "-1"){ diff --git a/src/Startup.cpp b/src/Startup.cpp index 588139e..c2d3f66 100755 --- a/src/Startup.cpp +++ b/src/Startup.cpp @@ -6,7 +6,12 @@ /// Created by Anonymous275 on 7/16/2020 /// #include "zip_file.h" +#include +#if defined(_WIN32) #include +#elif defined(__linux__) +#include +#endif #include "Discord/discord_info.h" #include "Network/network.hpp" #include "Security/Init.h" @@ -22,9 +27,16 @@ extern int TraceBack; bool Dev = false; namespace fs = std::filesystem; +#if defined(_WIN32) std::string GetEN(){ return "BeamMP-Launcher.exe"; } +#elif defined(__linux__) +std::string GetEN(){ + return "BeamMP-Launcher"; +} +#endif + std::string GetVer(){ return "2.0"; } @@ -39,6 +51,7 @@ std::string GetEP(char*P){ } (); return Ret; } +#if defined(_WIN32) void ReLaunch(int argc,char*args[]){ std::string Arg; for(int c = 2; c <= argc; c++){ @@ -62,8 +75,36 @@ void URelaunch(int argc,char* args[]){ std::this_thread::sleep_for(std::chrono::seconds(1)); exit(1); } +#elif defined(__linux__) +void ReLaunch(int argc,char*args[]){ + std::string Arg; + for(int c = 2; c <= argc; c++){ + Arg += " "; + Arg += args[c-1]; + } + system("clear"); + execl((GetEP() + GetEN()).c_str(), Arg.c_str(), NULL); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); +} +void URelaunch(int argc,char* args[]){ + std::string Arg; + for(int c = 2; c <= argc; c++){ + Arg += " "; + Arg += args[c-1]; + } + execl((GetEP() + GetEN()).c_str(), Arg.c_str(), NULL); + std::this_thread::sleep_for(std::chrono::seconds(1)); + exit(1); +} +#endif + void CheckName(int argc,char* args[]){ + #if defined(_WIN32) std::string DN = GetEN(),CDir = args[0],FN = CDir.substr(CDir.find_last_of('\\')+1); + #elif defined(__linux__) + std::string DN = GetEN(),CDir = args[0],FN = CDir.substr(CDir.find_last_of('/')+1); + #endif if(FN != DN){ if(fs::exists(DN))remove(DN.c_str()); if(fs::exists(DN))ReLaunch(argc,args); @@ -92,7 +133,11 @@ void CheckForUpdates(int argc,char*args[],const std::string& CV){ if(fs::exists(Back))remove(Back.c_str()); if(HTTP > CV){ + #if defined(_WIN32) system("cls"); + #elif defined(__linux__) + system("clear"); + #endif info("Update found!"); info("Updating..."); if(std::rename(EP.c_str(), Back.c_str()))error("failed creating a backup!"); @@ -125,6 +170,7 @@ void CustomPort(int argc, char* argv[]){ } } +#ifdef _WIN32 void LinuxPatch(){ HKEY hKey = nullptr; LONG result = RegOpenKeyEx(HKEY_CURRENT_USER, R"(Software\Wine)", 0, KEY_READ, &hKey); @@ -175,7 +221,9 @@ void LinuxPatch(){ info("Patched!"); } +#endif +#if defined(_WIN32) void InitLauncher(int argc, char* argv[]) { system("cls"); SetConsoleTitleA(("BeamMP Launcher v" + std::string(GetVer()) + GetPatch()).c_str()); @@ -188,6 +236,17 @@ void InitLauncher(int argc, char* argv[]) { Discord_Main(); CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch()); } +#elif defined(__linux__) +void InitLauncher(int argc, char* argv[]) { + system("clear"); + InitLog(); + CheckName(argc, argv); + CheckLocalKey(); + ConfigInit(); + CustomPort(argc, argv); + CheckForUpdates(argc, argv, std::string(GetVer()) + GetPatch()); +} +#endif size_t DirCount(const std::filesystem::path& path){ return (size_t)std::distance(std::filesystem::directory_iterator{path}, std::filesystem::directory_iterator{}); @@ -258,7 +317,12 @@ void PreGame(const std::string& GamePath){ }catch(std::exception&e){ fatal(e.what()); } + #if defined(_WIN32) std::string ZipPath(GetGamePath() + R"(mods\multiplayer\BeamMP.zip)"); + #elif defined(__linux__) + // Linux version of the game cant handle mods with uppercase names + std::string ZipPath(GetGamePath() + R"(mods/multiplayer/beammp.zip)"); + #endif HTTP::Download("https://backend.beammp.com/builds/client?download=true" "&pk=" + PublicKey + "&branch=" + Branch, ZipPath);