diff --git a/3rdparty/CMakeLists.txt b/3rdparty/CMakeLists.txt index 2abaf2280d9..6d7dd8bb10d 100644 --- a/3rdparty/CMakeLists.txt +++ b/3rdparty/CMakeLists.txt @@ -10,6 +10,21 @@ set_property(TARGET launchinfo PROPERTY FOLDER "3rdparty") add_subdirectory(fmt) set_property(TARGET fmt PROPERTY FOLDER "3rdparty") +# inih library +add_library(inih STATIC "inih/ini.h" "inih/ini.c") +set_property(TARGET inih PROPERTY FOLDER "3rdparty") +target_include_directories(inih PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}") +target_compile_definitions(inih PRIVATE + "-DINI_API=" + "-DINI_ALLOW_MULTILINE=0" + "-DINI_ALLOW_BOM=1" + "-DINI_ALLOW_INLINE_COMMENTS=0" + "-DINI_MAX_LINE=1024" + "-DINI_ALLOW_REALLOC=1" + "-DINI_INITIAL_ALLOC=1024" +) + +# re2 SET(RE2_BUILD_TESTING OFF CACHE BOOL "enable testing for RE2" FORCE) add_subdirectory(re2 EXCLUDE_FROM_ALL) target_include_directories(re2 PUBLIC diff --git a/3rdparty/inih/LICENSE.txt b/3rdparty/inih/LICENSE.txt new file mode 100644 index 00000000000..cb7ee2d017f --- /dev/null +++ b/3rdparty/inih/LICENSE.txt @@ -0,0 +1,27 @@ + +The "inih" library is distributed under the New BSD license: + +Copyright (c) 2009, Ben Hoyt +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Ben Hoyt nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY +EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY +DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/3rdparty/inih/ini.c b/3rdparty/inih/ini.c new file mode 100644 index 00000000000..800df502f44 --- /dev/null +++ b/3rdparty/inih/ini.c @@ -0,0 +1,324 @@ +// WZ Modifications: +// - 2023-11-08: +// Custom fixed_isspace replacement + +/* inih -- simple .INI file parser + +SPDX-License-Identifier: BSD-3-Clause + +Copyright (C) 2009-2020, Ben Hoyt + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS) +#define _CRT_SECURE_NO_WARNINGS +#endif + +#include +#include +#include + +#include "ini.h" + +#if !INI_USE_STACK +#if INI_CUSTOM_ALLOCATOR +#include +void* ini_malloc(size_t size); +void ini_free(void* ptr); +void* ini_realloc(void* ptr, size_t size); +#else +#include +#define ini_malloc malloc +#define ini_free free +#define ini_realloc realloc +#endif +#endif + +#define MAX_SECTION 50 +#define MAX_NAME 50 + +/* Used by ini_parse_string() to keep track of string parsing state. */ +typedef struct { + const char* ptr; + size_t num_left; +} ini_parse_string_ctx; + +int fixed_isspace(int ch) +{ + switch (ch) + { + case ' ': + case '\t': + case '\n': + case '\r': + case '\f': + case '\v': + return 1; + default: + return 0; + } +} + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && fixed_isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* lskip(const char* s) +{ + while (*s && fixed_isspace((unsigned char)(*s))) + s++; + return (char*)s; +} + +/* Return pointer to first char (of chars) or inline comment in given string, + or pointer to NUL at end of string if neither found. Inline comment must + be prefixed by a whitespace character to register as a comment. */ +static char* find_chars_or_comment(const char* s, const char* chars) +{ +#if INI_ALLOW_INLINE_COMMENTS + int was_space = 0; + while (*s && (!chars || !strchr(chars, *s)) && + !(was_space && strchr(INI_INLINE_COMMENT_PREFIXES, *s))) { + was_space = fixed_isspace((unsigned char)(*s)); + s++; + } +#else + while (*s && (!chars || !strchr(chars, *s))) { + s++; + } +#endif + return (char*)s; +} + +/* Similar to strncpy, but ensures dest (size bytes) is + NUL-terminated, and doesn't pad with NULs. */ +static char* strncpy0(char* dest, const char* src, size_t size) +{ + /* Could use strncpy internally, but it causes gcc warnings (see issue #91) */ + size_t i; + for (i = 0; i < size - 1 && src[i]; i++) + dest[i] = src[i]; + dest[i] = '\0'; + return dest; +} + +/* See documentation in header file. */ +int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user) +{ + /* Uses a fair bit of stack (use heap instead if you need to) */ +#if INI_USE_STACK + char line[INI_MAX_LINE]; + size_t max_line = INI_MAX_LINE; +#else + char* line; + size_t max_line = INI_INITIAL_ALLOC; +#endif +#if INI_ALLOW_REALLOC && !INI_USE_STACK + char* new_line; + size_t offset; +#endif + char section[MAX_SECTION] = ""; + char prev_name[MAX_NAME] = ""; + + char* start; + char* end; + char* name; + char* value; + int lineno = 0; + int error = 0; + +#if !INI_USE_STACK + line = (char*)ini_malloc(INI_INITIAL_ALLOC); + if (!line) { + return -2; + } +#endif + +#if INI_HANDLER_LINENO +#define HANDLER(u, s, n, v) handler(u, s, n, v, lineno) +#else +#define HANDLER(u, s, n, v) handler(u, s, n, v) +#endif + + /* Scan through stream line by line */ + while (reader(line, (int)max_line, stream) != NULL) { +#if INI_ALLOW_REALLOC && !INI_USE_STACK + offset = strlen(line); + while (offset == max_line - 1 && line[offset - 1] != '\n') { + max_line *= 2; + if (max_line > INI_MAX_LINE) + max_line = INI_MAX_LINE; + new_line = ini_realloc(line, max_line); + if (!new_line) { + ini_free(line); + return -2; + } + line = new_line; + if (reader(line + offset, (int)(max_line - offset), stream) == NULL) + break; + if (max_line >= INI_MAX_LINE) + break; + offset += strlen(line + offset); + } +#endif + + lineno++; + + start = line; +#if INI_ALLOW_BOM + if (lineno == 1 && (unsigned char)start[0] == 0xEF && + (unsigned char)start[1] == 0xBB && + (unsigned char)start[2] == 0xBF) { + start += 3; + } +#endif + start = lskip(rstrip(start)); + + if (strchr(INI_START_COMMENT_PREFIXES, *start)) { + /* Start-of-line comment */ + } +#if INI_ALLOW_MULTILINE + else if (*prev_name && *start && start > line) { +#if INI_ALLOW_INLINE_COMMENTS + end = find_chars_or_comment(start, NULL); + if (*end) + *end = '\0'; + rstrip(start); +#endif + /* Non-blank line with leading whitespace, treat as continuation + of previous name's value (as per Python configparser). */ + if (!HANDLER(user, section, prev_name, start) && !error) + error = lineno; + } +#endif + else if (*start == '[') { + /* A "[section]" line */ + end = find_chars_or_comment(start + 1, "]"); + if (*end == ']') { + *end = '\0'; + strncpy0(section, start + 1, sizeof(section)); + *prev_name = '\0'; +#if INI_CALL_HANDLER_ON_NEW_SECTION + if (!HANDLER(user, section, NULL, NULL) && !error) + error = lineno; +#endif + } + else if (!error) { + /* No ']' found on section line */ + error = lineno; + } + } + else if (*start) { + /* Not a comment, must be a name[=:]value pair */ + end = find_chars_or_comment(start, "=:"); + if (*end == '=' || *end == ':') { + *end = '\0'; + name = rstrip(start); + value = end + 1; +#if INI_ALLOW_INLINE_COMMENTS + end = find_chars_or_comment(value, NULL); + if (*end) + *end = '\0'; +#endif + value = lskip(value); + rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + strncpy0(prev_name, name, sizeof(prev_name)); + if (!HANDLER(user, section, name, value) && !error) + error = lineno; + } + else if (!error) { + /* No '=' or ':' found on name[=:]value line */ +#if INI_ALLOW_NO_VALUE + *end = '\0'; + name = rstrip(start); + if (!HANDLER(user, section, name, NULL) && !error) + error = lineno; +#else + error = lineno; +#endif + } + } + +#if INI_STOP_ON_FIRST_ERROR + if (error) + break; +#endif + } + +#if !INI_USE_STACK + ini_free(line); +#endif + + return error; +} + +/* See documentation in header file. */ +int ini_parse_file(FILE* file, ini_handler handler, void* user) +{ + return ini_parse_stream((ini_reader)fgets, file, handler, user); +} + +/* See documentation in header file. */ +int ini_parse(const char* filename, ini_handler handler, void* user) +{ + FILE* file; + int error; + + file = fopen(filename, "r"); + if (!file) + return -1; + error = ini_parse_file(file, handler, user); + fclose(file); + return error; +} + +/* An ini_reader function to read the next line from a string buffer. This + is the fgets() equivalent used by ini_parse_string(). */ +static char* ini_reader_string(char* str, int num, void* stream) { + ini_parse_string_ctx* ctx = (ini_parse_string_ctx*)stream; + const char* ctx_ptr = ctx->ptr; + size_t ctx_num_left = ctx->num_left; + char* strp = str; + char c; + + if (ctx_num_left == 0 || num < 2) + return NULL; + + while (num > 1 && ctx_num_left != 0) { + c = *ctx_ptr++; + ctx_num_left--; + *strp++ = c; + if (c == '\n') + break; + num--; + } + + *strp = '\0'; + ctx->ptr = ctx_ptr; + ctx->num_left = ctx_num_left; + return str; +} + +/* See documentation in header file. */ +int ini_parse_string(const char* string, ini_handler handler, void* user) { + ini_parse_string_ctx ctx; + + ctx.ptr = string; + ctx.num_left = strlen(string); + return ini_parse_stream((ini_reader)ini_reader_string, &ctx, handler, + user); +} diff --git a/3rdparty/inih/ini.h b/3rdparty/inih/ini.h new file mode 100644 index 00000000000..d1a2ba825a7 --- /dev/null +++ b/3rdparty/inih/ini.h @@ -0,0 +1,178 @@ +/* inih -- simple .INI file parser + +SPDX-License-Identifier: BSD-3-Clause + +Copyright (C) 2009-2020, Ben Hoyt + +inih is released under the New BSD license (see LICENSE.txt). Go to the project +home page for more info: + +https://github.com/benhoyt/inih + +*/ + +#ifndef INI_H +#define INI_H + +/* Make this header file easier to include in C++ code */ +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/* Nonzero if ini_handler callback should accept lineno parameter. */ +#ifndef INI_HANDLER_LINENO +#define INI_HANDLER_LINENO 0 +#endif + +/* Visibility symbols, required for Windows DLLs */ +#ifndef INI_API +#if defined _WIN32 || defined __CYGWIN__ +# ifdef INI_SHARED_LIB +# ifdef INI_SHARED_LIB_BUILDING +# define INI_API __declspec(dllexport) +# else +# define INI_API __declspec(dllimport) +# endif +# else +# define INI_API +# endif +#else +# if defined(__GNUC__) && __GNUC__ >= 4 +# define INI_API __attribute__ ((visibility ("default"))) +# else +# define INI_API +# endif +#endif +#endif + +/* Typedef for prototype of handler function. */ +#if INI_HANDLER_LINENO +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value, + int lineno); +#else +typedef int (*ini_handler)(void* user, const char* section, + const char* name, const char* value); +#endif + +/* Typedef for prototype of fgets-style reader function. */ +typedef char* (*ini_reader)(char* str, int num, void* stream); + +/* Parse given INI-style file. May have [section]s, name=value pairs + (whitespace stripped), and comments starting with ';' (semicolon). Section + is "" if name=value pair parsed before any section heading. name:value + pairs are also supported as a concession to Python's configparser. + + For each name=value pair parsed, call handler function with given user + pointer as well as section, name, and value (data only valid for duration + of handler call). Handler should return nonzero on success, zero on error. + + Returns 0 on success, line number of first error on parse error (doesn't + stop on first error), -1 on file open error, or -2 on memory allocation + error (only when INI_USE_STACK is zero). +*/ +INI_API int ini_parse(const char* filename, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't + close the file when it's finished -- the caller must do that. */ +INI_API int ini_parse_file(FILE* file, ini_handler handler, void* user); + +/* Same as ini_parse(), but takes an ini_reader function pointer instead of + filename. Used for implementing custom or string-based I/O (see also + ini_parse_string). */ +INI_API int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, + void* user); + +/* Same as ini_parse(), but takes a zero-terminated string with the INI data +instead of a file. Useful for parsing INI data from a network socket or +already in memory. */ +INI_API int ini_parse_string(const char* string, ini_handler handler, void* user); + +/* Nonzero to allow multi-line value parsing, in the style of Python's + configparser. If allowed, ini_parse() will call the handler with the same + name for each subsequent line parsed. */ +#ifndef INI_ALLOW_MULTILINE +#define INI_ALLOW_MULTILINE 1 +#endif + +/* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of + the file. See https://github.com/benhoyt/inih/issues/21 */ +#ifndef INI_ALLOW_BOM +#define INI_ALLOW_BOM 1 +#endif + +/* Chars that begin a start-of-line comment. Per Python configparser, allow + both ; and # comments at the start of a line by default. */ +#ifndef INI_START_COMMENT_PREFIXES +#define INI_START_COMMENT_PREFIXES ";#" +#endif + +/* Nonzero to allow inline comments (with valid inline comment characters + specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match + Python 3.2+ configparser behaviour. */ +#ifndef INI_ALLOW_INLINE_COMMENTS +#define INI_ALLOW_INLINE_COMMENTS 1 +#endif +#ifndef INI_INLINE_COMMENT_PREFIXES +#define INI_INLINE_COMMENT_PREFIXES ";" +#endif + +/* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ +#ifndef INI_USE_STACK +#define INI_USE_STACK 1 +#endif + +/* Maximum line length for any line in INI file (stack or heap). Note that + this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ +#ifndef INI_MAX_LINE +#define INI_MAX_LINE 200 +#endif + +/* Nonzero to allow heap line buffer to grow via realloc(), zero for a + fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is + zero. */ +#ifndef INI_ALLOW_REALLOC +#define INI_ALLOW_REALLOC 0 +#endif + +/* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK + is zero. */ +#ifndef INI_INITIAL_ALLOC +#define INI_INITIAL_ALLOC 200 +#endif + +/* Stop parsing on first error (default is to keep parsing). */ +#ifndef INI_STOP_ON_FIRST_ERROR +#define INI_STOP_ON_FIRST_ERROR 0 +#endif + +/* Nonzero to call the handler at the start of each new section (with + name and value NULL). Default is to only call the handler on + each name=value pair. */ +#ifndef INI_CALL_HANDLER_ON_NEW_SECTION +#define INI_CALL_HANDLER_ON_NEW_SECTION 0 +#endif + +/* Nonzero to allow a name without a value (no '=' or ':' on the line) and + call the handler with value NULL in this case. Default is to treat + no-value lines as an error. */ +#ifndef INI_ALLOW_NO_VALUE +#define INI_ALLOW_NO_VALUE 0 +#endif + +/* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory + allocation functions (INI_USE_STACK must also be 0). These functions must + have the same signatures as malloc/free/realloc and behave in a similar + way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */ +#ifndef INI_CUSTOM_ALLOCATOR +#define INI_CUSTOM_ALLOCATOR 0 +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* INI_H */ diff --git a/3rdparty/mINI/ini.h b/3rdparty/mINI/ini.h deleted file mode 100755 index 5cf4a62ede8..00000000000 --- a/3rdparty/mINI/ini.h +++ /dev/null @@ -1,814 +0,0 @@ -// WZ Modifications: -// - 2020-01-10: -// Adapted to use mINI::INIFileStreamGenerator to generate the file streams -// Fix -Wpedantic warnings - -/* - * The MIT License (MIT) - * Copyright (c) 2018 Danijel Durakovic - * - * 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. - * - */ - -/////////////////////////////////////////////////////////////////////////////// -// -// /mINI/ v0.9.7 -// An INI file reader and writer for the modern age. -// -/////////////////////////////////////////////////////////////////////////////// -// -// A tiny utility library for manipulating INI files with a straightforward -// API and a minimal footprint. It conforms to the (somewhat) standard INI -// format - sections and keys are case insensitive and all leading and -// trailing whitespace is ignored. Comments are lines that begin with a -// semicolon. Trailing comments are allowed on section lines. -// -// Files are read on demand, upon which data is kept in memory and the file -// is closed. This utility supports lazy writing, which only writes changes -// and updates to a file and preserves custom formatting and comments. A lazy -// write invoked by a write() call will read the output file, find what -// changes have been made and update the file accordingly. If you only need to -// generate files, use generate() instead. Section and key order is preserved -// on read, write and insert. -// -/////////////////////////////////////////////////////////////////////////////// -// -// /* BASIC USAGE EXAMPLE: */ -// -// /* read from file */ -// mINI::INIFile file("myfile.ini"); -// mINI::INIStructure ini; -// file.read(ini); -// -// /* read value; gets a reference to actual value in the structure. -// if key or section don't exist, a new empty value will be created */ -// std::string& value = ini["section"]["key"]; -// -// /* read value safely; gets a copy of value in the structure. -// does not alter the structure */ -// std::string value = ini.get("section").get("key"); -// -// /* set or update values */ -// ini["section"]["key"] = "value"; -// -// /* set multiple values */ -// ini["section2"].set({ -// {"key1", "value1"}, -// {"key2", "value2"} -// }); -// -// /* write updates back to file, preserving comments and formatting */ -// file.write(ini); -// -// /* or generate a file (overwrites the original) */ -// file.generate(ini); -// -/////////////////////////////////////////////////////////////////////////////// -// -// Long live the INI file!!! -// -/////////////////////////////////////////////////////////////////////////////// - -#ifndef MINI_INI_H_ -#define MINI_INI_H_ - -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(_MSC_VER) -#include -#endif -#include - -namespace mINI -{ - namespace INIStringUtil - { - const std::string whitespaceDelimiters = " \t\n\r\f\v"; - inline void trim(std::string& str) - { - str.erase(str.find_last_not_of(whitespaceDelimiters) + 1); - str.erase(0, str.find_first_not_of(whitespaceDelimiters)); - } -#ifndef MINI_CASE_SENSITIVE - inline void toLower(std::string& str) - { - std::transform(str.begin(), str.end(), str.begin(), ::tolower); - } -#endif - inline void replace(std::string& str, std::string const& a, std::string const& b) - { - if (!a.empty()) - { - std::size_t pos = 0; - while ((pos = str.find(a, pos)) != std::string::npos) - { - str.replace(pos, a.size(), b); - pos += b.size(); - } - } - } -#ifdef _WIN32 - const std::string endl = "\r\n"; -#else - const std::string endl = "\n"; -#endif - } - - template - class INIMap - { - private: - using T_DataIndexMap = std::unordered_map; - using T_DataItem = std::pair; - using T_DataContainer = std::vector; - using T_MultiArgs = typename std::vector>; - - T_DataIndexMap dataIndexMap; - T_DataContainer data; - - inline std::size_t setEmpty(std::string& key) - { - std::size_t index = data.size(); - dataIndexMap[key] = index; - data.emplace_back(key, T()); - return index; - } - - public: - using const_iterator = typename T_DataContainer::const_iterator; - - INIMap() { } - - INIMap(INIMap const& other) - { - std::size_t data_size = other.data.size(); - for (std::size_t i = 0; i < data_size; ++i) - { - auto const& key = other.data[i].first; - auto const& obj = other.data[i].second; - data.emplace_back(key, obj); - } - dataIndexMap = T_DataIndexMap(other.dataIndexMap); - } - - T& operator[](std::string key) - { - INIStringUtil::trim(key); -#ifndef MINI_CASE_SENSITIVE - INIStringUtil::toLower(key); -#endif - auto it = dataIndexMap.find(key); - bool hasIt = (it != dataIndexMap.end()); - std::size_t index = (hasIt) ? it->second : setEmpty(key); - return data[index].second; - } - T get(std::string key) const - { - INIStringUtil::trim(key); -#ifndef MINI_CASE_SENSITIVE - INIStringUtil::toLower(key); -#endif - auto it = dataIndexMap.find(key); - if (it == dataIndexMap.end()) - { - return T(); - } - return T(data[it->second].second); - } - bool has(std::string key) const - { - INIStringUtil::trim(key); -#ifndef MINI_CASE_SENSITIVE - INIStringUtil::toLower(key); -#endif - return (dataIndexMap.count(key) == 1); - } - void set(std::string key, T obj) - { - INIStringUtil::trim(key); -#ifndef MINI_CASE_SENSITIVE - INIStringUtil::toLower(key); -#endif - auto it = dataIndexMap.find(key); - if (it != dataIndexMap.end()) - { - data[it->second].second = obj; - } - else - { - dataIndexMap[key] = data.size(); - data.emplace_back(key, obj); - } - } - void set(T_MultiArgs const& multiArgs) - { - for (auto const& it : multiArgs) - { - auto const& key = it.first; - auto const& obj = it.second; - set(key, obj); - } - } - bool remove(std::string key) - { - INIStringUtil::trim(key); -#ifndef MINI_CASE_SENSITIVE - INIStringUtil::toLower(key); -#endif - auto it = dataIndexMap.find(key); - if (it != dataIndexMap.end()) - { - std::size_t index = it->second; - data.erase(data.begin() + index); - dataIndexMap.erase(it); - for (auto& it2 : dataIndexMap) - { - auto& vi = it2.second; - if (vi > index) - { - vi--; - } - } - return true; - } - return false; - } - void clear() - { - data.clear(); - dataIndexMap.clear(); - } - std::size_t size() const - { - return data.size(); - } - const_iterator begin() const { return data.begin(); } - const_iterator end() const { return data.end(); } - }; - - using INIStructure = INIMap>; - - namespace INIParser - { - using T_ParseValues = std::pair; - - enum class PDataType : char - { - PDATA_NONE, - PDATA_COMMENT, - PDATA_SECTION, - PDATA_KEYVALUE, - PDATA_UNKNOWN - }; - - inline PDataType parseLine(std::string line, T_ParseValues& parseData) - { - parseData.first.clear(); - parseData.second.clear(); - INIStringUtil::trim(line); - if (line.empty()) - { - return PDataType::PDATA_NONE; - } - char firstCharacter = line[0]; - if (firstCharacter == ';') - { - return PDataType::PDATA_COMMENT; - } - if (firstCharacter == '[') - { - auto commentAt = line.find_first_of(';'); - if (commentAt != std::string::npos) - { - line = line.substr(0, commentAt); - } - auto closingBracketAt = line.find_last_of(']'); - if (closingBracketAt != std::string::npos) - { - auto section = line.substr(1, closingBracketAt - 1); - INIStringUtil::trim(section); - parseData.first = section; - return PDataType::PDATA_SECTION; - } - } - auto lineNorm = line; - INIStringUtil::replace(lineNorm, "\\=", " "); - auto equalsAt = lineNorm.find_first_of('='); - if (equalsAt != std::string::npos) - { - auto key = line.substr(0, equalsAt); - INIStringUtil::trim(key); - INIStringUtil::replace(key, "\\=", "="); - auto value = line.substr(equalsAt + 1); - INIStringUtil::trim(value); - parseData.first = key; - parseData.second = value; - return PDataType::PDATA_KEYVALUE; - } - return PDataType::PDATA_UNKNOWN; - } - } - - class INIFileStreamGenerator - { - protected: - std::string utf8Path; - public: - INIFileStreamGenerator(std::string const& utf8Path) - : utf8Path(utf8Path) - { } - virtual ~INIFileStreamGenerator() { } - public: - virtual std::shared_ptr getFileReadStream() const - { - if (utf8Path.empty()) - { - return nullptr; - } - return std::make_shared(utf8Path, std::ios::in | std::ios::binary); - } - virtual std::shared_ptr getFileWriteStream() const - { - if (utf8Path.empty()) - { - return nullptr; - } - return std::make_shared(utf8Path, std::ios::out | std::ios::binary); - } - virtual bool fileExists() const - { - if (utf8Path.empty()) - { - return false; - } -#if defined(_MSC_VER) - struct _stat buf; - bool fileExists = (_stat(utf8Path.c_str(), &buf) == 0); -#else - struct stat buf; - bool fileExists = (stat(utf8Path.c_str(), &buf) == 0); -#endif - return fileExists; - } - }; - - class INIReader - { - public: - using T_LineData = std::vector; - using T_LineDataPtr = std::shared_ptr; - - private: - std::shared_ptr fileReadStream; - T_LineDataPtr lineData; - - T_LineData readFile() - { - std::string fileContents; - fileReadStream->seekg(0, std::ios::end); - fileContents.resize(fileReadStream->tellg()); - fileReadStream->seekg(0, std::ios::beg); - std::size_t fileSize = fileContents.size(); - fileReadStream->read(&fileContents[0], fileSize); - fileReadStream.reset(); - T_LineData output; - if (fileSize == 0) - { - return output; - } - std::string buffer; - buffer.reserve(50); - for (std::size_t i = 0; i < fileSize; ++i) - { - char& c = fileContents[i]; - if (c == '\n') - { - output.emplace_back(buffer); - buffer.clear(); - continue; - } - if (c != '\0' && c != '\r') - { - buffer += c; - } - } - output.emplace_back(buffer); - return output; - } - - public: - INIReader(std::shared_ptr const& streamGenerator, bool keepLineData = false) - { - fileReadStream = streamGenerator->getFileReadStream(); - if (keepLineData) - { - lineData = std::make_shared(); - } - } - ~INIReader() { } - - bool operator>>(INIStructure& data) - { - if (!fileReadStream) - { - return false; - } - T_LineData fileLines = readFile(); - std::string section; - bool inSection = false; - INIParser::T_ParseValues parseData; - for (auto const& line : fileLines) - { - auto parseResult = INIParser::parseLine(line, parseData); - if (parseResult == INIParser::PDataType::PDATA_SECTION) - { - inSection = true; - data[section = parseData.first]; - } - else if (inSection && parseResult == INIParser::PDataType::PDATA_KEYVALUE) - { - auto const& key = parseData.first; - auto const& value = parseData.second; - data[section][key] = value; - } - if (lineData && parseResult != INIParser::PDataType::PDATA_UNKNOWN) - { - if (parseResult == INIParser::PDataType::PDATA_KEYVALUE && !inSection) - { - continue; - } - lineData->emplace_back(line); - } - } - return true; - } - T_LineDataPtr getLines() - { - return lineData; - } - }; - - class INIGenerator - { - private: - std::shared_ptr fileWriteStream; - - public: - bool prettyPrint = false; - - INIGenerator(std::shared_ptr const& streamGenerator) - { - fileWriteStream = streamGenerator->getFileWriteStream(); - } - ~INIGenerator() { } - - bool operator<<(INIStructure const& data) - { - if (fileWriteStream == nullptr) - { - return false; - } - if (!data.size()) - { - return true; - } - auto it = data.begin(); - for (;;) - { - auto const& section = it->first; - auto const& collection = it->second; - (*fileWriteStream) - << "[" - << section - << "]"; - if (collection.size()) - { - (*fileWriteStream) << INIStringUtil::endl; - auto it2 = collection.begin(); - for (;;) - { - auto key = it2->first; - INIStringUtil::replace(key, "=", "\\="); - auto value = it2->second; - INIStringUtil::trim(value); - (*fileWriteStream) - << key - << ((prettyPrint) ? " = " : "=") - << value; - if (++it2 == collection.end()) - { - break; - } - (*fileWriteStream) << INIStringUtil::endl; - } - } - if (++it == data.end()) - { - break; - } - (*fileWriteStream) << INIStringUtil::endl; - if (prettyPrint) - { - (*fileWriteStream) << INIStringUtil::endl; - } - } - return true; - } - }; - - class INIWriter - { - private: - using T_LineData = std::vector; - using T_LineDataPtr = std::shared_ptr; - - std::shared_ptr streamGenerator; - - T_LineData getLazyOutput(T_LineDataPtr const& lineData, INIStructure& data, INIStructure& original) - { - T_LineData output; - INIParser::T_ParseValues parseData; - std::string sectionCurrent; - bool parsingSection = false; - bool continueToNextSection = false; - bool discardNextEmpty = false; - bool writeNewKeys = false; - std::size_t lastKeyLine = 0; - for (auto line = lineData->begin(); line != lineData->end(); ++line) - { - if (!writeNewKeys) - { - auto parseResult = INIParser::parseLine(*line, parseData); - if (parseResult == INIParser::PDataType::PDATA_SECTION) - { - if (parsingSection) - { - writeNewKeys = true; - parsingSection = false; - --line; - continue; - } - sectionCurrent = parseData.first; - if (data.has(sectionCurrent)) - { - parsingSection = true; - continueToNextSection = false; - discardNextEmpty = false; - output.emplace_back(*line); - lastKeyLine = output.size(); - } - else - { - continueToNextSection = true; - discardNextEmpty = true; - continue; - } - } - else if (parseResult == INIParser::PDataType::PDATA_KEYVALUE) - { - if (continueToNextSection) - { - continue; - } - if (data.has(sectionCurrent)) - { - auto& collection = data[sectionCurrent]; - auto const& key = parseData.first; - auto const& value = parseData.second; - if (collection.has(key)) - { - auto outputValue = collection[key]; - if (value == outputValue) - { - output.emplace_back(*line); - } - else - { - INIStringUtil::trim(outputValue); - auto lineNorm = *line; - INIStringUtil::replace(lineNorm, "\\=", " "); - auto equalsAt = lineNorm.find_first_of('='); - auto valueAt = lineNorm.find_first_not_of( - INIStringUtil::whitespaceDelimiters, - equalsAt + 1 - ); - std::string outputLine = line->substr(0, valueAt); - if (prettyPrint && equalsAt + 1 == valueAt) - { - outputLine += " "; - } - outputLine += outputValue; - output.emplace_back(outputLine); - } - lastKeyLine = output.size(); - } - } - } - else - { - if (discardNextEmpty && line->empty()) - { - discardNextEmpty = false; - } - else if (parseResult != INIParser::PDataType::PDATA_UNKNOWN) - { - output.emplace_back(*line); - } - } - } - if (writeNewKeys || std::next(line) == lineData->end()) - { - T_LineData linesToAdd; - if (data.has(sectionCurrent) && original.has(sectionCurrent)) - { - auto const& collection = data[sectionCurrent]; - auto const& collectionOriginal = original[sectionCurrent]; - for (auto const& it : collection) - { - auto key = it.first; - if (collectionOriginal.has(key)) - { - continue; - } - auto value = it.second; - INIStringUtil::replace(key, "=", "\\="); - INIStringUtil::trim(value); - linesToAdd.emplace_back( - key + ((prettyPrint) ? " = " : "=") + value - ); - } - } - if (!linesToAdd.empty()) - { - output.insert( - output.begin() + lastKeyLine, - linesToAdd.begin(), - linesToAdd.end() - ); - } - if (writeNewKeys) - { - writeNewKeys = false; - --line; - } - } - } - for (auto const& it : data) - { - auto const& section = it.first; - if (original.has(section)) - { - continue; - } - if (prettyPrint && output.size() > 0 && !output.back().empty()) - { - output.emplace_back(); - } - output.emplace_back("[" + section + "]"); - auto const& collection = it.second; - for (auto const& it2 : collection) - { - auto key = it2.first; - auto value = it2.second; - INIStringUtil::replace(key, "=", "\\="); - INIStringUtil::trim(value); - output.emplace_back( - key + ((prettyPrint) ? " = " : "=") + value - ); - } - } - return output; - } - - public: - bool prettyPrint = false; - - INIWriter(std::shared_ptr const& streamGenerator) - : streamGenerator(streamGenerator) - { - } - ~INIWriter() { } - - bool operator<<(INIStructure& data) - { - bool fileExists = streamGenerator->fileExists(); - if (!fileExists) - { - INIGenerator generator(streamGenerator); - generator.prettyPrint = prettyPrint; - return generator << data; - } - INIStructure originalData; - T_LineDataPtr lineData; - bool readSuccess = false; - { - INIReader reader(streamGenerator, true); - if ((readSuccess = reader >> originalData)) - { - lineData = reader.getLines(); - } - } - if (!readSuccess) - { - return false; - } - T_LineData output = getLazyOutput(lineData, data, originalData); - - std::shared_ptr fileWriteStream = streamGenerator->getFileWriteStream(); - if (fileWriteStream != nullptr) - { - if (output.size()) - { - auto line = output.begin(); - for (;;) - { - (*fileWriteStream) << *line; - if (++line == output.end()) - { - break; - } - (*fileWriteStream) << INIStringUtil::endl; - } - } - return true; - } - return false; - } - }; - - class INIFile - { - private: - std::shared_ptr fileStreamGenerator; - - public: -// // Note: This will *not* properly handle Unicode paths on Windows (and possibly other systems) -// INIFile(std::string const& filename) -// { -// fileStreamGenerator = std::make_shared(filename); -// } - INIFile(std::shared_ptr const& fileStreamGenerator) - : fileStreamGenerator(fileStreamGenerator) - { } - - ~INIFile() { } - - bool read(INIStructure& data) const - { - if (data.size()) - { - data.clear(); - } - if (fileStreamGenerator == nullptr) - { - return false; - } - INIReader reader(fileStreamGenerator); - return reader >> data; - } - bool generate(INIStructure const& data, bool pretty = false) const - { - if (fileStreamGenerator == nullptr) - { - return false; - } - INIGenerator generator(fileStreamGenerator); - generator.prettyPrint = pretty; - return generator << data; - } - bool write(INIStructure& data, bool pretty = false) const - { - if (fileStreamGenerator == nullptr) - { - return false; - } - INIWriter writer(fileStreamGenerator); - writer.prettyPrint = pretty; - return writer << data; - } - }; -} - -#endif // MINI_INI_H_ diff --git a/COPYING.NONGPL b/COPYING.NONGPL index 1b1eaaac8ae..f53686f5ad3 100644 --- a/COPYING.NONGPL +++ b/COPYING.NONGPL @@ -21,8 +21,8 @@ data/base/texpages/page-25-sky-urban.png - BSD, Copyright (c) 2014, Kenneth MacKay (https://github.com/kmackay/micro-ecc) 3rdparty/sha/* - BSD, Copyright (c) 1998-2010, Brian Gladman, Worcester, UK. (https://github.com/BrianGladman/sha) -3rdparty/mINI/* - - MIT, Copyright (c) 2018 Danijel Durakovic (https://github.com/pulzed/mINI) +3rdparty/inih/* + - BSD, Copyright (C) 2009-2020, Ben Hoyt (https://github.com/benhoyt/inih) 3rdparty/miniupnp/* - BSD, Copyright (c) 2005-2017, Thomas BERNARD (http://miniupnp.free.fr) 3rdparty/optional-lite/* diff --git a/pkg/copyright b/pkg/copyright index c105f7f2cce..416651aa49f 100644 --- a/pkg/copyright +++ b/pkg/copyright @@ -155,9 +155,9 @@ Files: 3rdparty/LRUCache11/* Copyright: 2012-2022 SAURAV MOHAPATRA License: ISC -Files: 3rdparty/mINI/* -Copyright: 2018 Danijel Durakovic -License: Expat +Files: 3rdparty/inih/* +Copyright: 2009-2020 Ben Hoyt +License: BSD-3-clause-inih Files: 3rdparty/miniupnpc/* Copyright: 2005-2019, Thomas BERNARD @@ -369,6 +369,29 @@ License: BSD-3-clause-etcpak (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +License: BSD-3-clause-inih + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Ben Hoyt nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + . + THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY + EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY + DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND + ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + License: BSD-3-clause-miniupnpc Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: diff --git a/src/3rdparty/INIReaderWriter.cpp b/src/3rdparty/INIReaderWriter.cpp new file mode 100644 index 00000000000..542166fee27 --- /dev/null +++ b/src/3rdparty/INIReaderWriter.cpp @@ -0,0 +1,228 @@ +// Read/write an INI file into easy-to-access name/value pairs. + +// SPDX-License-Identifier: BSD-3-Clause + +// Originally based on: IniReader (https://github.com/benhoyt/inih) + +// Copyright (C) 2009-2020, Ben Hoyt +// Copyright (C) 2023 Warzone 2100 Project + +#include +#include +#include +#ifndef INI_API +# define INI_API +#endif +#include +#include "INIReaderWriter.h" + +using std::string; + +namespace INIDetail +{ + inline unsigned char ascii_tolower(unsigned char c) + { + if ('A' <= c && c <= 'Z') + { + c += 'a' - 'A'; + } + return c; + } + + int ascii_strcasecmp(const char* s1, const char* s2) + { + const unsigned char *us1 = (const unsigned char *)s1; + const unsigned char *us2 = (const unsigned char *)s2; + + while (ascii_tolower(*us1) == ascii_tolower(*us2++)) + if (*us1++ == '\0') + return 0; + + return ascii_tolower(*us1) - ascii_tolower(*--us2); + } +} + +INIReaderWriter::INIReaderWriter(const string& filename) +{ + _error = ini_parse(filename.c_str(), ValueHandler, this); + _loaded = true; +} + +INIReaderWriter::INIReaderWriter(const char *buffer, size_t buffer_size) +{ + string content(buffer, buffer_size); + _error = ini_parse_string(content.c_str(), ValueHandler, this); + _loaded = true; +} + +INIReaderWriter::INIReaderWriter() +{ + _error = 0; + _loaded = false; +} + +int INIReaderWriter::ParseError() const +{ + return _error; +} + +bool INIReaderWriter::LoadedFromData() const +{ + return _loaded; +} + +void INIReaderWriter::IniSection::dumpTo(std::string& outputStr, std::string endl /*= "\n"*/) const +{ + for (const auto& i : _values) + { + outputStr += i.first + "=" + i.second + endl; + } +} + +bool INIReaderWriter::IniSection::HasValue(const std::string& name) const +{ + auto it = _values.find(name); + return it != _values.end(); +} + +string INIReaderWriter::IniSection::Get(const string& name, const string& default_value) const +{ + auto it = _values.find(name); + if (it == _values.end()) + { + return default_value; + } + return it->second; +} + +string INIReaderWriter::IniSection::GetString(const string& name, const string& default_value) const +{ + const string str = Get(name, ""); + return str.empty() ? default_value : str; +} + +long INIReaderWriter::IniSection::GetInteger(const string& name, long default_value) const +{ + string valstr = Get(name, ""); + const char* value = valstr.c_str(); + char* end; + // This parses "1234" (decimal) and also "0x4D2" (hex) + long n = strtol(value, &end, 0); + return end > value ? n : default_value; +} + +int64_t INIReaderWriter::IniSection::GetInteger64(const std::string& name, int64_t default_value) const +{ + string valstr = Get(name, ""); + const char* value = valstr.c_str(); + char* end; + // This parses "1234" (decimal) and also "0x4D2" (hex) + int64_t n = strtoll(value, &end, 0); + return end > value ? n : default_value; +} + +unsigned long INIReaderWriter::IniSection::GetUnsigned(const string& name, unsigned long default_value) const +{ + string valstr = Get(name, ""); + const char* value = valstr.c_str(); + char* end; + // This parses "1234" (decimal) and also "0x4D2" (hex) + unsigned long n = strtoul(value, &end, 0); + return end > value ? n : default_value; +} + +uint64_t INIReaderWriter::IniSection::GetUnsigned64(const std::string& name, uint64_t default_value) const +{ + string valstr = Get(name, ""); + const char* value = valstr.c_str(); + char* end; + // This parses "1234" (decimal) and also "0x4D2" (hex) + uint64_t n = strtoull(value, &end, 0); + return end > value ? n : default_value; +} + +double INIReaderWriter::IniSection::GetReal(const string& name, double default_value) const +{ + string valstr = Get(name, ""); + const char* value = valstr.c_str(); + char* end; + double n = strtod(value, &end); + return end > value ? n : default_value; +} + +bool INIReaderWriter::IniSection::GetBoolean(const string& name, bool default_value) const +{ + string valstr = Get(name, ""); + // Convert to lower case to make string comparisons case-insensitive + std::transform(valstr.begin(), valstr.end(), valstr.begin(), + [](const unsigned char& ch) { return static_cast(::tolower(ch)); }); + if (valstr == "true" || valstr == "yes" || valstr == "on" || valstr == "1") + return true; + else if (valstr == "false" || valstr == "no" || valstr == "off" || valstr == "0") + return false; + else + return default_value; +} + +// Set a string value in a section +void INIReaderWriter::IniSection::SetString(const std::string& name, const std::string& value) +{ + _values[name] = value; +} + +void INIReaderWriter::IniSection::SetBool(const string& name, bool value) +{ + SetString(name, (value) ? "true" : "false"); +} + +INIReaderWriter::IniSection& INIReaderWriter::GetSection(const std::string& section) +{ + return _sections[section]; +} + +bool INIReaderWriter::HasSection(const string& section) const +{ + return _sections.find(section) != _sections.end(); +} + +bool INIReaderWriter::HasValue(const string& section, const string& name) const +{ + auto it = _sections.find(section); + if (it == _sections.end()) + { + return false; + } + return it->second.HasValue(name); +} + +int INIReaderWriter::ValueHandler(void* user, const char* section, const char* name, + const char* value) +{ + if (!name) // Happens when INI_CALL_HANDLER_ON_NEW_SECTION enabled + return 1; + INIReaderWriter* reader = static_cast(user); + auto& iniSection = reader->_sections[(section) ? section : ""]; + iniSection.SetString((name) ? name : "", value ? value : ""); + return 1; +} + +// Dump the current ini state to a string (ex. for output to a file) +std::string INIReaderWriter::dump() const +{ +#ifdef _WIN32 + const std::string endl = "\r\n"; +#else + const std::string endl = "\n"; +#endif + + std::string result; + result.reserve(2048); + + for (const auto& section : _sections) + { + result += std::string("[") + section.first + "]" + endl; + section.second.dumpTo(result, endl); + } + + return result; +} diff --git a/src/3rdparty/INIReaderWriter.h b/src/3rdparty/INIReaderWriter.h new file mode 100644 index 00000000000..62b63939857 --- /dev/null +++ b/src/3rdparty/INIReaderWriter.h @@ -0,0 +1,143 @@ +// Read / write an INI file into easy-to-access name/value pairs. + +// SPDX-License-Identifier: BSD-3-Clause + +// Originally based on: IniReader (https://github.com/benhoyt/inih) + +// Copyright (C) 2009-2020, Ben Hoyt +// Copyright (C) 2023 Warzone 2100 Project + +#ifndef INIREADER_H +#define INIREADER_H + +#include +#include +#include + +namespace INIDetail +{ + int ascii_strcasecmp(const char* s1, const char* s2); + + struct CaseInsensitiveComparator + { + bool operator()(const std::string& a, const std::string& b) const noexcept + { + return INIDetail::ascii_strcasecmp(a.c_str(), b.c_str()) < 0; + } + }; +} + +class INIReaderWriter +{ +public: + class IniSection + { + public: + bool HasValue(const std::string& name) const; + + public: // GETTERS + // Get a string value from INI file, returning default_value if not found. + std::string Get(const std::string& name, + const std::string& default_value) const; + + // Get a string value from INI file, returning default_value if not found, + // empty, or contains only whitespace. + std::string GetString(const std::string& name, + const std::string& default_value) const; + + // Get an integer (long) value from INI file, returning default_value if + // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). + long GetInteger(const std::string& name, long default_value) const; + + // Get a 64-bit integer (int64_t) value from INI file, returning default_value if + // not found or not a valid integer (decimal "1234", "-1234", or hex "0x4d2"). + int64_t GetInteger64(const std::string& name, int64_t default_value) const; + + // Get an unsigned integer (unsigned long) value from INI file, returning default_value if + // not found or not a valid unsigned integer (decimal "1234", or hex "0x4d2"). + unsigned long GetUnsigned(const std::string& name, unsigned long default_value) const; + + // Get an unsigned 64-bit integer (uint64_t) value from INI file, returning default_value if + // not found or not a valid unsigned integer (decimal "1234", or hex "0x4d2"). + uint64_t GetUnsigned64(const std::string& name, uint64_t default_value) const; + + // Get a real (floating point double) value from INI file, returning + // default_value if not found or not a valid floating point value + // according to strtod(). + double GetReal(const std::string& name, double default_value) const; + + // Get a boolean value from INI file, returning default_value if not found or if + // not a valid true/false value. Valid true values are "true", "yes", "on", "1", + // and valid false values are "false", "no", "off", "0" (not case sensitive). + bool GetBoolean(const std::string& name, bool default_value) const; + + public: // SETTERS // TODO: implement + + // Set a string value in a section + void SetString(const std::string& name, const std::string& value); + + void SetBool(const std::string& name, bool value); + + public: + + // old WZ compat functions + inline bool has(const std::string& name) const { return HasValue(name); } + + public: + + void dumpTo(std::string& outputStr, std::string endl = "\n") const; + + private: + + typedef std::map ValuesMap; + ValuesMap _values; + }; +public: + // Construct INIReaderWriter and parse given filename. See ini.h for more info + // about the parsing. + explicit INIReaderWriter(const std::string& filename); + + // Construct INIReaderWriter and parse given buffer. See ini.h for more info + // about the parsing. + explicit INIReaderWriter(const char *buffer, size_t buffer_size); + + // Construct a blank IniReaderWriter (usually for writing) + INIReaderWriter(); + + // Return the result of ini_parse(), i.e., 0 on success, line number of + // first error on parse error, or -1 on file open error. + int ParseError() const; + + // Whether this IniReaderWriter was initialized from loaded data (returns true), or a fresh blank instance (returns false) + bool LoadedFromData() const; + + IniSection& GetSection(const std::string& section); + + // Return true if the given section exists (section must contain at least + // one name=value pair). + bool HasSection(const std::string& section) const; + + // Return true if a value exists with the given section and field names. + bool HasValue(const std::string& section, const std::string& name) const; + + // Dump the current ini state to a string (ex. for output to a file) + std::string dump() const; + +private: + int _error; + bool _loaded = false; + + struct CaseInsensitiveComparator + { + bool operator()(const std::string& a, const std::string& b) const noexcept + { + return INIDetail::ascii_strcasecmp(a.c_str(), b.c_str()) < 0; + } + }; + + std::map _sections; + static int ValueHandler(void* user, const char* section, const char* name, + const char* value); +}; + +#endif // INIREADER_H diff --git a/src/3rdparty/physfs.hpp b/src/3rdparty/physfs.hpp deleted file mode 100755 index 2d475c0afb1..00000000000 --- a/src/3rdparty/physfs.hpp +++ /dev/null @@ -1,280 +0,0 @@ -// A heavily modified / reduced version of physfs-hpp -// Original: https://github.com/Ybalrid/physfs-hpp/blob/master/include/physfs.hpp -// -// WZ Modifications: -// - Reduced down to just the filestream classes, restructured, uses WZ PHYSFS wrappers -// - Workaround to fix -Wswitch warning -// -// Copyright (c) 2021 Warzone 2100 Project -// Licensed under the same terms as the original license (below). -// -// Original License: -// -// Copyright (c) 2013 Kevin Howell and others. -// Copyright (c) 2018 Arthur Brainville (Ybalrid) -// -// This software is provided 'as-is', without any express or implied warranty. -// In no event will the authors be held liable for any damages arising from -// the use of this software. -// -// Permission is granted to anyone to use this software for any purpose, -// including commercial applications, and to alter it and redistribute it -// freely, subject to the following restrictions: -// -// 1. The origin of this software must not be misrepresented; you must not -// claim that you wrote the original software. If you use this software in a -// product, an acknowledgment in the product documentation would be -// appreciated but is not required. -// -// 2. Altered source versions must be plainly marked as such, and must not be -// misrepresented as being the original software. -// -// 3. This notice may not be removed or altered from any source distribution. -// -// Arthur Brainville (Ybalrid) -// - -#ifndef _INCLUDE_PHYSFS_HPP_ -#define _INCLUDE_PHYSFS_HPP_ - -#include -#include -#include - -namespace PhysFS { - - typedef enum { - READ, - WRITE, - APPEND - } mode; - - class base_fstream { - protected: - PHYSFS_File * const file; - public: - base_fstream(PHYSFS_File * file); - virtual ~base_fstream(); - size_t length(); - }; - - class ifstream : public base_fstream, public std::istream { - protected: - ifstream(PHYSFS_File * file); - public: - static std::shared_ptr make(std::string const & filename); - virtual ~ifstream(); - }; - - class ofstream : public base_fstream, public std::ostream { - protected: - ofstream(PHYSFS_File * file); - public: - static std::shared_ptr make(std::string const & filename, mode writeMode = WRITE); - virtual ~ofstream(); - }; - - class fstream : public base_fstream, public std::iostream { - protected: - fstream(PHYSFS_File * file); - public: - static std::shared_ptr make(std::string const & filename, mode openMode = READ); - virtual ~fstream(); - }; -} - -#ifdef PHYFSPP_IMPL - -#include -#include -#include -#include -#include "lib/framework/physfs_ext.h" - -using std::streambuf; -using std::ios_base; - -namespace PhysFS { - -class fbuf : public streambuf { -private: - fbuf(const fbuf & other); - fbuf& operator=(const fbuf& other); - - int_type underflow() { - if (PHYSFS_eof(file)) { - return traits_type::eof(); - } - size_t bytesRead = WZ_PHYSFS_readBytes(file, buffer, bufferSize); - if (bytesRead < 1) { - return traits_type::eof(); - } - setg(buffer, buffer, buffer + bytesRead); - return (unsigned char) *gptr(); - } - - pos_type seekoff(off_type pos, ios_base::seekdir dir, ios_base::openmode mode) { - if (dir == std::ios_base::beg) - { - if (PHYSFS_seek(file, pos) == 0) - { - return pos_type(-1); - } - } - else if (dir == std::ios_base::cur) - { - // subtract characters currently in buffer from seek position - if (PHYSFS_seek(file, (PHYSFS_tell(file) + pos) - (egptr() - gptr())) == 0) - { - return pos_type(-1); - } - } - else if (dir == std::ios_base::end) - { - if (PHYSFS_seek(file, PHYSFS_fileLength(file) + pos) == 0) - { - return pos_type(-1); - } - } - else - { - throw std::invalid_argument("invalid seekdir value"); - } - - if (mode & std::ios_base::in) { - setg(egptr(), egptr(), egptr()); - } - if (mode & std::ios_base::out) { - setp(buffer, buffer); - } - return PHYSFS_tell(file); - } - - pos_type seekpos(pos_type pos, std::ios_base::openmode mode) { - if (PHYSFS_seek(file, pos) == 0) - { - return pos_type(-1); - } - if (mode & std::ios_base::in) { - setg(egptr(), egptr(), egptr()); - } - if (mode & std::ios_base::out) { - setp(buffer, buffer); - } - return PHYSFS_tell(file); - } - - int_type overflow( int_type c = traits_type::eof() ) { - if (pptr() == pbase() && c == traits_type::eof()) { - return 0; // no-op - } - if (WZ_PHYSFS_writeBytes(file, pbase(), pptr() - pbase()) < 1) { - return traits_type::eof(); - } - if (c != traits_type::eof()) { - if (WZ_PHYSFS_writeBytes(file, &c, 1) < 1) { - return traits_type::eof(); - } - } - - return 0; - } - - int sync() { - return overflow(); - } - - char * buffer; - size_t const bufferSize; -protected: - PHYSFS_File * const file; -public: - fbuf(PHYSFS_File * file, std::size_t bufferSize = 2048) : bufferSize(bufferSize), file(file) { - buffer = new char[bufferSize]; - char * end = buffer + bufferSize; - setg(end, end, end); - setp(buffer, end); - } - - ~fbuf() { - sync(); - delete [] buffer; - } -}; - -base_fstream::base_fstream(PHYSFS_File* file) : file(file) { - if (file == nullptr) { - throw std::invalid_argument("attempted to construct fstream with NULL ptr"); - } -} - -base_fstream::~base_fstream() { - PHYSFS_close(file); -} - -size_t base_fstream::length() { - return PHYSFS_fileLength(file); -} - -PHYSFS_File* openWithMode(char const * filename, mode openMode) { - PHYSFS_File* file = nullptr; - switch (openMode) { - case WRITE: - file = PHYSFS_openWrite(filename); - break; - case APPEND: - file = PHYSFS_openAppend(filename); - break; - case READ: - file = PHYSFS_openRead(filename); - } - return file; -} - -ifstream::ifstream(PHYSFS_File * file) - : base_fstream(file), std::istream(new fbuf(file)) {} - -std::shared_ptr ifstream::make(std::string const & filename) -{ - PHYSFS_File* file = openWithMode(filename.c_str(), READ); - if (!file) { return nullptr; } - return std::shared_ptr(new ifstream(file)); -} - -ifstream::~ifstream() { - delete rdbuf(); -} - -ofstream::ofstream(PHYSFS_File * file) - : base_fstream(file), std::ostream(new fbuf(file)) {} - -std::shared_ptr ofstream::make(std::string const & filename, mode writeMode) -{ - PHYSFS_File* file = openWithMode(filename.c_str(), writeMode); - if (!file) { return nullptr; } - return std::shared_ptr(new ofstream(file)); -} - -ofstream::~ofstream() { - delete rdbuf(); -} - -fstream::fstream(PHYSFS_File * file) - : base_fstream(file), std::iostream(new fbuf(file)) {} - -std::shared_ptr fstream::make(std::string const & filename, mode openMode) -{ - PHYSFS_File* file = openWithMode(filename.c_str(), openMode); - if (!file) { return nullptr; } - return std::shared_ptr(new fstream(file)); -} - -fstream::~fstream() { - delete rdbuf(); -} - -} // namespace PhysFS - -#endif - -#endif /* _INCLUDE_PHYSFS_HPP_ */ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cb0a37b755f..4fe629bc320 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -114,6 +114,7 @@ endif() target_link_libraries(warzone2100 nlohmann_json) target_link_libraries(warzone2100 optional-lite) target_link_libraries(warzone2100 quickjs) +target_link_libraries(warzone2100 inih) include(IncludeFindCurl) target_link_libraries(warzone2100 CURL::libcurl) diff --git a/src/configuration.cpp b/src/configuration.cpp index 079e9840db4..4b372132274 100644 --- a/src/configuration.cpp +++ b/src/configuration.cpp @@ -25,6 +25,7 @@ #include "lib/framework/wzconfig.h" #include "lib/framework/input.h" +#include "lib/framework/file.h" #include "lib/framework/physfs_ext.h" #include "lib/netplay/netplay.h" #include "lib/sound/mixer.h" @@ -59,9 +60,7 @@ #include -#include "mINI/ini.h" -#define PHYFSPP_IMPL -#include "3rdparty/physfs.hpp" +#include "3rdparty/INIReaderWriter.h" // //////////////////////////////////////////////////////////////////////////// @@ -74,94 +73,46 @@ static const char *fileName = "config"; // //////////////////////////////////////////////////////////////////////////// -// PhysFS implementation of mINI::INIFileStreamGenerator +// PhysFS helpers -class PhysFSFileStreamGenerator : public mINI::INIFileStreamGenerator +static inline std::string WZ_PHYSFS_getRealPath(const char *filename) { -public: - PhysFSFileStreamGenerator(std::string const& utf8Path) - : INIFileStreamGenerator(utf8Path) - { } - virtual ~PhysFSFileStreamGenerator() { } -public: - virtual std::shared_ptr getFileReadStream() const override - { - if (utf8Path.empty()) - { - return nullptr; - } - try { - return std::static_pointer_cast(PhysFS::ifstream::make(utf8Path)); - } - catch (const std::exception&) - { - // file likely does not exist - return nullptr; - } - } - virtual std::shared_ptr getFileWriteStream() const override + std::string fullPath = WZ_PHYSFS_getRealDir_String(filename); + if (fullPath.empty()) { return fullPath; } + fullPath += PHYSFS_getDirSeparator(); + fullPath += filename; + return fullPath; +} + +static inline optional WZ_PHYSFS_getFileSize(const char *filename) +{ +#if defined(WZ_PHYSFS_2_1_OR_GREATER) + PHYSFS_Stat metaData = {}; + if (PHYSFS_stat(filename, &metaData) == 0) { - if (utf8Path.empty()) - { - return nullptr; - } - try { - return std::static_pointer_cast(PhysFS::ofstream::make(utf8Path)); - } - catch (const std::exception&) - { - // file likely does not exist - return nullptr; - } + return nullopt; // failed to get file info } - virtual bool fileExists() const override + if (metaData.filesize < 0) { - if (utf8Path.empty()) - { - return false; - } - return PHYSFS_exists(utf8Path.c_str()); + // unknown filesize + return nullopt; } - optional fileSize() const - { -#if defined(WZ_PHYSFS_2_1_OR_GREATER) - PHYSFS_Stat metaData; - if (PHYSFS_stat(utf8Path.c_str(), &metaData) == 0) - { - return nullopt; // failed to get file info - } - if (metaData.filesize < 0) - { - // unknown filesize - return nullopt; - } - return static_cast(metaData.filesize); + return static_cast(metaData.filesize); #else - return nullopt; // unknown + return nullopt; #endif - } - std::string realPath() const - { - std::string fullPath = WZ_PHYSFS_getRealDir_String(utf8Path.c_str()); - if (fullPath.empty()) { return fullPath; } - fullPath += PHYSFS_getDirSeparator(); - fullPath += utf8Path; - return fullPath; - } -}; +} // //////////////////////////////////////////////////////////////////////////// -typedef mINI::INIMap IniSection; - -static optional iniSectionGetInteger(const IniSection& iniSection, const std::string& key, optional defaultValue = nullopt) +static optional iniSectionGetInteger(const INIReaderWriter::IniSection& iniSection, const std::string& key, optional defaultValue = nullopt) { - if (!iniSection.has(key)) + if (!iniSection.HasValue(key)) { return defaultValue; } try { - auto valueStr = iniSection.get(key); + auto valueStr = iniSection.Get(key, ""); int valueInt = std::stoi(valueStr); return valueInt; } @@ -172,18 +123,18 @@ static optional iniSectionGetInteger(const IniSection& iniSection, const st } } -static void iniSectionSetInteger(IniSection& iniSection, const std::string& key, int value) +static void iniSectionSetInteger(INIReaderWriter::IniSection& iniSection, const std::string& key, int value) { - iniSection[key] = std::to_string(value); + iniSection.SetString(key, std::to_string(value)); } -static optional iniSectionGetBool(const IniSection& iniSection, const std::string& key, optional defaultValue = nullopt) +static optional iniSectionGetBool(const INIReaderWriter::IniSection& iniSection, const std::string& key, optional defaultValue = nullopt) { - if (!iniSection.has(key)) + if (!iniSection.HasValue(key)) { return defaultValue; } - auto valueStr = WzString::fromUtf8(iniSection.get(key)).toLower(); + auto valueStr = WzString::fromUtf8(iniSection.Get(key, "")).toLower(); // first check if it's equal to "true" or "false" (case-insensitive) if (valueStr == "true") { @@ -211,18 +162,18 @@ static optional iniSectionGetBool(const IniSection& iniSection, const std: return defaultValue; } -static void iniSectionSetBool(IniSection& iniSection, const std::string& key, bool value) +static void iniSectionSetBool(INIReaderWriter::IniSection& iniSection, const std::string& key, bool value) { - iniSection[key] = (value) ? "true" : "false"; + iniSection.SetString(key, (value) ? "true" : "false"); } -static optional iniSectionGetString(const IniSection& iniSection, const std::string& key, optional defaultValue = nullopt) +static optional iniSectionGetString(const INIReaderWriter::IniSection& iniSection, const std::string& key, optional defaultValue = nullopt) { - if (!iniSection.has(key)) + if (!iniSection.HasValue(key)) { return defaultValue; } - std::string result = iniSection.get(key); + std::string result = iniSection.Get(key, ""); // To support prior INI files written by QSettings, strip surrounding "" if present if (!result.empty() && result.front() == '"' && result.back() == '"') { @@ -235,20 +186,13 @@ static optional iniSectionGetString(const IniSection& iniSection, c return result; } -bool saveIniFile(mINI::INIFile &file, mINI::INIStructure &ini) +bool saveIniFile(const char* outputPath, const INIReaderWriter& ini) { // write out ini file changes - try - { - if (!file.write(ini)) - { - debug(LOG_INFO, "Could not write configuration file \"%s\"", fileName); - return false; - } - } - catch (const std::exception& e) + std::string iniOutput = ini.dump(); + if (!saveFile(outputPath, iniOutput.c_str(), iniOutput.size())) { - debug(LOG_ERROR, "Ini write failed with exception: %s", e.what()); + debug(LOG_ERROR, "Ini write failed"); return false; } return true; @@ -256,43 +200,83 @@ bool saveIniFile(mINI::INIFile &file, mINI::INIStructure &ini) constexpr uint64_t MAX_CONFIG_FILE_SIZE = 1024 * 1024 * 2; // 2 MB seems like enough... -// //////////////////////////////////////////////////////////////////////////// -bool loadConfig() +static INIReaderWriter loadConfigIniFile(const char* inputFile) { - // first, create a file instance - auto fileStreamGenerator = std::make_shared(fileName); - mINI::INIFile file(fileStreamGenerator); + uint64_t fileStatSize = WZ_PHYSFS_getFileSize(inputFile).value_or(0); + if (fileStatSize > MAX_CONFIG_FILE_SIZE) + { + debug(LOG_ERROR, "Could not read existing configuration file \"%s\"; filesize (%" PRIu64 ") exceeds max", inputFile, fileStatSize); + return INIReaderWriter(); + } + + // load in the existing ini file + PHYSFS_File* file = PHYSFS_openRead(inputFile); + if (!file) + { + debug(LOG_WZ, "Could not read existing configuration file \"%s\"", inputFile); + return INIReaderWriter(); + } - // next, create a structure that will hold data - mINI::INIStructure ini; - bool createdConfigFile = false; + debug(LOG_WZ, "Reading configuration from: %s", WZ_PHYSFS_getRealPath(inputFile).c_str()); - // now we can read the file - try + // get file size from the open file handle + PHYSFS_sint64 filesize = PHYSFS_fileLength(file); + if (filesize < 0) { - uint64_t fileSize = fileStreamGenerator->fileSize().value_or(0); - if (fileSize > MAX_CONFIG_FILE_SIZE) - { - createdConfigFile = true; - debug(LOG_ERROR, "Could not read existing configuration file \"%s\"; filesize (%" PRIu64 ") exceeds max", fileName, fileSize); - // will just proceed with an empty ini structure - } - if (!createdConfigFile && !file.read(ini)) - { - createdConfigFile = true; - debug(LOG_WZ, "Could not read existing configuration file \"%s\"", fileName); - // will just proceed with an empty ini structure - } + // File size could not be determined. Is a directory? + debug(LOG_INFO, "Could not read existing configuration file \"%s\"; filesize returned (%" PRIi64 ")", inputFile, static_cast(filesize)); + return INIReaderWriter(); } - catch (const std::exception& e) + if (static_cast(filesize) > MAX_CONFIG_FILE_SIZE) { - createdConfigFile = true; - debug(LOG_ERROR, "Ini read failed with exception: %s", e.what()); - ini.clear(); - // will just proceed with an empty ini structure + debug(LOG_ERROR, "Could not read existing configuration file \"%s\"; filesize (%" PRIu64 ") exceeds max", inputFile, static_cast(filesize)); + return INIReaderWriter(); } + ASSERT_OR_RETURN(INIReaderWriter(), filesize < static_cast(std::numeric_limits::max()), "\"%s\" filesize >= std::numeric_limits::max()", inputFile); + ASSERT_OR_RETURN(INIReaderWriter(), static_cast(filesize) < static_cast(std::numeric_limits::max()), "\"%s\" filesize >= std::numeric_limits::max()", inputFile); + + std::vector outputBuffer; + outputBuffer.clear(); + outputBuffer.resize(static_cast(filesize + 1)); - auto& iniGeneral = ini["General"]; + /* Load the file data */ + PHYSFS_sint64 length_read = WZ_PHYSFS_readBytes(file, outputBuffer.data(), static_cast(filesize)); + if (length_read != filesize) + { + outputBuffer.clear(); + + debug(LOG_ERROR, "Reading %s short: %s", inputFile, WZ_PHYSFS_getLastError()); + return INIReaderWriter(); + } + + if (!PHYSFS_close(file)) + { + outputBuffer.clear(); + debug(LOG_ERROR, "Error closing %s: %s", inputFile, WZ_PHYSFS_getLastError()); + // but continue on... + } + + // append null + outputBuffer[outputBuffer.size() - 1] = 0; + + // load IniReaderWriter from the buffer + auto result = INIReaderWriter(outputBuffer.data(), outputBuffer.size() - 1); + if (result.ParseError() != 0) + { + debug(LOG_ERROR, "Could not read existing configuration file \"%s\"; error parsing line: %d", inputFile, result.ParseError()); + return INIReaderWriter(); + } + return result; +} + + +// //////////////////////////////////////////////////////////////////////////// +bool loadConfig() +{ + auto ini = loadConfigIniFile(fileName); + bool createdConfigFile = !ini.LoadedFromData(); + + auto& iniGeneral = ini.GetSection("General"); auto iniGetInteger = [&iniGeneral](const std::string& key, optional defaultValue) -> optional { return iniSectionGetInteger(iniGeneral, key, defaultValue); @@ -333,7 +317,6 @@ bool loadConfig() ActivityManager::instance().beginLoadingSettings(); - debug(LOG_WZ, "Reading configuration from: %s", fileStreamGenerator->realPath().c_str()); if (auto value = iniGetIntegerOpt("voicevol")) { sound_SetUIVolume(static_cast(value.value()) / 100.0f); @@ -375,7 +358,7 @@ bool loadConfig() } if (iniGeneral.has("language")) { - setLanguage(iniGeneral.get("language").c_str()); + setLanguage(iniGetString("language", "").value().c_str()); } if (auto value = iniGetBoolOpt("nomousewarp")) { @@ -623,36 +606,7 @@ bool loadConfig() // //////////////////////////////////////////////////////////////////////////// bool saveConfig() { - // first, create a file instance - auto fileStreamGenerator = std::make_shared(fileName); - mINI::INIFile file(fileStreamGenerator); - - // next, create a structure that will hold data - mINI::INIStructure ini; - - // read in the current file - try - { - uint64_t fileSize = fileStreamGenerator->fileSize().value_or(0); - bool skipLoadExisting = false; - if (fileSize > MAX_CONFIG_FILE_SIZE) - { - skipLoadExisting = true; - debug(LOG_ERROR, "Could not read existing configuration file \"%s\"; filesize (%" PRIu64 ") exceeds max", fileName, fileSize); - // will just proceed with an empty ini structure - } - if (!skipLoadExisting && !file.read(ini)) - { - debug(LOG_WZ, "Could not read existing configuration file \"%s\"", fileName); - // will just proceed with an empty ini structure - } - } - catch (const std::exception& e) - { - debug(LOG_ERROR, "Ini read failed with exception: %s", e.what()); - ini.clear(); - // will just proceed with an empty ini structure - } + auto ini = loadConfigIniFile(fileName); std::string fullConfigFilePath; if (PHYSFS_getWriteDir()) @@ -663,7 +617,7 @@ bool saveConfig() fullConfigFilePath += fileName; debug(LOG_WZ, "Writing configuration to: \"%s\"", fullConfigFilePath.c_str()); - auto& iniGeneral = ini["General"]; + auto& iniGeneral = ini.GetSection("General"); auto iniSetInteger = [&iniGeneral](const std::string& key, int value) { iniSectionSetInteger(iniGeneral, key, value); @@ -672,7 +626,7 @@ bool saveConfig() iniSectionSetBool(iniGeneral, key, value); }; auto iniSetString = [&iniGeneral](const std::string& key, const std::string& value) { - iniGeneral[key] = value; + iniGeneral.SetString(key, value); }; auto iniSetFromCString = [&iniGeneral](const std::string& key, const char* value, size_t maxLength) { std::string strVal; @@ -682,7 +636,7 @@ bool saveConfig() ASSERT(len < maxLength, "Input c-string value (for key: %s) appears to be missing null-terminator?", key.c_str()); strVal.assign(value, len); } - iniGeneral[key] = strVal; + iniGeneral.SetString(key, strVal); }; // ////////////////////////// @@ -807,32 +761,13 @@ bool saveConfig() iniSetInteger("configVersion", CURRCONFVERSION); // write out ini file changes - bool result = saveIniFile(file, ini); + bool result = saveIniFile(fileName, ini); return result; } bool saveGfxConfig() { - // first, create a file instance - mINI::INIFile file(std::make_shared(fileName)); - - // next, create a structure that will hold data - mINI::INIStructure ini; - - // read in the current file - try - { - if (!file.read(ini)) - { - debug(LOG_WZ, "Could not read existing configuration file \"%s\"", fileName); - // will just proceed with an empty ini structure - } - } - catch (const std::exception& e) - { - debug(LOG_ERROR, "Ini read failed with exception: %s", e.what()); - return false; - } + auto ini = loadConfigIniFile(fileName); std::string fullConfigFilePath; if (PHYSFS_getWriteDir()) @@ -843,17 +778,17 @@ bool saveGfxConfig() fullConfigFilePath += fileName; debug(LOG_WZ, "Writing gfx configuration to: \"%s\"", fullConfigFilePath.c_str()); - auto& iniGeneral = ini["General"]; + auto& iniGeneral = ini.GetSection("General"); auto iniSetString = [&iniGeneral](const std::string& key, const std::string& value) { - iniGeneral[key] = value; + iniGeneral.SetString(key, value); }; // only change the gfx entry iniSetString("gfxbackend", to_string(war_getGfxBackend())); // write out ini file changes - bool result = saveIniFile(file, ini); + bool result = saveIniFile(fileName, ini); return result; } @@ -861,29 +796,26 @@ bool saveGfxConfig() // Ensures that others' games don't change our own configuration settings bool reloadMPConfig() { - // first, create a file instance - mINI::INIFile file(std::make_shared(fileName)); - - // next, create a structure that will hold data - mINI::INIStructure ini; - - // now we can read the file - try + auto ini = loadConfigIniFile(fileName); + if (!ini.LoadedFromData()) { - if (!file.read(ini)) - { - debug(LOG_INFO, "Could not read existing configuration file \"%s\"", fileName); - } - } - catch (const std::exception& e) - { - debug(LOG_ERROR, "Ini read failed with exception: %s", e.what()); - return false; + debug(LOG_INFO, "Could not read existing configuration file \"%s\"", fileName); } - auto& iniGeneral = ini["General"]; + auto& iniGeneral = ini.GetSection("General"); + + debug(LOG_WZ, "Reloading mp config"); - debug(LOG_WZ, "Reloading prefs prefs to registry"); + auto iniSetFromCString = [&iniGeneral](const std::string& key, const char* value, size_t maxLength) { + std::string strVal; + if (value) + { + size_t len = strnlen(value, maxLength); + ASSERT(len < maxLength, "Input c-string value (for key: %s) appears to be missing null-terminator?", key.c_str()); + strVal.assign(value, len); + } + iniGeneral.SetString(key, strVal); + }; // If we're in-game, we already have our own configuration set, so no need to reload it. if (NetPlay.isHost && !ingame.localJoiningInProgress) @@ -905,7 +837,7 @@ bool reloadMPConfig() { if (bMultiPlayer && NetPlay.bComms) { - iniGeneral["gameName"] = std::string(game.name); // last hosted game + iniSetFromCString("gameName", game.name, 128); // last hosted game } else { @@ -927,7 +859,7 @@ bool reloadMPConfig() iniSectionSetInteger(iniGeneral, "alliance", game.alliance); // allow alliances // write out ini file changes - bool result = saveIniFile(file, ini); + bool result = saveIniFile(fileName, ini); return result; }