From 6f468a2316927e073aaabd8022f6d503ee256d10 Mon Sep 17 00:00:00 2001 From: Daniel Collins Date: Sun, 23 Jun 2024 23:25:13 +0100 Subject: [PATCH] Load main configuration from ipxwrapper.ini (#15). --- Makefile | 16 +-- inih/LICENSE.txt | 27 ++++ inih/ini.c | 304 +++++++++++++++++++++++++++++++++++++++++ inih/ini.h | 178 ++++++++++++++++++++++++ ipxwrapper.ini.example | 39 ++++++ manifest.src.txt | 5 + src/common.c | 52 ++++++- src/common.h | 3 + src/config.c | 128 ++++++++++++++++- src/config.h | 2 +- src/directplay.c | 2 +- src/ipxconfig.cpp | 2 +- src/ipxwrapper.c | 2 +- src/stubdll.c | 2 +- 14 files changed, 746 insertions(+), 16 deletions(-) create mode 100644 inih/LICENSE.txt create mode 100644 inih/ini.c create mode 100644 inih/ini.h create mode 100644 ipxwrapper.ini.example diff --git a/Makefile b/Makefile index 7e0811d..34509d7 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # IPXWrapper - Makefile -# Copyright (C) 2011-2023 Daniel Collins +# Copyright (C) 2011-2024 Daniel Collins # # This program is free software; you can redistribute it and/or modify it # under the terms of the GNU General Public License version 2 as published by @@ -32,8 +32,8 @@ endif INCLUDE := -I./include/ -I./winpcap/include/ -D_WIN32_WINNT=0x0500 -D_WIN32_IE=0x0500 -DHAVE_REMOTE -CFLAGS := -std=c99 -mno-ms-bitfields -Wall $(DBG_OPT) $(INCLUDE) -CXXFLAGS := -std=c++0x -mno-ms-bitfields -Wall $(DBG_OPT) $(INCLUDE) +CFLAGS := -std=c99 -mno-ms-bitfields -Wall -DINI_HANDLER_LINENO=1 $(DBG_OPT) $(INCLUDE) +CXXFLAGS := -std=c++0x -mno-ms-bitfields -Wall -DINI_HANDLER_LINENO=1 $(DBG_OPT) $(INCLUDE) DEPDIR := .d $(shell mkdir -p $(DEPDIR)/src/ $(DEPDIR)/tools/ $(DEPDIR)/tests/tap/) @@ -86,7 +86,7 @@ dist: all IPXWRAPPER_OBJS := src/ipxwrapper.o src/winsock.o src/ipxwrapper_stubs.o src/log.o src/common.o \ src/interface.o src/interface2.o src/router.o src/ipxwrapper.def src/addrcache.o src/config.o src/addr.o \ - src/firewall.o src/ethernet.o src/funcprof.o src/coalesce.o + src/firewall.o src/ethernet.o src/funcprof.o src/coalesce.o inih/ini.o ipxwrapper.dll: $(IPXWRAPPER_OBJS) echo 'const char *version_string = "$(VERSION)", *compile_time = "'`date`'";' | $(CC) -c -x c -o version.o - @@ -99,7 +99,7 @@ src/ipxwrapper_stubs.s: src/ipxwrapper_stubs.txt # WSOCK32.DLL # -wsock32.dll: src/stubdll.o src/wsock32_stubs.o src/log.o src/common.o src/config.o src/addr.o src/funcprof.o src/wsock32.def +wsock32.dll: src/stubdll.o src/wsock32_stubs.o src/log.o src/common.o src/config.o src/addr.o src/funcprof.o inih/ini.o src/wsock32.def $(CC) $(CFLAGS) -Wl,--enable-stdcall-fixup -static-libgcc -shared -o $@ $^ src/wsock32_stubs.s: src/wsock32_stubs.txt @@ -109,7 +109,7 @@ src/wsock32_stubs.s: src/wsock32_stubs.txt # MSWSOCK.DLL # -mswsock.dll: src/stubdll.o src/mswsock_stubs.o src/log.o src/common.o src/config.o src/addr.o src/funcprof.o src/mswsock.def +mswsock.dll: src/stubdll.o src/mswsock_stubs.o src/log.o src/common.o src/config.o src/addr.o src/funcprof.o inih/ini.o src/mswsock.def $(CC) $(CFLAGS) -Wl,--enable-stdcall-fixup -static-libgcc -shared -o $@ $^ src/mswsock_stubs.s: src/mswsock_stubs.txt @@ -119,7 +119,7 @@ src/mswsock_stubs.s: src/mswsock_stubs.txt # DPWSOCKX.DLL # -dpwsockx.dll: src/directplay.o src/log.o src/dpwsockx_stubs.o src/common.o src/config.o src/addr.o src/funcprof.o src/dpwsockx.def +dpwsockx.dll: src/directplay.o src/log.o src/dpwsockx_stubs.o src/common.o src/config.o src/addr.o src/funcprof.o inih/ini.o src/dpwsockx.def $(CC) $(CFLAGS) -Wl,--enable-stdcall-fixup -static-libgcc -shared -o $@ $^ -lwsock32 src/dpwsockx_stubs.s: src/dpwsockx_stubs.txt @@ -130,7 +130,7 @@ src/dpwsockx_stubs.s: src/dpwsockx_stubs.txt # IPXCONFIG_OBJS := src/ipxconfig.o icons/ipxconfig.o src/addr.o src/interface2.o src/common.o \ - src/config.o src/ipxconfig_stubs.o src/funcprof.o + src/config.o src/ipxconfig_stubs.o src/funcprof.o inih/ini.o ipxconfig.exe: $(IPXCONFIG_OBJS) $(CXX) $(CXXFLAGS) -Wl,--enable-stdcall-fixup -static-libgcc -static-libstdc++ -mwindows -o $@ $^ -liphlpapi -lcomctl32 -lws2_32 diff --git a/inih/LICENSE.txt b/inih/LICENSE.txt new file mode 100644 index 0000000..cb7ee2d --- /dev/null +++ b/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/inih/ini.c b/inih/ini.c new file mode 100644 index 0000000..f2f9a6a --- /dev/null +++ b/inih/ini.c @@ -0,0 +1,304 @@ +/* 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; + +/* Strip whitespace chars off end of given string, in place. Return s. */ +static char* ini_rstrip(char* s) +{ + char* p = s + strlen(s); + while (p > s && isspace((unsigned char)(*--p))) + *p = '\0'; + return s; +} + +/* Return pointer to first non-whitespace char in given string. */ +static char* ini_lskip(const char* s) +{ + while (*s && 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* ini_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 = 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* ini_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 = ini_lskip(ini_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 = ini_find_chars_or_comment(start, NULL); + if (*end) + *end = '\0'; + ini_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 = ini_find_chars_or_comment(start + 1, "]"); + if (*end == ']') { + *end = '\0'; + ini_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 = ini_find_chars_or_comment(start, "=:"); + if (*end == '=' || *end == ':') { + *end = '\0'; + name = ini_rstrip(start); + value = end + 1; +#if INI_ALLOW_INLINE_COMMENTS + end = ini_find_chars_or_comment(value, NULL); + if (*end) + *end = '\0'; +#endif + value = ini_lskip(value); + ini_rstrip(value); + + /* Valid name[=:]value pair found, call handler */ + ini_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 = ini_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/inih/ini.h b/inih/ini.h new file mode 100644 index 0000000..d1a2ba8 --- /dev/null +++ b/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/ipxwrapper.ini.example b/ipxwrapper.ini.example new file mode 100644 index 0000000..4a8ed78 --- /dev/null +++ b/ipxwrapper.ini.example @@ -0,0 +1,39 @@ +; Example IPXWrapper configuration file +; +; If this file is named ipxwrapper.ini and placed alongside the application +; executable like ipxwrapper.dll, it will be loaded and any settings in the +; registry created using ipxconfig.exe will be ignored. + +; Uncomment the lines below to use a DOSBox IPX server +; +; dosbox server address = dosbox.example.com +; dosbox server port = 213 + +; Uncomment the line below to enable "packet coalescing" +; +; When packet coalescing is enabled and the application sends large numbers of +; small packets in quick succession, IPXWrapper will batch them up into larger +; packets to reduce packet loss and improve throughput. +; +; NOTE: This requires IPXWrapper 0.7.1 or later on all computers. +; +; coalesce packets = yes + +; Uncomment the line below to automatically create a Windows Firewall exception +; for the application at start-up. +; +; firewall exception = yes + +; Uncomment the line below to disable all logging +; +; logging = none + +; Uncomment the line below to enable debug logging +; (this will slow down most games) +; +; logging = debug + +; Uncomment the line below to enable debug logging with full API tracing +; (slows down games even more!) +; +; logging = trace diff --git a/manifest.src.txt b/manifest.src.txt index 1932896..7d1c737 100644 --- a/manifest.src.txt +++ b/manifest.src.txt @@ -1,4 +1,5 @@ changes.txt +ipxwrapper.ini.example license.txt readme.txt readme.dev.txt @@ -8,6 +9,10 @@ manifest.bin.txt manifest.src.txt mkstubs.pl +inih/LICENSE.txt +inih/ini.c +inih/ini.h + src/addr.c src/addr.h src/addrcache.c diff --git a/src/common.c b/src/common.c index 32a3c4b..7b6c18f 100644 --- a/src/common.c +++ b/src/common.c @@ -1,5 +1,5 @@ /* IPXWrapper - Common functions - * Copyright (C) 2011-2023 Daniel Collins + * Copyright (C) 2011-2024 Daniel Collins * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by @@ -369,3 +369,53 @@ void __stdcall log_call(unsigned int entry, const char *symbol, unsigned int tar { log_printf(LOG_CALL, "%s:%s -> %s", dll_names[entry], symbol, dll_names[target]); } + +wchar_t *get_module_path(HMODULE module) +{ + size_t size = 256; + wchar_t *path = NULL; + + do { + free(path); + + size *= 2; + + if(!(path = (wchar_t*)(malloc(sizeof(wchar_t) * size)))) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + } while(GetModuleFileNameW(module, path, size) == size); + + return path; +} + +wchar_t *get_module_relative_path(HMODULE module, const wchar_t *relative_path) +{ + wchar_t *module_path = get_module_path(module); + if(module_path == NULL) + { + return NULL; + } + + wchar_t *module_path_last_slash = wcsrchr(module_path, '\\'); + + size_t module_path_base_len = module_path_last_slash != NULL + ? (module_path_last_slash - module_path) + 1 + : 0; + + wchar_t *path = malloc((module_path_base_len + wcslen(relative_path) + 1) * sizeof(wchar_t)); + if(path == NULL) + { + free(module_path); + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return NULL; + } + + memcpy(path, module_path, (module_path_base_len * sizeof(wchar_t))); + wcscpy((path + module_path_base_len), relative_path); + + free(module_path); + + return path; +} diff --git a/src/common.h b/src/common.h index 78d56d7..41cb5c8 100644 --- a/src/common.h +++ b/src/common.h @@ -75,6 +75,9 @@ void unload_dlls(void); void __stdcall *find_sym(unsigned int dllnum, const char *symbol); void __stdcall log_call(unsigned int entry, const char *symbol, unsigned int target); +wchar_t *get_module_path(HMODULE module); +wchar_t *get_module_relative_path(HMODULE module, const wchar_t *relative_path); + void log_init(); void log_open(const char *file); void log_close(); diff --git a/src/config.c b/src/config.c index cb2597b..98c8295 100644 --- a/src/config.c +++ b/src/config.c @@ -1,5 +1,5 @@ /* ipxwrapper - Configuration header - * Copyright (C) 2011-2021 Daniel Collins + * Copyright (C) 2011-2024 Daniel Collins * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published by @@ -17,11 +17,15 @@ #include +#include "../inih/ini.h" + #include "config.h" #include "common.h" #include "interface.h" -main_config_t get_main_config(void) +static int process_ini_directive(void *context, const char *section, const char *name, const char *value, int lineno); + +main_config_t get_main_config(bool ignore_ini) { /* Defaults */ @@ -39,6 +43,35 @@ main_config_t get_main_config(void) config.dosbox_server_port = 213; config.dosbox_coalesce = false; + if(!ignore_ini) + { + wchar_t *ini_path = get_module_relative_path(NULL, L"ipxwrapper.ini"); + + FILE *ini_file = _wfopen(ini_path, L"r"); + if(ini_file != NULL) + { + int ini_result = ini_parse_file(ini_file, &process_ini_directive, &config); + + if(ini_result > 0) + { + log_printf(LOG_ERROR, "Parse error in ipxwrapper.ini at line %d", ini_result); + } + + /* Check log_level here because min_log_level isn't initialised yet. */ + if(config.log_level <= LOG_INFO) + { + log_printf(LOG_INFO, "Loaded configuration from %S", ini_path); + } + + fclose(ini_file); + free(ini_path); + + return config; + } + + free(ini_path); + } + HKEY reg = reg_open_main(false); /* Load pre-0.4.x "global" config structure and values. */ @@ -85,6 +118,97 @@ main_config_t get_main_config(void) return config; } +static int process_ini_directive(void *context, const char *section, const char *name, const char *value, int lineno) +{ + main_config_t *config = (main_config_t*)(context); + + if(strcmp(section, "") != 0) + { + log_printf(LOG_ERROR, "Ignoring directive in unknown ipxwrapper.ini section \"%s\"", section); + return 1; + } + + if(strcmp(name, "dosbox server address") == 0) + { + free(config->dosbox_server_addr); + config->dosbox_server_addr = NULL; + + config->dosbox_server_addr = strdup(value); + + if(config->dosbox_server_addr != NULL) + { + config->encap_type = ENCAP_TYPE_DOSBOX; + } + } + else if(strcmp(name, "dosbox server port") == 0) + { + int port = atoi(value); + + if(port >= 1 && port <= 65535) + { + config->dosbox_server_port = port; + } + else{ + log_printf(LOG_ERROR, "Invalid \"dosbox server port\" (%s) specified in ipxwrapper.ini", value); + } + } + else if(strcmp(name, "coalesce packets") == 0) + { + if(strcmp(value, "yes") == 0) + { + config->dosbox_coalesce = true; + } + else if(strcmp(value, "no") == 0) + { + config->dosbox_coalesce = false; + } + else{ + log_printf(LOG_ERROR, "Invalid \"coalesce packets\" (%s) specified in ipxwrapper.ini (expected \"yes\" or \"no\")", value); + } + } + else if(strcmp(name, "firewall exception") == 0) + { + if(strcmp(value, "yes") == 0) + { + config->fw_except = true; + } + else if(strcmp(value, "no") == 0) + { + config->fw_except = false; + } + else{ + log_printf(LOG_ERROR, "Invalid \"firewall exception\" (%s) specified in ipxwrapper.ini (expected \"yes\" or \"no\")", value); + } + } + else if(strcmp(name, "logging") == 0) + { + if(strcmp(value, "none") == 0) + { + config->log_level = LOG_DISABLED; + } + else if(strcmp(value, "info") == 0) + { + config->log_level = LOG_INFO; + } + else if(strcmp(value, "debug") == 0) + { + config->log_level = LOG_DEBUG; + } + else if(strcmp(value, "trace") == 0) + { + config->log_level = LOG_CALL; + } + else{ + log_printf(LOG_ERROR, "Invalid \"logging\" (%s) specified in ipxwrapper.ini (expected \"none\", \"info\", \"debug\" or \"trace\")", value); + } + } + else{ + log_printf(LOG_ERROR, "Unknown directive \"%s\" in ipxwrapper.ini", name); + } + + return 1; +} + bool set_main_config(const main_config_t *config) { HKEY reg = reg_open_main(true); diff --git a/src/config.h b/src/config.h index 6fd8570..247b24f 100644 --- a/src/config.h +++ b/src/config.h @@ -77,7 +77,7 @@ struct v1_iface_config { unsigned char primary; } __attribute__((__packed__)); -main_config_t get_main_config(void); +main_config_t get_main_config(bool ignore_ini); bool set_main_config(const main_config_t *config); iface_config_t get_iface_config(addr48_t hwaddr); diff --git a/src/directplay.c b/src/directplay.c index 6c423b8..59b5453 100644 --- a/src/directplay.c +++ b/src/directplay.c @@ -729,7 +729,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { log_init(); - min_log_level = get_main_config().log_level; + min_log_level = get_main_config(false).log_level; } else if(fdwReason == DLL_PROCESS_DETACH) { diff --git a/src/ipxconfig.cpp b/src/ipxconfig.cpp index 1c2d44d..b65eeec 100644 --- a/src/ipxconfig.cpp +++ b/src/ipxconfig.cpp @@ -97,7 +97,7 @@ static const bool PCAP_INSTALLED = _pcap_installed(); static std::vector nics; -static main_config_t main_config = get_main_config(); +static main_config_t main_config = get_main_config(true); static addr48_t primary_iface = get_primary_iface(); static std::string inv_error; diff --git a/src/ipxwrapper.c b/src/ipxwrapper.c index 8603797..2c50db6 100644 --- a/src/ipxwrapper.c +++ b/src/ipxwrapper.c @@ -129,7 +129,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) log_init(); - main_config = get_main_config(); + main_config = get_main_config(false); min_log_level = main_config.log_level; ipx_encap_type = main_config.encap_type; diff --git a/src/stubdll.c b/src/stubdll.c index d8f7284..8607282 100644 --- a/src/stubdll.c +++ b/src/stubdll.c @@ -35,7 +35,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { log_init(); - main_config_t config = get_main_config(); + main_config_t config = get_main_config(false); min_log_level = config.log_level;