From 7683ca0bf859ee2cb494a362ef575edec96efb05 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Thu, 19 Sep 2024 12:35:45 +0200 Subject: [PATCH 1/5] tools: check for missing game strings --- tools/additional_lint | 3 +- tools/libtrx/cli/additional_lint.py | 22 +++++--- tools/libtrx/linting.py | 78 ++++++++++++++++++++++++++--- 3 files changed, 88 insertions(+), 15 deletions(-) diff --git a/tools/additional_lint b/tools/additional_lint index 722353d..e2bf467 100755 --- a/tools/additional_lint +++ b/tools/additional_lint @@ -1,4 +1,5 @@ #!/usr/bin/env python3 from libtrx.cli.additional_lint import run_script +from libtrx.paths import LIBTRX_REPO_DIR -run_script(ignored_patterns=["*.patch", "*.bin", "gl_core_3_3.h"]) +run_script(root_dir=LIBTRX_REPO_DIR, ignored_patterns=["*.patch", "*.bin", "gl_core_3_3.h"]) diff --git a/tools/libtrx/cli/additional_lint.py b/tools/libtrx/cli/additional_lint.py index fbf954d..b429b1c 100644 --- a/tools/libtrx/cli/additional_lint.py +++ b/tools/libtrx/cli/additional_lint.py @@ -6,7 +6,7 @@ from pathlib import Path from libtrx.files import find_versioned_files, is_binary_file -from libtrx.linting import lint_file +from libtrx.linting import LintContext, lint_bulk_files, lint_file def parse_args() -> argparse.Namespace: @@ -37,22 +37,32 @@ def filter_files( def run_script( - root_dir: Path | None = None, ignored_patterns: list[str] | None = None + root_dir: Path, ignored_patterns: list[str] | None = None ) -> None: args = parse_args() + + context = LintContext( + root_dir=root_dir, + versioned_files=find_versioned_files(root_dir=root_dir), + ) if args.path: files = args.path else: - files = find_versioned_files(root_dir=root_dir) - - files = filter_files(files, ignored_patterns, debug=args.debug) + files = context.versioned_files + files = list(filter_files(files, ignored_patterns, debug=args.debug)) exit_code = 0 for file in files: if args.debug: print(f"Checking {file}...", file=sys.stderr) - for lint_warning in lint_file(file): + for lint_warning in lint_file(context, file): print(str(lint_warning), file=sys.stderr) exit_code = 1 + if args.debug: + print(f"Checking files in bulk {file}...", file=sys.stderr) + for lint_warning in lint_bulk_files(context, files): + print(str(lint_warning), file=sys.stderr) + exit_code = 1 + exit(exit_code) diff --git a/tools/libtrx/linting.py b/tools/libtrx/linting.py index 43b2c7b..2e490f3 100644 --- a/tools/libtrx/linting.py +++ b/tools/libtrx/linting.py @@ -1,11 +1,17 @@ #!/usr/bin/env python3 -import re import json +import re from collections.abc import Callable, Iterable from dataclasses import dataclass from pathlib import Path +@dataclass +class LintContext: + root_dir: Path + versioned_files: list[Path] + + @dataclass class LintWarning: path: Path @@ -19,7 +25,9 @@ def __str__(self) -> str: return f"{prefix}: {self.message}" -def lint_json_validity(path: Path) -> Iterable[LintWarning]: +def lint_json_validity( + context: LintContext, path: Path +) -> Iterable[LintWarning]: if path.suffix != ".json": return try: @@ -28,7 +36,7 @@ def lint_json_validity(path: Path) -> Iterable[LintWarning]: yield LintWarning(path, f"malformed JSON: {ex!s}") -def lint_newlines(path: Path) -> Iterable[LintWarning]: +def lint_newlines(context: LintContext, path: Path) -> Iterable[LintWarning]: text = path.read_text(encoding="utf-8") if not text: return @@ -38,7 +46,9 @@ def lint_newlines(path: Path) -> Iterable[LintWarning]: yield LintWarning(path, "extra newline character at end of file") -def lint_trailing_whitespace(path: Path) -> Iterable[LintWarning]: +def lint_trailing_whitespace( + context: LintContext, path: Path +) -> Iterable[LintWarning]: if path.suffix == ".md": return for i, line in enumerate(path.open("r"), 1): @@ -46,7 +56,9 @@ def lint_trailing_whitespace(path: Path) -> Iterable[LintWarning]: yield LintWarning(path, "trailing whitespace", line=i) -def lint_const_primitives(path: Path) -> Iterable[LintWarning]: +def lint_const_primitives( + context: LintContext, path: Path +) -> Iterable[LintWarning]: if path.suffix != ".h": return for i, line in enumerate(path.open("r"), 1): @@ -56,14 +68,64 @@ def lint_const_primitives(path: Path) -> Iterable[LintWarning]: yield LintWarning(path, "useless const", line=i) -ALL_LINTERS: list[Callable[[], Iterable[LintWarning]]] = [ +def lint_game_strings( + context: LintContext, paths: list[Path] +) -> Iterable[LintWarning]: + def_paths = list(context.root_dir.rglob("**/game_string.def")) + defs = [ + match.group(1) + for path in def_paths + for match in re.finditer( + r"GS_DEFINE\(([A-Z_]+),.*\)", path.read_text() + ) + ] + if not defs: + return + + path_hints = " or ".join( + str(path.relative_to(context.root_dir)) for path in def_paths + ) + + for path in paths: + if path.suffix != ".c": + continue + for i, line in enumerate(path.open("r"), 1): + if not (match := re.search(r"GS\(([A-Z_]+)\)", line)): + continue + + def_ = match.group(1) + if def_ in defs: + continue + + yield LintWarning( + path, + f"undefined game string: {def_}. " + f"Make sure it's defined in {path_hints}.", + i, + ) + + +ALL_LINTERS: list[Callable[[LintContext, Path], Iterable[LintWarning]]] = [ lint_json_validity, lint_newlines, lint_trailing_whitespace, lint_const_primitives, ] +ALL_BULK_LINTERS: list[ + Callable[[LintContext, list[Path]], Iterable[LintWarning]] +] = [ + lint_game_strings, +] + -def lint_file(file: Path) -> Iterable[LintWarning]: +def lint_file(context: LintContext, file: Path) -> Iterable[LintWarning]: for linter_func in ALL_LINTERS: - yield from linter_func(file) + yield from linter_func(context, file) + + +def lint_bulk_files( + context: LintContext, files: list[Path] +) -> Iterable[LintWarning]: + for linter_func in ALL_BULK_LINTERS: + yield from linter_func(context, files) From 88c7e60774574898ba6772ca193ef96447776b79 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Thu, 19 Sep 2024 10:16:35 +0200 Subject: [PATCH 2/5] console: import /set --- include/libtrx/config/config.h | 9 + include/libtrx/game/console/commands/config.h | 5 + include/libtrx/game/game_string.def | 4 + meson.build | 1 + src/game/console/commands/config.c | 205 ++++++++++++++++++ 5 files changed, 224 insertions(+) create mode 100644 include/libtrx/config/config.h create mode 100644 include/libtrx/game/console/commands/config.h create mode 100644 src/game/console/commands/config.c diff --git a/include/libtrx/config/config.h b/include/libtrx/config/config.h new file mode 100644 index 0000000..5999263 --- /dev/null +++ b/include/libtrx/config/config.h @@ -0,0 +1,9 @@ +#pragma once + +#include "config_option.h" + +bool Config_Read(void); +bool Config_Write(void); +void Config_Sanitize(void); +void Config_ApplyChanges(void); +const CONFIG_OPTION *Config_GetOptionMap(void); diff --git a/include/libtrx/game/console/commands/config.h b/include/libtrx/game/console/commands/config.h new file mode 100644 index 0000000..7834cdd --- /dev/null +++ b/include/libtrx/game/console/commands/config.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../common.h" + +extern CONSOLE_COMMAND g_Console_Cmd_Config; diff --git a/include/libtrx/game/game_string.def b/include/libtrx/game/game_string.def index f7e58e1..bb3b32b 100644 --- a/include/libtrx/game/game_string.def +++ b/include/libtrx/game/game_string.def @@ -1,3 +1,7 @@ GS_DEFINE(OSD_POS_GET, "Room: %d\nPosition: %.3f, %.3f, %.3f\nRotation: %.3f,%.3f,%.3f") GS_DEFINE(OSD_CURRENT_HEALTH_GET, "Current Lara's health: %d") GS_DEFINE(OSD_CURRENT_HEALTH_SET, "Lara's health set to %d") +GS_DEFINE(OSD_CONFIG_OPTION_GET, "%s is currently set to %s") +GS_DEFINE(OSD_CONFIG_OPTION_SET, "%s changed to %s") +GS_DEFINE(MISC_ON, "On") +GS_DEFINE(MISC_OFF, "Off") diff --git a/meson.build b/meson.build index ff434fd..f77439c 100644 --- a/meson.build +++ b/meson.build @@ -65,6 +65,7 @@ sources = [ 'src/engine/image.c', 'src/enum_str.c', 'src/filesystem.c', + 'src/game/console/commands/config.c', 'src/game/console/commands/pos.c', 'src/game/console/commands/set_health.c', 'src/game/items.c', diff --git a/src/game/console/commands/config.c b/src/game/console/commands/config.c new file mode 100644 index 0000000..5d17cf5 --- /dev/null +++ b/src/game/console/commands/config.c @@ -0,0 +1,205 @@ +#include "game/console/commands/config.h" + +#include "config/config.h" +#include "config/config_map.h" +#include "game/game_string.h" +#include "memory.h" +#include "strings.h" + +#include +#include +#include + +static COMMAND_RESULT Console_Cmd_Set(const char *args); +static bool Console_Cmd_Config_SetCurrentValue( + const char *key, const char *new_value); +static const char *Console_Cmd_Config_Resolve(const char *option_name); +static bool Console_Cmd_Config_SameKey(const char *key1, const char *key2); +static bool Console_Cmd_Config_GetCurrentValue( + const char *key, char *target, size_t target_size); + +static const char *Console_Cmd_Config_Resolve(const char *const option_name) +{ + const char *dot = strrchr(option_name, '.'); + if (dot) { + return dot + 1; + } + return option_name; +} + +static bool Console_Cmd_Config_SameKey(const char *key1, const char *key2) +{ + key1 = Console_Cmd_Config_Resolve(key1); + key2 = Console_Cmd_Config_Resolve(key2); + const size_t len1 = strlen(key1); + const size_t len2 = strlen(key2); + if (len1 != len2) { + return false; + } + for (uint32_t i = 0; i < len1; i++) { + char c1 = key1[i]; + char c2 = key2[i]; + if (c1 == '_') { + c1 = '-'; + } + if (c2 == '_') { + c2 = '-'; + } + if (c1 != c2) { + return false; + } + } + return true; +} + +static bool Console_Cmd_Config_GetCurrentValue( + const char *const key, char *target, const size_t target_size) +{ + const CONFIG_OPTION *found_option = NULL; + for (const CONFIG_OPTION *option = Config_GetOptionMap(); + option->name != NULL; option++) { + if (!Console_Cmd_Config_SameKey(option->name, key)) { + continue; + } + found_option = option; + break; + } + + if (found_option == NULL) { + return false; + } + + assert(found_option->target != NULL); + switch (found_option->type) { + case COT_BOOL: + snprintf( + target, target_size, "%s", + *(bool *)found_option->target ? GS(MISC_ON) : GS(MISC_OFF)); + break; + case COT_INT32: + snprintf(target, target_size, "%d", *(int32_t *)found_option->target); + break; + case COT_FLOAT: + snprintf(target, target_size, "%.2f", *(float *)found_option->target); + break; + case COT_DOUBLE: + snprintf(target, target_size, "%.2f", *(double *)found_option->target); + break; + case COT_ENUM: + for (const ENUM_STRING_MAP *enum_map = found_option->param; + enum_map->text != NULL; enum_map++) { + if (enum_map->value == *(int32_t *)found_option->target) { + strncpy(target, enum_map->text, target_size); + } + } + break; + } + return true; +} + +static bool Console_Cmd_Config_SetCurrentValue( + const char *const key, const char *const new_value) +{ + const CONFIG_OPTION *found_option = NULL; + + for (const CONFIG_OPTION *option = Config_GetOptionMap(); + option->name != NULL; option++) { + if (!Console_Cmd_Config_SameKey(option->name, key)) { + continue; + } + found_option = option; + break; + } + + switch (found_option->type) { + case COT_BOOL: + if (String_Match(new_value, "on|true|1")) { + *(bool *)found_option->target = true; + return true; + } else if (String_Match(new_value, "off|false|0")) { + *(bool *)found_option->target = false; + return true; + } + break; + + case COT_INT32: { + int32_t new_value_typed; + if (sscanf(new_value, "%d", &new_value_typed) == 1) { + *(int32_t *)found_option->target = new_value_typed; + return true; + } + break; + } + + case COT_FLOAT: { + float new_value_typed; + if (sscanf(new_value, "%f", &new_value_typed) == 1) { + *(float *)found_option->target = new_value_typed; + return true; + } + break; + } + + case COT_DOUBLE: { + double new_value_typed; + if (sscanf(new_value, "%lf", &new_value_typed) == 1) { + *(double *)found_option->target = new_value_typed; + return true; + } + break; + } + + case COT_ENUM: + for (const ENUM_STRING_MAP *enum_map = found_option->param; + enum_map->text != NULL; enum_map++) { + if (String_Equivalent(enum_map->text, new_value)) { + *(int32_t *)found_option->target = enum_map->value; + return true; + } + } + break; + } + + return false; +} + +static COMMAND_RESULT Console_Cmd_Config(const char *const args) +{ + COMMAND_RESULT result = CR_BAD_INVOCATION; + + char *key = Memory_DupStr(args); + char *space = strchr(key, ' '); + char *new_value = NULL; + if (space != NULL) { + new_value = space + 1; + space[0] = '\0'; // NULL-terminate the key + } + + if (new_value != NULL) { + if (Console_Cmd_Config_SetCurrentValue(key, new_value)) { + Config_Sanitize(); + Config_Write(); + Config_ApplyChanges(); + + char final_value[128]; + assert(Console_Cmd_Config_GetCurrentValue(key, final_value, 128)); + Console_Log(GS(OSD_CONFIG_OPTION_SET), key, final_value); + result = CR_SUCCESS; + } + } else { + char cur_value[128]; + if (Console_Cmd_Config_GetCurrentValue(key, cur_value, 128)) { + Console_Log(GS(OSD_CONFIG_OPTION_GET), key, cur_value); + result = CR_SUCCESS; + } + } + +cleanup: + Memory_FreePointer(&key); + return result; +} + +CONSOLE_COMMAND g_Console_Cmd_Config = { + .prefix = "set", + .proc = Console_Cmd_Config, +}; From 30c922c058514aa01a3935a387f27add5856e812 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Thu, 19 Sep 2024 13:58:15 +0200 Subject: [PATCH 3/5] console/cmd/config: fix crash when setting unknown options --- include/libtrx/game/game_string.def | 1 + src/game/console/commands/config.c | 93 ++++++++++++++++------------- 2 files changed, 53 insertions(+), 41 deletions(-) diff --git a/include/libtrx/game/game_string.def b/include/libtrx/game/game_string.def index bb3b32b..fcf8d0b 100644 --- a/include/libtrx/game/game_string.def +++ b/include/libtrx/game/game_string.def @@ -3,5 +3,6 @@ GS_DEFINE(OSD_CURRENT_HEALTH_GET, "Current Lara's health: %d") GS_DEFINE(OSD_CURRENT_HEALTH_SET, "Lara's health set to %d") GS_DEFINE(OSD_CONFIG_OPTION_GET, "%s is currently set to %s") GS_DEFINE(OSD_CONFIG_OPTION_SET, "%s changed to %s") +GS_DEFINE(OSD_CONFIG_OPTION_UNKNOWN_OPTION, "Unknown option: %s") GS_DEFINE(MISC_ON, "On") GS_DEFINE(MISC_OFF, "Off") diff --git a/src/game/console/commands/config.c b/src/game/console/commands/config.c index 5d17cf5..d5f327f 100644 --- a/src/game/console/commands/config.c +++ b/src/game/console/commands/config.c @@ -10,13 +10,18 @@ #include #include -static COMMAND_RESULT Console_Cmd_Set(const char *args); -static bool Console_Cmd_Config_SetCurrentValue( - const char *key, const char *new_value); static const char *Console_Cmd_Config_Resolve(const char *option_name); static bool Console_Cmd_Config_SameKey(const char *key1, const char *key2); + +static const CONFIG_OPTION *Console_Cmd_Config_GetOptionFromKey( + const char *key); + static bool Console_Cmd_Config_GetCurrentValue( - const char *key, char *target, size_t target_size); + const CONFIG_OPTION *option, char *target, size_t target_size); +static bool Console_Cmd_Config_SetCurrentValue( + const CONFIG_OPTION *option, const char *new_value); + +static COMMAND_RESULT Console_Cmd_Set(const char *args); static const char *Console_Cmd_Config_Resolve(const char *const option_name) { @@ -52,43 +57,46 @@ static bool Console_Cmd_Config_SameKey(const char *key1, const char *key2) return true; } -static bool Console_Cmd_Config_GetCurrentValue( - const char *const key, char *target, const size_t target_size) +static const CONFIG_OPTION *Console_Cmd_Config_GetOptionFromKey( + const char *const key) { - const CONFIG_OPTION *found_option = NULL; for (const CONFIG_OPTION *option = Config_GetOptionMap(); option->name != NULL; option++) { - if (!Console_Cmd_Config_SameKey(option->name, key)) { - continue; + if (Console_Cmd_Config_SameKey(option->name, key)) { + return option; } - found_option = option; - break; } - if (found_option == NULL) { + return NULL; +} + +static bool Console_Cmd_Config_GetCurrentValue( + const CONFIG_OPTION *const option, char *target, const size_t target_size) +{ + if (option == NULL) { return false; } - assert(found_option->target != NULL); - switch (found_option->type) { + assert(option->target != NULL); + switch (option->type) { case COT_BOOL: snprintf( target, target_size, "%s", - *(bool *)found_option->target ? GS(MISC_ON) : GS(MISC_OFF)); + *(bool *)option->target ? GS(MISC_ON) : GS(MISC_OFF)); break; case COT_INT32: - snprintf(target, target_size, "%d", *(int32_t *)found_option->target); + snprintf(target, target_size, "%d", *(int32_t *)option->target); break; case COT_FLOAT: - snprintf(target, target_size, "%.2f", *(float *)found_option->target); + snprintf(target, target_size, "%.2f", *(float *)option->target); break; case COT_DOUBLE: - snprintf(target, target_size, "%.2f", *(double *)found_option->target); + snprintf(target, target_size, "%.2f", *(double *)option->target); break; case COT_ENUM: - for (const ENUM_STRING_MAP *enum_map = found_option->param; + for (const ENUM_STRING_MAP *enum_map = option->param; enum_map->text != NULL; enum_map++) { - if (enum_map->value == *(int32_t *)found_option->target) { + if (enum_map->value == *(int32_t *)option->target) { strncpy(target, enum_map->text, target_size); } } @@ -98,26 +106,20 @@ static bool Console_Cmd_Config_GetCurrentValue( } static bool Console_Cmd_Config_SetCurrentValue( - const char *const key, const char *const new_value) + const CONFIG_OPTION *const option, const char *const new_value) { - const CONFIG_OPTION *found_option = NULL; - - for (const CONFIG_OPTION *option = Config_GetOptionMap(); - option->name != NULL; option++) { - if (!Console_Cmd_Config_SameKey(option->name, key)) { - continue; - } - found_option = option; - break; + if (option == NULL) { + return CR_BAD_INVOCATION; } - switch (found_option->type) { + assert(option->target != NULL); + switch (option->type) { case COT_BOOL: if (String_Match(new_value, "on|true|1")) { - *(bool *)found_option->target = true; + *(bool *)option->target = true; return true; } else if (String_Match(new_value, "off|false|0")) { - *(bool *)found_option->target = false; + *(bool *)option->target = false; return true; } break; @@ -125,7 +127,7 @@ static bool Console_Cmd_Config_SetCurrentValue( case COT_INT32: { int32_t new_value_typed; if (sscanf(new_value, "%d", &new_value_typed) == 1) { - *(int32_t *)found_option->target = new_value_typed; + *(int32_t *)option->target = new_value_typed; return true; } break; @@ -134,7 +136,7 @@ static bool Console_Cmd_Config_SetCurrentValue( case COT_FLOAT: { float new_value_typed; if (sscanf(new_value, "%f", &new_value_typed) == 1) { - *(float *)found_option->target = new_value_typed; + *(float *)option->target = new_value_typed; return true; } break; @@ -143,17 +145,17 @@ static bool Console_Cmd_Config_SetCurrentValue( case COT_DOUBLE: { double new_value_typed; if (sscanf(new_value, "%lf", &new_value_typed) == 1) { - *(double *)found_option->target = new_value_typed; + *(double *)option->target = new_value_typed; return true; } break; } case COT_ENUM: - for (const ENUM_STRING_MAP *enum_map = found_option->param; + for (const ENUM_STRING_MAP *enum_map = option->param; enum_map->text != NULL; enum_map++) { if (String_Equivalent(enum_map->text, new_value)) { - *(int32_t *)found_option->target = enum_map->value; + *(int32_t *)option->target = enum_map->value; return true; } } @@ -175,20 +177,29 @@ static COMMAND_RESULT Console_Cmd_Config(const char *const args) space[0] = '\0'; // NULL-terminate the key } + const CONFIG_OPTION *const option = + Console_Cmd_Config_GetOptionFromKey(key); + if (option == NULL) { + Console_Log(GS(OSD_CONFIG_OPTION_UNKNOWN_OPTION), key); + result = CR_FAILURE; + goto cleanup; + } + if (new_value != NULL) { - if (Console_Cmd_Config_SetCurrentValue(key, new_value)) { + if (Console_Cmd_Config_SetCurrentValue(option, new_value)) { Config_Sanitize(); Config_Write(); Config_ApplyChanges(); char final_value[128]; - assert(Console_Cmd_Config_GetCurrentValue(key, final_value, 128)); + assert( + Console_Cmd_Config_GetCurrentValue(option, final_value, 128)); Console_Log(GS(OSD_CONFIG_OPTION_SET), key, final_value); result = CR_SUCCESS; } } else { char cur_value[128]; - if (Console_Cmd_Config_GetCurrentValue(key, cur_value, 128)) { + if (Console_Cmd_Config_GetCurrentValue(option, cur_value, 128)) { Console_Log(GS(OSD_CONFIG_OPTION_GET), key, cur_value); result = CR_SUCCESS; } From a444a38cd1ea7e11c10ddd4b373f4fda926eebb9 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Thu, 19 Sep 2024 14:13:58 +0200 Subject: [PATCH 4/5] console/cmd/config: add get/set helper api --- include/libtrx/game/console/commands/config.h | 6 ++ src/game/console/commands/config.c | 102 ++++++++++++------ 2 files changed, 75 insertions(+), 33 deletions(-) diff --git a/include/libtrx/game/console/commands/config.h b/include/libtrx/game/console/commands/config.h index 7834cdd..3a552b9 100644 --- a/include/libtrx/game/console/commands/config.h +++ b/include/libtrx/game/console/commands/config.h @@ -1,5 +1,11 @@ #pragma once +#include "../../../config/config_option.h" #include "../common.h" extern CONSOLE_COMMAND g_Console_Cmd_Config; + +const CONFIG_OPTION *Console_Cmd_Config_GetOptionFromKey(const char *key); +const CONFIG_OPTION *Console_Cmd_Config_GetOptionFromTarget(const void *target); +COMMAND_RESULT Console_Cmd_Config_Helper( + const CONFIG_OPTION *option, const char *new_value); diff --git a/src/game/console/commands/config.c b/src/game/console/commands/config.c index d5f327f..8759628 100644 --- a/src/game/console/commands/config.c +++ b/src/game/console/commands/config.c @@ -12,9 +12,7 @@ static const char *Console_Cmd_Config_Resolve(const char *option_name); static bool Console_Cmd_Config_SameKey(const char *key1, const char *key2); - -static const CONFIG_OPTION *Console_Cmd_Config_GetOptionFromKey( - const char *key); +static char *Console_Cmd_Config_NormalizeKey(const char *key); static bool Console_Cmd_Config_GetCurrentValue( const CONFIG_OPTION *option, char *target, size_t target_size); @@ -57,17 +55,17 @@ static bool Console_Cmd_Config_SameKey(const char *key1, const char *key2) return true; } -static const CONFIG_OPTION *Console_Cmd_Config_GetOptionFromKey( - const char *const key) +static char *Console_Cmd_Config_NormalizeKey(const char *key) { - for (const CONFIG_OPTION *option = Config_GetOptionMap(); - option->name != NULL; option++) { - if (Console_Cmd_Config_SameKey(option->name, key)) { - return option; + // TODO: Once we support arbitrary glyphs, this conversion should + // no longer be necessary. + char *result = Memory_DupStr(key); + for (uint32_t i = 0; i < strlen(result); i++) { + if (result[i] == '_') { + result[i] = '-'; } } - - return NULL; + return result; } static bool Console_Cmd_Config_GetCurrentValue( @@ -165,13 +163,70 @@ static bool Console_Cmd_Config_SetCurrentValue( return false; } +const CONFIG_OPTION *Console_Cmd_Config_GetOptionFromKey(const char *const key) +{ + for (const CONFIG_OPTION *option = Config_GetOptionMap(); + option->name != NULL; option++) { + if (Console_Cmd_Config_SameKey(option->name, key)) { + return option; + } + } + + return NULL; +} + +const CONFIG_OPTION *Console_Cmd_Config_GetOptionFromTarget( + const void *const target) +{ + for (const CONFIG_OPTION *option = Config_GetOptionMap(); + option->name != NULL; option++) { + if (option->target == target) { + return option; + } + } + + return NULL; +} + +COMMAND_RESULT Console_Cmd_Config_Helper( + const CONFIG_OPTION *const option, const char *const new_value) +{ + assert(option != NULL); + + char *normalized_name = Console_Cmd_Config_NormalizeKey(option->name); + + COMMAND_RESULT result = CR_BAD_INVOCATION; + if (new_value == NULL || String_IsEmpty(new_value)) { + char cur_value[128]; + if (Console_Cmd_Config_GetCurrentValue(option, cur_value, 128)) { + Console_Log(GS(OSD_CONFIG_OPTION_GET), normalized_name, cur_value); + result = CR_SUCCESS; + } + } + + if (Console_Cmd_Config_SetCurrentValue(option, new_value)) { + Config_Sanitize(); + Config_Write(); + Config_ApplyChanges(); + + char final_value[128]; + assert(Console_Cmd_Config_GetCurrentValue(option, final_value, 128)); + Console_Log(GS(OSD_CONFIG_OPTION_SET), normalized_name, final_value); + result = CR_SUCCESS; + } + +cleanup: + Memory_FreePointer(&normalized_name); + return result; +} + static COMMAND_RESULT Console_Cmd_Config(const char *const args) { COMMAND_RESULT result = CR_BAD_INVOCATION; char *key = Memory_DupStr(args); - char *space = strchr(key, ' '); - char *new_value = NULL; + char *const space = strchr(key, ' '); + const char *new_value = NULL; if (space != NULL) { new_value = space + 1; space[0] = '\0'; // NULL-terminate the key @@ -182,27 +237,8 @@ static COMMAND_RESULT Console_Cmd_Config(const char *const args) if (option == NULL) { Console_Log(GS(OSD_CONFIG_OPTION_UNKNOWN_OPTION), key); result = CR_FAILURE; - goto cleanup; - } - - if (new_value != NULL) { - if (Console_Cmd_Config_SetCurrentValue(option, new_value)) { - Config_Sanitize(); - Config_Write(); - Config_ApplyChanges(); - - char final_value[128]; - assert( - Console_Cmd_Config_GetCurrentValue(option, final_value, 128)); - Console_Log(GS(OSD_CONFIG_OPTION_SET), key, final_value); - result = CR_SUCCESS; - } } else { - char cur_value[128]; - if (Console_Cmd_Config_GetCurrentValue(option, cur_value, 128)) { - Console_Log(GS(OSD_CONFIG_OPTION_GET), key, cur_value); - result = CR_SUCCESS; - } + result = Console_Cmd_Config_Helper(option, new_value); } cleanup: From 39c2d55127f456909c21934ba48f431a0040eac6 Mon Sep 17 00:00:00 2001 From: Marcin Kurczewski Date: Thu, 19 Sep 2024 14:30:11 +0200 Subject: [PATCH 5/5] console/cmd/heal: import --- include/libtrx/game/console/commands/heal.h | 5 ++++ include/libtrx/game/game_string.def | 2 ++ include/libtrx/game/lara/misc.h | 3 +++ meson.build | 1 + src/game/console/commands/heal.c | 29 +++++++++++++++++++++ 5 files changed, 40 insertions(+) create mode 100644 include/libtrx/game/console/commands/heal.h create mode 100644 include/libtrx/game/lara/misc.h create mode 100644 src/game/console/commands/heal.c diff --git a/include/libtrx/game/console/commands/heal.h b/include/libtrx/game/console/commands/heal.h new file mode 100644 index 0000000..0f1fa0a --- /dev/null +++ b/include/libtrx/game/console/commands/heal.h @@ -0,0 +1,5 @@ +#pragma once + +#include "../common.h" + +extern CONSOLE_COMMAND g_Console_Cmd_Heal; diff --git a/include/libtrx/game/game_string.def b/include/libtrx/game/game_string.def index fcf8d0b..07c458b 100644 --- a/include/libtrx/game/game_string.def +++ b/include/libtrx/game/game_string.def @@ -6,3 +6,5 @@ GS_DEFINE(OSD_CONFIG_OPTION_SET, "%s changed to %s") GS_DEFINE(OSD_CONFIG_OPTION_UNKNOWN_OPTION, "Unknown option: %s") GS_DEFINE(MISC_ON, "On") GS_DEFINE(MISC_OFF, "Off") +GS_DEFINE(OSD_HEAL_ALREADY_FULL_HP, "Lara's already at full health") +GS_DEFINE(OSD_HEAL_SUCCESS, "Healed Lara back to full health") diff --git a/include/libtrx/game/lara/misc.h b/include/libtrx/game/lara/misc.h new file mode 100644 index 0000000..5c7ce0c --- /dev/null +++ b/include/libtrx/game/lara/misc.h @@ -0,0 +1,3 @@ +#pragma once + +void Lara_Extinguish(void); diff --git a/meson.build b/meson.build index f77439c..4e3b290 100644 --- a/meson.build +++ b/meson.build @@ -66,6 +66,7 @@ sources = [ 'src/enum_str.c', 'src/filesystem.c', 'src/game/console/commands/config.c', + 'src/game/console/commands/heal.c', 'src/game/console/commands/pos.c', 'src/game/console/commands/set_health.c', 'src/game/items.c', diff --git a/src/game/console/commands/heal.c b/src/game/console/commands/heal.c new file mode 100644 index 0000000..f687947 --- /dev/null +++ b/src/game/console/commands/heal.c @@ -0,0 +1,29 @@ +#include "game/console/commands/heal.h" + +#include "game/game.h" +#include "game/game_string.h" +#include "game/lara/common.h" +#include "game/lara/misc.h" + +static COMMAND_RESULT Console_Cmd_Heal(const char *const args) +{ + if (!Game_IsPlayable()) { + return CR_UNAVAILABLE; + } + + ITEM_INFO *const lara_item = Lara_GetItem(); + if (lara_item->hit_points == LARA_MAX_HITPOINTS) { + Console_Log(GS(OSD_HEAL_ALREADY_FULL_HP)); + return CR_SUCCESS; + } + + lara_item->hit_points = LARA_MAX_HITPOINTS; + Lara_Extinguish(); + Console_Log(GS(OSD_HEAL_SUCCESS)); + return CR_SUCCESS; +} + +CONSOLE_COMMAND g_Console_Cmd_Heal = { + .prefix = "heal", + .proc = Console_Cmd_Heal, +};