From 3094b571d58661d595a9853ebb094ef42185ef1c Mon Sep 17 00:00:00 2001 From: TheoryOfNekomata Date: Sat, 25 Jun 2022 14:53:10 +0800 Subject: [PATCH] Update config file parsing Unify config loading in net and video subsystems. --- src/packages/game/IZ_app.c | 57 ++++-- src/packages/game/IZ_config.c | 235 ++++++++++++++++++++++ src/packages/game/IZ_config.h | 25 ++- src/packages/game/net/IZ_net.c | 108 +++++----- src/packages/game/output/video/IZ_video.c | 83 +++++--- src/packages/game/output/video/IZ_video.h | 2 +- src/packages/server/IZ_app.c | 60 +++--- 7 files changed, 441 insertions(+), 129 deletions(-) diff --git a/src/packages/game/IZ_app.c b/src/packages/game/IZ_app.c index 3088ca8..517a141 100644 --- a/src/packages/game/IZ_app.c +++ b/src/packages/game/IZ_app.c @@ -65,29 +65,44 @@ void IZ_AppTeardown(struct IZ_App* app) { SDL_Quit(); } +void IZ_AppPrintHelpOptions() { + printf("\n"); + printf("Options:\n"); + printf("\n"); + printf( + "\n" + "Options:\n" + "\n" + " -c Specifies the path to the config file. (default: \"./config-game.ini\")\n" + " -f Specifies the frames per second. (default: 30)\n" + " -h Displays this help screen.\n" + " -i Specifies the interval of sending packets (default: 200)\n" + " in milliseconds.\n" + + ); +} + +void IZ_AppPrintHelpUsage() { + printf("\n"); + printf("Usage:"); + printf(" %s [options]\n", "game.exe"); +} + +void IZ_AppPrintHelpHeader() { + printf("\n"); + printf("%s - %s\n", IZ_APP_NAME, IZ_APP_DESCRIPTION); +} + +void IZ_AppPrintHelp() { + IZ_AppPrintHelpHeader(); + IZ_AppPrintHelpUsage(); + IZ_AppPrintHelpOptions(); +} + IZ_ProcedureResult IZ_AppRun(struct IZ_App* app, u8 argc, const char* argv[]) { + // TODO have a config subsystem that handles these. if (IZ_ConfigGetCommandlineOption(argc, argv, "-h")) { - printf( - ( - "\n" - "%s - %s\n" - "\n" - "Usage:\n" - "\n" - " %s [options]\n" - "\n" - "Options:\n" - "\n" - " -c Specifies the path to the config file. (default: \"./config-game.ini\")\n" - " -f Specifies the frames per second. (default: 30)\n" - " -h Displays this help file.\n" - " -i Specifies the interval of sending packets (default: 200)\n" - " in milliseconds.\n" - ), - IZ_APP_NAME, - IZ_APP_DESCRIPTION, - "game.exe" - ); + IZ_AppPrintHelp(); return IZ_APP_RUN_RESULT_OK; } diff --git a/src/packages/game/IZ_config.c b/src/packages/game/IZ_config.c index b40ef31..89f4d6d 100644 --- a/src/packages/game/IZ_config.c +++ b/src/packages/game/IZ_config.c @@ -33,3 +33,238 @@ const char* IZ_ConfigGetCommandlineOption(u8 argc, const char* argv[], const cha return NULL; } + +typedef bool IZ_ConfigLoadParamsStringValidator(const char*); + +typedef bool IZ_ConfigLoadParamsU16Validator(u16); + +typedef bool IZ_ConfigLoadParamsU8Validator(u8); + +void IZ_ConfigEnsureValidString(IZ_ConfigItem item, char* buffer) { + char dest[item.dest_size]; + if (item.validator) { + IZ_ConfigLoadParamsStringValidator* validator = item.validator; + if (validator(buffer)) { + memcpy_s(dest, item.dest_size, buffer, item.dest_size); + item.dest = &dest; + return; + } + memcpy_s(dest, item.dest_size, item.default_value, item.dest_size); + item.dest = &dest; + return; + } + memcpy_s(dest, item.dest_size, buffer, item.dest_size); + item.dest = &dest; +} + +void IZ_ConfigLoadString(IZ_ConfigItem item, const char* config_path) { + char buffer[item.dest_size]; + ini_gets(item.section, item.key, item.default_value, buffer, item.dest_size, config_path); + IZ_ConfigEnsureValidString(item, buffer); +} + +void IZ_ConfigEnsureValidU8(IZ_ConfigItem item, u8 raw_value, u8 default_value) { + if (item.validator) { + IZ_ConfigLoadParamsU8Validator* validator = item.validator; + if (validator(raw_value)) { + item.dest = &raw_value; + return; + } + item.dest = &default_value; + return; + } + item.dest = &raw_value; +} + +void IZ_ConfigLoadU8(IZ_ConfigItem item, const char* config_path) { + static u8 raw_value; + static u8 default_value; + default_value = *((u8*) item.default_value); + raw_value = ini_getl(item.section, item.key, default_value, config_path); + IZ_ConfigEnsureValidU8(item, raw_value, default_value); +} + +void IZ_ConfigEnsureValidU16(IZ_ConfigItem item, u16 raw_value, u16 default_value) { + if (item.validator) { + IZ_ConfigLoadParamsU16Validator* validator = item.validator; + if (validator(raw_value)) { + item.dest = &raw_value; + return; + } + item.dest = &default_value; + return; + } + item.dest = &raw_value; +} + +void IZ_ConfigLoadU16(IZ_ConfigItem item, const char* config_path) { + static u16 raw_value; + static u16 default_value; + default_value = *((u16*) item.default_value); + raw_value = ini_getl(item.section, item.key, default_value, config_path); + IZ_ConfigEnsureValidU16(item, raw_value, default_value); +} + +void IZ_ConfigLoad(IZ_ConfigItem item[], const char* config_path) { + u8 i; + for (i = 0; item[i].type != IZ_CONFIG_TYPE_VOID; i += 1) { + switch (item[i].type) { + case IZ_CONFIG_TYPE_STRING: + IZ_ConfigLoadString(item[i], config_path); + break; + case IZ_CONFIG_TYPE_U8: + IZ_ConfigLoadU8(item[i], config_path); + break; + case IZ_CONFIG_TYPE_U16: + IZ_ConfigLoadU16(item[i], config_path); + break; + default: + break; + } + } +} + +IZ_ProcedureResult IZ_ConfigSaveU8(IZ_ConfigItem item, const char* config_path) { + u8 dest = *((u8*) item.dest); + if (item.validator) { + IZ_ConfigLoadParamsU8Validator* validator = item.validator; + if (!validator(dest)) { + dest = *((const u8*) item.default_value); + } + } + + if (!ini_putl(item.section, item.key, dest, config_path)) { + return -1; + } + + return 0; +} + +IZ_ProcedureResult IZ_ConfigSaveU16(IZ_ConfigItem item, const char* config_path) { + u16 dest = *((u16*) item.dest); + if (item.validator) { + IZ_ConfigLoadParamsU8Validator* validator = item.validator; + if (!validator(dest)) { + dest = *((const u16*) item.default_value); + } + } + + if (!ini_putl(item.section, item.key, dest, config_path)) { + return -1; + } + + return 0; +} + +IZ_ProcedureResult IZ_ConfigSaveString(IZ_ConfigItem item, const char* config_path) { + const char* dest = (const char*) item.dest; + if (item.validator) { + IZ_ConfigLoadParamsStringValidator* validator = item.validator; + if (!validator(dest)) { + dest = (const char*) item.default_value; + } + } + + if (!ini_puts(item.section, item.key, dest, config_path)) { + return -1; + } + + return 0; +} + +IZ_ProcedureResult IZ_ConfigSave(IZ_ConfigItem item[], const char* config_path) { + u8 i; + u16 problems = 0; + for (i = 0; item[i].type != IZ_CONFIG_TYPE_VOID; i += 1) { + switch (item[i].type) { + case IZ_CONFIG_TYPE_STRING: + IZ_ConfigSaveString(item[i], config_path); + break; + case IZ_CONFIG_TYPE_U8: + IZ_ConfigSaveU8(item[i], config_path); + break; + case IZ_CONFIG_TYPE_U16: + IZ_ConfigSaveU16(item[i], config_path); + break; + default: + break; + } + } + + return -problems; +} + +void IZ_ConfigOverrideU8(IZ_ConfigItem item, u8 argc, const char* argv[]) { + if (!item.cmdline_option) { + return; + } + const char* cmdline_buffer; + char* rest_of_string; + static u8 dest; + static u8 default_value; + default_value = *((u16*) item.default_value); + if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, item.cmdline_option))) { + dest = strtol(cmdline_buffer, &rest_of_string, 10); + if (strcmp(cmdline_buffer, rest_of_string) != 0) { + IZ_ConfigEnsureValidU8(item, dest, default_value); + return; + } + item.dest = &default_value; + } +} + +void IZ_ConfigOverrideU16(IZ_ConfigItem item, u8 argc, const char* argv[]) { + if (!item.cmdline_option) { + return; + } + const char* cmdline_buffer; + char* rest_of_string; + static u16 dest; + static u16 default_value; + default_value = *((u16*) item.default_value); + if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, item.cmdline_option))) { + dest = strtol(cmdline_buffer, &rest_of_string, 10); + if (strcmp(cmdline_buffer, rest_of_string) != 0) { + IZ_ConfigEnsureValidU16(item, dest, default_value); + return; + } + item.dest = &default_value; + } +} + +void IZ_ConfigOverrideString(IZ_ConfigItem item, u8 argc, const char* argv[]) { + if (!item.cmdline_option) { + return; + } + const char* cmdline_buffer; + if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, item.cmdline_option))) { + IZ_ConfigEnsureValidString(item, cmdline_buffer); + } +} + +void IZ_ConfigOverride(IZ_ConfigItem item[], u8 argc, const char* argv[]) { + u8 i; + for (i = 0; item[i].type != IZ_CONFIG_TYPE_VOID; i += 1) { + switch (item[i].type) { + case IZ_CONFIG_TYPE_U8: + IZ_ConfigOverrideU8(item[i], argc, argv); + break; + case IZ_CONFIG_TYPE_U16: + IZ_ConfigOverrideU16(item[i], argc, argv); + break; + case IZ_CONFIG_TYPE_STRING: + IZ_ConfigOverrideString(item[i], argc, argv); + break; + default: + break; + } + } +} + +void IZ_ConfigInit(IZ_ConfigItem item[], const char* config_path, u8 argc, const char* argv[]) { + IZ_ConfigLoad(item, config_path); + if (IZ_ConfigSave(item, config_path)) { + // TODO log errors in saving + } + IZ_ConfigOverride(item, argc, argv); +} diff --git a/src/packages/game/IZ_config.h b/src/packages/game/IZ_config.h index 605c1f1..68aa0d8 100644 --- a/src/packages/game/IZ_config.h +++ b/src/packages/game/IZ_config.h @@ -3,11 +3,34 @@ #include #include +#include +#include #include "IZ_common.h" -// TODO unify loading of config from cmdline and config file +typedef enum { + IZ_CONFIG_TYPE_VOID, + IZ_CONFIG_TYPE_STRING, + IZ_CONFIG_TYPE_U8, + IZ_CONFIG_TYPE_U16 +} IZ_ConfigType; + +typedef struct { + IZ_ConfigType type; + size_t dest_size; + const char* section; + const char* key; + const char* cmdline_option; + const void* default_value; + void* validator; + void* dest; +} IZ_ConfigItem; + void IZ_ConfigGetDefaultPath(const char*, size_t); const char* IZ_ConfigGetCommandlineOption(u8, const char**, const char*); +void IZ_ConfigInit(IZ_ConfigItem[], const char*, u8, const char**); + +IZ_ProcedureResult IZ_ConfigSave(IZ_ConfigItem[], const char*); + #endif diff --git a/src/packages/game/net/IZ_net.c b/src/packages/game/net/IZ_net.c index ed10f54..a9b8651 100644 --- a/src/packages/game/net/IZ_net.c +++ b/src/packages/game/net/IZ_net.c @@ -1,57 +1,66 @@ #include "IZ_net.h" -void IZ_NetLoadConfig(IZ_NetState* state, const char* config_path) { - char buffer[32]; - - ini_gets("Network", "Username", IZ_NET_DEFAULT_STATE.config.username, buffer, 32, config_path); - memcpy_s(state->config.username, 32, buffer, 32); - - state->config.packet_interval_ms = ini_getl("Network", "PacketIntervalMs", IZ_NET_DEFAULT_STATE.config.packet_interval_ms, config_path); - if (!(100 <= state->config.packet_interval_ms && state->config.packet_interval_ms <= 500)) { - state->config.packet_interval_ms = IZ_NET_DEFAULT_STATE.config.packet_interval_ms; - } - - state->config.max_reconnect_retries = ini_getl("Network", "MaxReconnectRetries", IZ_NET_DEFAULT_STATE.config.max_reconnect_retries, config_path); - if (!(0 <= state->config.max_reconnect_retries && state->config.max_reconnect_retries <= 8)) { - state->config.max_reconnect_retries = IZ_NET_DEFAULT_STATE.config.max_reconnect_retries; - } - - state->config.reconnect_interval_secs = ini_getl("Network", "ReconnectIntervalSeconds", IZ_NET_DEFAULT_STATE.config.reconnect_interval_secs, config_path); - if (!(3 <= state->config.reconnect_interval_secs && state->config.reconnect_interval_secs <= 10)) { - state->config.reconnect_interval_secs = IZ_NET_DEFAULT_STATE.config.reconnect_interval_secs; - } +bool IZ_NetIsValidPacketIntervalMs(long packet_interval_ms) { + return (100 <= packet_interval_ms && packet_interval_ms <= 500); } -IZ_ProcedureResult IZ_NetSaveConfig(IZ_NetState* state, const char* config_path) { - if (!ini_puts("Network", "Username", state->config.username, config_path)) { - return -1; - } - - if (!ini_putl("Network", "PacketIntervalMs", state->config.packet_interval_ms, config_path)) { - return -1; - } - - if (!ini_putl("Network", "MaxReconnectRetries", state->config.max_reconnect_retries, config_path)) { - return -1; - } +bool IZ_NetIsValidMaxReconnectRetries(long max_reconnect_retries) { + return (0 <= max_reconnect_retries && max_reconnect_retries <= 8); +} - if (!ini_putl("Network", "ReconnectIntervalSeconds", state->config.reconnect_interval_secs, config_path)) { - return -1; - } +bool IZ_NetIsValidReconnectIntervalSeconds(long reconnect_interval_secs) { + return (3 <= reconnect_interval_secs && reconnect_interval_secs <= 10); +} - return 0; +static IZ_ConfigItem net_config_items[] = { + { + IZ_CONFIG_TYPE_STRING, + 32, + "Network", + "Username", + NULL, + &IZ_NET_DEFAULT_STATE.config.username, + NULL + }, + { + IZ_CONFIG_TYPE_U16, + sizeof(u16), + "Network", + "PacketIntervalMs", + "-i", + &IZ_NET_DEFAULT_STATE.config.packet_interval_ms, + IZ_NetIsValidPacketIntervalMs, + }, + { + IZ_CONFIG_TYPE_U8, + sizeof(u8), + "Network", + "MaxReconnectRetries", + NULL, + &IZ_NET_DEFAULT_STATE.config.max_reconnect_retries, + IZ_NetIsValidMaxReconnectRetries, + }, + { + IZ_CONFIG_TYPE_U8, + sizeof(u8), + "Network", + "ReconnectIntervalSeconds", + NULL, + &IZ_NET_DEFAULT_STATE.config.reconnect_interval_secs, + IZ_NetIsValidReconnectIntervalSeconds, + }, +}; + +void IZ_NetBindStateToConfig(IZ_NetState* state, IZ_ConfigItem config_items[]) { + config_items[0].dest = &state->config.username; + config_items[1].dest = &state->config.packet_interval_ms; + config_items[2].dest = &state->config.max_reconnect_retries; + config_items[3].dest = &state->config.reconnect_interval_secs; } -void IZ_NetOverrideConfig(IZ_NetState* state, u8 argc, const char* argv[]) { - const char* cmdline_buffer; - char* rest_of_string; - u16 packet_interval_ms; - if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, "-i"))) { - packet_interval_ms = strtol(cmdline_buffer, &rest_of_string, 10); - if (strcmp(cmdline_buffer, rest_of_string) != 0) { - state->config.packet_interval_ms = packet_interval_ms; - } - } +IZ_ProcedureResult IZ_NetSaveConfig(IZ_NetState* state, const char* config_path) { + IZ_NetBindStateToConfig(state, net_config_items); + return IZ_ConfigSave(net_config_items, config_path); } IZ_ProcedureResult IZ_NetInitialize( @@ -63,11 +72,8 @@ IZ_ProcedureResult IZ_NetInitialize( const char* argv[] ) { memcpy_s(state, sizeof(IZ_NetState), &IZ_NET_DEFAULT_STATE, sizeof(IZ_NetState)); - IZ_NetLoadConfig(state, config_path); - if (IZ_NetSaveConfig(state, config_path) < 0) { - return -1; - } - IZ_NetOverrideConfig(state, argc, argv); + IZ_NetBindStateToConfig(state, net_config_items); + IZ_ConfigInit(net_config_items, config_path, argc, argv); if (!user_data) { return -2; } diff --git a/src/packages/game/output/video/IZ_video.c b/src/packages/game/output/video/IZ_video.c index ff7b051..a588793 100644 --- a/src/packages/game/output/video/IZ_video.c +++ b/src/packages/game/output/video/IZ_video.c @@ -1,45 +1,64 @@ #include "IZ_video.h" -IZ_ProcedureResult IZ_VideoSaveConfig(IZ_VideoState* state, const char* config_path) { - if (!ini_putl("Video", "Width", state->config.width, config_path)) { - return -1; - } - if (!ini_putl("Video", "Height", state->config.height, config_path)) { - return -1; - } - if (!ini_putl("Video", "MaxFps", state->config.max_fps, config_path)) { - return -1; - } +bool IZ_VideoIsValidWidth(u16 width) { + // TODO check screen size + return (320 <= width && width <= 16384); +} - return 0; +bool IZ_VideoIsValidHeight(u16 height) { + // TODO check screen size + return (240 <= height && height <= 8192); } -void IZ_VideoLoadConfig(IZ_VideoState* state, const char* config_path) { - state->config.width = ini_getl("Video", "Width", IZ_DEFAULT_VIDEO_STATE.config.width, config_path); - state->config.height = ini_getl("Video", "Height", IZ_DEFAULT_VIDEO_STATE.config.height, config_path); - state->config.max_fps = ini_getl("Video", "MaxFps", IZ_DEFAULT_VIDEO_STATE.config.max_fps, config_path); +bool IZ_VideoIsValidMaxFPS(u8 max_fps) { + return (10 <= max_fps && max_fps <= 200); } -void IZ_VideoOverrideConfig(IZ_VideoState* state, u8 argc, const char* argv[]) { - const char* cmdline_buffer; - char* rest_of_string; - u8 max_fps; - if ((cmdline_buffer = IZ_ConfigGetCommandlineOption(argc, argv, "-f"))) { - max_fps = strtol(cmdline_buffer, &rest_of_string, 10); - if (strcmp(cmdline_buffer, rest_of_string) != 0) { - state->config.max_fps = max_fps; - } - } +static IZ_ConfigItem video_config_items[] = { + { + IZ_CONFIG_TYPE_U16, + sizeof(u16), + "Video", + "Width", + NULL, + &IZ_VIDEO_DEFAULT_STATE.config.width, + IZ_VideoIsValidWidth, + }, + { + IZ_CONFIG_TYPE_U16, + sizeof(u16), + "Video", + "Height", + NULL, + &IZ_VIDEO_DEFAULT_STATE.config.height, + IZ_VideoIsValidHeight, + }, + { + IZ_CONFIG_TYPE_U8, + sizeof(u8), + "Video", + "MaxFps", + "-f", + &IZ_VIDEO_DEFAULT_STATE.config.max_fps, + IZ_VideoIsValidMaxFPS, + }, +}; + +void IZ_VideoBindStateToConfig(IZ_VideoState* state, IZ_ConfigItem config_items[]) { + config_items[0].dest = &state->config.width; + config_items[1].dest = &state->config.height; + config_items[2].dest = &state->config.max_fps; } -IZ_ProcedureResult IZ_VideoInitialize(IZ_VideoState* state, void* user_data, const char* config_path, u8 argc, const char* argv[]) { - SDL_memcpy(state, &IZ_DEFAULT_VIDEO_STATE, sizeof(IZ_VideoState)); +IZ_ProcedureResult IZ_VideoSaveConfig(IZ_VideoState* state, const char* config_path) { + IZ_VideoBindStateToConfig(state, video_config_items); + return IZ_ConfigSave(video_config_items, config_path); +} - IZ_VideoLoadConfig(state, config_path); - if (IZ_VideoSaveConfig(state, config_path)) { -// fprintf_s(stderr, "Error committing video config.\n"); - } - IZ_VideoOverrideConfig(state, argc, argv); +IZ_ProcedureResult IZ_VideoInitialize(IZ_VideoState* state, void* user_data, const char* config_path, u8 argc, const char* argv[]) { + SDL_memcpy(state, &IZ_VIDEO_DEFAULT_STATE, sizeof(IZ_VideoState)); + IZ_VideoBindStateToConfig(state, video_config_items); + IZ_ConfigInit(video_config_items, config_path, argc, argv); state->last_update_at = 0u; state->user_data = user_data; diff --git a/src/packages/game/output/video/IZ_video.h b/src/packages/game/output/video/IZ_video.h index fdf4006..df74861 100644 --- a/src/packages/game/output/video/IZ_video.h +++ b/src/packages/game/output/video/IZ_video.h @@ -30,7 +30,7 @@ typedef struct { IZ_Sprite active_sprites[MAX_ACTIVE_SPRITES]; } IZ_VideoState; -static const IZ_VideoState IZ_DEFAULT_VIDEO_STATE = { +static const IZ_VideoState IZ_VIDEO_DEFAULT_STATE = { .user_data = NULL, .config = { .width = 320u, diff --git a/src/packages/server/IZ_app.c b/src/packages/server/IZ_app.c index 32f83cc..94afbf3 100644 --- a/src/packages/server/IZ_app.c +++ b/src/packages/server/IZ_app.c @@ -41,31 +41,45 @@ void IZ_AppTeardown(IZ_App* app) { lwsl_user("Server closed. Bye!\n"); } +void IZ_AppPrintHelpOptions() { + printf("\n"); + printf("Options:\n"); + printf("\n"); + printf( + "\n" + "Options:\n" + "\n" + " -c Specifies the path to the config file. (default: \"./config-server.ini\")\n" + " -d Specifies the path to the database. (default: \"./db.sqlite\")\n" + " -h Displays this help screen.\n" + " -m Specifies the message of the day. (default: \"\")\n" + " -n Specifies the name of the server. (default: \"%s\")\n" + " -p Specifies the port where the server runs. (default: 42069)\n", + IZ_APP_NAME + ); +} + +void IZ_AppPrintHelpUsage() { + printf("\n"); + printf("Usage:"); + printf(" %s [options]\n", "server.exe"); +} + +void IZ_AppPrintHelpHeader() { + printf("\n"); + printf("%s - %s\n", IZ_APP_NAME, IZ_APP_SERVER_DESCRIPTION); +} + +void IZ_AppPrintHelp() { + IZ_AppPrintHelpHeader(); + IZ_AppPrintHelpUsage(); + IZ_AppPrintHelpOptions(); +} + IZ_ProcedureResult IZ_AppRun(IZ_App *app, u8 argc, const char **argv) { + // TODO have a config subsystem that handles these. if (IZ_ConfigGetCommandlineOption(argc, argv, "-h")) { - printf( - ( - "\n" - "%s - %s\n" - "\n" - "Usage:\n" - "\n" - " %s [options]\n" - "\n" - "Options:\n" - "\n" - " -c Specifies the path to the config file. (default: \"./config-server.ini\")\n" - " -d Specifies the path to the database. (default: \"./db.sqlite\")\n" - " -h Displays this help file.\n" - " -m Specifies the message of the day. (default: \"\")\n" - " -n Specifies the name of the server. (default: \"%s\")\n" - " -p Specifies the port where the server runs. (default: 42069)\n" - ), - IZ_APP_NAME, - IZ_APP_SERVER_DESCRIPTION, - "server.exe", - IZ_APP_NAME - ); + IZ_AppPrintHelp(); return 0; }