From a118a3fa98a4ccbc96d774b0d125abac702d2815 Mon Sep 17 00:00:00 2001 From: akmubi Date: Tue, 25 Aug 2026 15:16:10 +0500 Subject: [PATCH 1/8] signatures: rewrite --- include/signatures.h | 6 +- src/input.c | 81 +++++++------ src/main.c | 2 +- src/signatures.c | 276 ++++++++++++++++++++++++++++--------------- 4 files changed, 228 insertions(+), 137 deletions(-) diff --git a/include/signatures.h b/include/signatures.h index 357c669..1a72904 100644 --- a/include/signatures.h +++ b/include/signatures.h @@ -21,7 +21,9 @@ #include "unreal_funcs.inc" -bool -scan_user_module_signatures(void); +void +signatures_init(void); +void +enable_input_hooks(void); #endif /* SIGNATURES_H */ diff --git a/src/input.c b/src/input.c index f961885..1706a66 100644 --- a/src/input.c +++ b/src/input.c @@ -178,55 +178,60 @@ static input_key_map_entry_t g_key_map[INPUT_KEY_MAX] = { void input_key_map_init(void) { - MASSERT(globals.name_pool != NULL, "name pool is missing"); - - /* resolve each UE4 key name to its cmp_idx by scanning the name pool */ - fname_entry_allocator_t *entries = &globals.name_pool->entries; - uint32_t num_blocks = entries->current_block + 1; - - for (uint32_t block = 0; block < num_blocks && block < FNAME_MAX_BLOCKS; ++block) { - uint8_t *base = entries->blocks[block]; - if (!base) { - continue; - } + static bool map_inited = false; + if (!map_inited) { + MASSERT(globals.name_pool != NULL, "name pool is missing"); + + /* resolve each UE4 key name to its cmp_idx by scanning the name pool */ + fname_entry_allocator_t *entries = &globals.name_pool->entries; + uint32_t num_blocks = entries->current_block + 1; + + for (uint32_t block = 0; block < num_blocks && block < FNAME_MAX_BLOCKS; ++block) { + uint8_t *base = entries->blocks[block]; + if (!base) { + continue; + } - uint32_t end_offset = (1u << FNAME_BLOCK_OFFSET_BITS) * 2; - if (block == entries->current_block) { - end_offset = entries->current_byte_cursor; - } + uint32_t end_offset = (1u << FNAME_BLOCK_OFFSET_BITS) * 2; + if (block == entries->current_block) { + end_offset = entries->current_byte_cursor; + } - uint32_t byte_offset = 0; - while (byte_offset < end_offset) { - fname_entry_t *entry = (fname_entry_t *)(base + byte_offset); - uint16_t len = entry->header.len; + uint32_t byte_offset = 0; + while (byte_offset < end_offset) { + fname_entry_t *entry = (fname_entry_t *)(base + byte_offset); + uint16_t len = entry->header.len; - if (len > 0 && !entry->header.is_wide) { - for (int i = 0; i < COUNTOF(g_key_map); ++i) { - if (g_key_map[i].cmp_idx != 0) { - continue; // already resolved - } + if (len > 0 && !entry->header.is_wide) { + for (int i = 0; i < COUNTOF(g_key_map); ++i) { + if (g_key_map[i].cmp_idx != 0) { + continue; // already resolved + } - str_t ue_name = g_key_map[i].ue_name; - str_t entry_name = str_make(entry->name.ansi, (uint64_t)len); + str_t ue_name = g_key_map[i].ue_name; + str_t entry_name = str_make(entry->name.ansi, (uint64_t)len); - if (str_equal(ue_name, entry_name, 0)) { - uint32_t cmp_idx = (block << FNAME_BLOCK_OFFSET_BITS) | (byte_offset / 2); - g_key_map[i].cmp_idx = cmp_idx; + if (str_equal(ue_name, entry_name, 0)) { + uint32_t cmp_idx = (block << FNAME_BLOCK_OFFSET_BITS) | (byte_offset / 2); + g_key_map[i].cmp_idx = cmp_idx; - // LOG_DEBUG("%.*s: cmp_idx: %d, block: %d, byte_offset: %d", STR_ARG(ue_name), cmp_idx, block, - // byte_offset); - break; + // LOG_DEBUG("%.*s: cmp_idx: %d, block: %d, byte_offset: %d", STR_ARG(ue_name), cmp_idx, block, + // byte_offset); + break; + } } } - } - /* advance to next entry: header (2 bytes) + payload, aligned to 2 */ - uint32_t entry_size = sizeof(fname_entry_header_t); + /* advance to next entry: header (2 bytes) + payload, aligned to 2 */ + uint32_t entry_size = sizeof(fname_entry_header_t); - entry_size += entry->header.is_wide ? (len * 2) : len; - entry_size = ALIGN_UP(entry_size, 2); - byte_offset += entry_size; + entry_size += entry->header.is_wide ? (len * 2) : len; + entry_size = ALIGN_UP(entry_size, 2); + byte_offset += entry_size; + } } + + map_inited = true; } } diff --git a/src/main.c b/src/main.c index 503abcc..16c94ee 100644 --- a/src/main.c +++ b/src/main.c @@ -268,7 +268,7 @@ loader_init(HINSTANCE inst) ui_manager_preinit(&globals.ui_manager, &globals.mod_manager, &globals.perm); hook_chain_init(); - scan_user_module_signatures(); + signatures_init(); globals.listeners = ARENA_PUSH_ARRAY_ZERO(&globals.perm, uobject_listener_t, CONFIG_UOBJECT_ARRAY_MAX_LISTENERS); ASSERT(globals.listeners != NULL); diff --git a/src/signatures.c b/src/signatures.c index 08c76e3..0dce17b 100644 --- a/src/signatures.c +++ b/src/signatures.c @@ -2,13 +2,12 @@ #include "globals.h" #include "hook_chain.h" +#include "input.h" #include "log.h" #include "sigscan.h" #include -static hook_chain_handle_t g_signature_hooks[64]; - #define UE_FUNC_NORMAL(NAME, RET, ...) \ NAME##_fn_t NAME = NULL; #define UE_FUNC_HOOKED(NAME, RET, ...) \ @@ -24,6 +23,14 @@ static hook_chain_handle_t g_signature_hooks[64]; .original = NULL, \ } +#define SIG_ENTRY_PATCHED(ENTRY_TYPE, NAME, PATCH_FN, PATTERN, ...) \ + { \ + .entry = SIG_ENTRY_##ENTRY_TYPE(NAME, PATTERN, __VA_ARGS__), \ + .detour = NULL, \ + .original = NULL, \ + .patch = PATCH_FN, \ + } + #define SIG_ENTRY_HOOKED(ENTRY_TYPE, NAME, PATTERN, ...) \ { \ .entry = SIG_ENTRY_##ENTRY_TYPE(NAME, PATTERN, __VA_ARGS__), \ @@ -31,6 +38,26 @@ static hook_chain_handle_t g_signature_hooks[64]; .original = (LPVOID *)&NAME##_real, \ } +enum { + SIG_KIND_EARLY, + SIG_KIND_MAIN, + SIG_KIND_INPUT, + SIG_KIND_MAX, +}; + +typedef void (*patch_fn_t)(sigscan_entry_t *entry); + +typedef struct signature_s { + sigscan_entry_t entry; + void **original; + void *detour; + patch_fn_t patch; + hook_chain_handle_t hook; + bool resolved; + bool hook_created; + bool hook_enabled; +} signature_t; + static void patch_get_pak_signkey_helper(sigscan_entry_t *e) { @@ -75,125 +102,182 @@ patch_setup_signed_pak_reader(sigscan_entry_t *e) } } -bool -scan_user_module_signatures(void) +static signature_t g_early_sigs[] = { + SIG_ENTRY_PATCHED(DIRECT, get_pak_signkey_helper, patch_get_pak_signkey_helper, "48 83 EC ? E8 ? ? ? ? 83 78 ? 00", 0x0274C4D0, 0x026D2420), + SIG_ENTRY_PATCHED(DIRECT, setup_signed_pak_reader, patch_setup_signed_pak_reader, "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC ?? 80 B9 ?? ?? ?? ?? 00 4D 8B E0", 0x0400D950, 0x03E23700), +}; + +static signature_t g_main_sigs[] = { + SIG_ENTRY_HOOKED(CALL_ARG, pak_file_mount, "E8 ? ? ? ? 84 C0 74 ? 41 FF C5 FF C6", 0x0401689D, 0x03E2C69D), + SIG_ENTRY_HOOKED(DIRECT, io_dispatcher_mount, "40 53 41 55 41 57 48 81 EC ? ? ? ? 48 8B 05", 0x02745980, 0x026CB8D0), + SIG_ENTRY_HOOKED(DIRECT, pak_precacher_add_new_block, "48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 45 33 C9", 0x00000000, 0x03E1CF90), + SIG_ENTRY_HOOKED(DIRECT, game_engine_tick, "48 8B C4 44 88 40 ? 48 89 48 ? 55 53", 0x0434A5A0, 0x04162180), + SIG_ENTRY_HOOKED(DIRECT, process_event, "40 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ? ? ? ? 48 8D 6C 24 ? 48 89 9D ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C5 48 89 85 ? ? ? ? 8B 41", 0x029FDCD0, 0x02981E80), + SIG_ENTRY_HOOKED(DIRECT, ufunction_invoke, "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC ? 48 8B 59 ? 4D 8B F1", 0x0289B0B0, 0x0281F2B0), + SIG_ENTRY_HOOKED(DIRECT, d3d12_viewport_init, "48 89 74 24 ? 48 89 7C 24 ? 55 41 54 41 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 45 ? 4C 8B 69", 0x02D44E20, 0x02C98770), + SIG_ENTRY_NORMAL(LEA_ADDR, globals.uobjects, "4C 8D 1D ? ? ? ? 89 41", 0x00FDCEF0, 0x00FA15D0), + SIG_ENTRY_NORMAL(LEA_ADDR, globals.name_pool, "48 8D 05 ? ? ? ? EB ? 48 8D 0D ? ? ? ? E8 ? ? ? ? C6 05 ? ? ? ? ? 8B 80 ? ? ? ? 83 8B", 0x042D26E4, 0x040E9DA4), + SIG_ENTRY_NORMAL(MOV64_ADDR, globals.gworld_ptr, "48 8B 05 ? ? ? ? 4C 8D 44 24 ? 48 8D 54 24 ? 48 89 44 24 ? 48 8B CB", 0x047AD35A, 0x045C489A), + SIG_ENTRY_NORMAL(LEA_ADDR, globals.natives, "48 8D 05 ? ? ? ? B9 ? ? ? ? 0F 1F 40 ? 66 0F 1F 84 00 ? ? ? ? ? ? ? 48 89 50 ? 48 89 50 ? 48 8D 40 ? 48 89 50 ? 48 89 50 ? 48 89 50 ? 48 89 50 ? 48 89 50 ? 48 83 E9 ? 75 ? 8B 05 ? ? ? ? 33 C9", 0x00A062C7, 0x009BC917), + SIG_ENTRY_NORMAL(LEA_ADDR, globals.uobject_vtable, "48 8D 05 ? ? ? ? ? ? ? 48 8B FA 48 8B D9", 0x02A1A6E1, 0x0299E821), + SIG_ENTRY_NORMAL(MOV64_ADDR, globals.gameplay_tags_manager_ptr, "48 8B 0D ? ? ? ? B2 ? E8 ? ? ? ? 48 8B 0D ? ? ? ? E8", 0x03FC72E3, 0x03DDD933), + SIG_ENTRY_NORMAL(LEA_ADDR, globals.on_gameplay_tag_tree_changed, "48 8D 0D ? ? ? ? 41 C6 84 24", 0x03FC467D, 0x03DDACCD), + SIG_ENTRY_NORMAL(DIRECT, static_load_object, "4C 89 4C 24 ? 48 89 54 24 ? 48 89 4C 24 ? 55 53 56 57 41 54 41 55 41 56 41 57 48 8B EC 48 83 EC ? 33 FF 49 8B F0", 0x02A14930, 0x02998AC0), + SIG_ENTRY_NORMAL(DIRECT, static_construct_object, "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 56 41 57 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 84 24 ? ? ? ? ? ? ? 48 8B D9", 0x02A1B9F0, 0x0299FB10), + SIG_ENTRY_NORMAL(DIRECT, create_default_object, "4C 8B DC 55 57 49 8D AB ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 48 83 B9 ? ? ? ? 00 48 8B F9 0F 85", 0x02896DE0, 0x0281B000), + SIG_ENTRY_NORMAL(DIRECT, static_load_class, "40 55 53 57 41 56 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 8B BD", 0x02A15D70, 0x02999EF0), + SIG_ENTRY_NORMAL(DIRECT, fname_construct, "48 89 5C 24 ? 57 48 83 EC ? 48 8B D9 48 89 54 24 ? 33 C9", 0x027EF960, 0x027758B0), + SIG_ENTRY_NORMAL(DIRECT, add_uobject_delete_listener, "48 89 5C 24 ? 57 48 83 EC ? 48 8D 0D ? ? ? ? 48 8B FA FF 15", 0x02A0CFF0, 0x02991170), + SIG_ENTRY_NORMAL(CALL_ARG, tarray_grow, "E8 ? ? ? ? 48 8B 05 ? ? ? ? 48 8D 0D ? ? ? ? ? ? ? ? 48 8B 5C 24 ? 48 83 C4 ? 5F 48 FF 25 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 40 53", 0x02A0D029, 0x029911A9), + SIG_ENTRY_NORMAL(CALL_ARG, tarray_shrink, "E8 ? ? ? ? 48 8D 0D ? ? ? ? 48 83 C4 ? 5B 48 FF 25 ? ? ? ? ? 8B 42", 0x02A0D107, 0x02991287), + SIG_ENTRY_NORMAL(DIRECT, get_mcast_sparse_delegate, "48 89 5C 24 ? 57 48 83 EC ? 48 8B F9 48 8B DA 48 8D 0D ? ? ? ? FF 15 ? ? ? ? 4C 8B C7", 0x02A06FF0, 0x0298B190), + SIG_ENTRY_NORMAL(DIRECT, fscript_map_add_pair, "40 55 56 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 45 ? 0F B6 41", 0x02A26830, 0x029AA910), + SIG_ENTRY_NORMAL(DIRECT, fscript_map_remove_pair, "48 89 5C 24 ? 48 89 74 24 ? 55 57 41 56 48 8B EC 48 83 EC ? 0F B6 41 ? 48 8B F2", 0x029EF0A0, 0x02972FE0), + SIG_ENTRY_NORMAL(DIRECT, generic_map_find, "48 89 5C 24 ? 48 89 74 24 ? 55 57 41 56 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 45 ? 4D 8B F1", 0x041C7390, 0x03FDEB30), + SIG_ENTRY_NORMAL(DIRECT, add_tag_table_row, "48 8B C4 55 57 48 8D 68 ? 48 81 EC ? ? ? ? 48 89 58 ? 48 89 70 ? 4C 89 60 ? 4C 89 68 ? 4C 89 78", 0x03FC7410, 0x03DDDA60), + SIG_ENTRY_NORMAL(DIRECT, tmulticast_delegate_broadcast, "40 56 48 83 EC ? FF 41 ? 48 8B F1 8B 49 ? 48 89 5C 24", 0x00E6BEA0, 0x00E1FD30), + SIG_ENTRY_NORMAL(DIRECT, request_gameplay_tag, "40 53 55 56 57 48 83 EC ? 8B 81", 0x03FC8DC0, 0x03DDF410), +}; + +static signature_t g_input_sigs[] = { + SIG_ENTRY_HOOKED(DIRECT, process_mouse_move_event, "40 55 53 57 41 54 41 55 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 33 FF", 0x02B54DD0, 0x02ADF210), + SIG_ENTRY_HOOKED(DIRECT, process_mouse_button_down_event, "40 55 53 57 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? FF 81", 0x02B51260, 0x02ADB6A0), + SIG_ENTRY_HOOKED(CALL_ARG, process_mouse_button_up_event, "E8 ? ? ? ? 48 8B 9C 24 ? ? ? ? 84 C0 4C 8B B4 24", 0x02B59791, 0x02AE3BD1), + SIG_ENTRY_HOOKED(DIRECT, process_mouse_button_double_click_event, "40 55 53 56 57 41 55 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? ? ? ? 4D 8B F0", 0x02B53870, 0x02ADDCB0), + SIG_ENTRY_HOOKED(DIRECT, process_mouse_wheel_or_gesture_event, "40 55 56 57 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 84 24 ? ? ? ? 49 8B F8", 0x02B543D0, 0x02ADE810), + SIG_ENTRY_HOOKED(DIRECT, process_key_char_event, "48 89 5C 24 ? 55 56 57 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? FF 81 ? ? ? ? 45 33 FF", 0x02B4F960, 0x02AD9DA0), + SIG_ENTRY_HOOKED(DIRECT, process_key_down_event, "4C 8B DC 55 53 57 41 56 49 8D AB ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 49 89 73 ? 48 8B F9", 0x02B4FE70, 0x02ADA2B0), + SIG_ENTRY_HOOKED(DIRECT, process_key_up_event, "40 55 56 41 55 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? FF 81", 0x02B505E0, 0x02ADAA20), + SIG_ENTRY_HOOKED(DIRECT, process_analog_input_event, "4C 8B DC 55 53 57 41 57 49 8D AB ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? FF 81", 0x02B50A80, 0x02ADAEC0), + SIG_ENTRY_HOOKED(DIRECT, process_app_activation_event, "48 89 6C 24 ? 56 48 83 EC ? 80 3D ? ? ? ? 00 48 8D B1", 0x02B57D70, 0x02AE21B0), +}; + +static struct { + signature_t *sigs; + int count; +} g_sig_table[SIG_KIND_MAX] = { + [SIG_KIND_EARLY] = {g_early_sigs, COUNTOF(g_early_sigs)}, + [SIG_KIND_MAIN] = {g_main_sigs, COUNTOF(g_main_sigs) }, + [SIG_KIND_INPUT] = {g_input_sigs, COUNTOF(g_input_sigs)}, +}; + +static void +scan_signatures(signature_t *sigs, int count) { - struct { - sigscan_entry_t entry; - // for hooking - LPVOID detour; - LPVOID *original; - } sig_table[] = { - SIG_ENTRY_NORMAL(DIRECT, get_pak_signkey_helper, "48 83 EC ? E8 ? ? ? ? 83 78 ? 00", 0x0274C4D0, 0x026D2420), - SIG_ENTRY_NORMAL(DIRECT, setup_signed_pak_reader, "48 89 5C 24 ?? 48 89 6C 24 ?? 48 89 74 24 ?? 57 41 54 41 55 41 56 41 57 48 83 EC ?? 80 B9 ?? ?? ?? ?? 00 4D 8B E0", 0x0400D950, 0x03E23700), - SIG_ENTRY_HOOKED(CALL_ARG, pak_file_mount, "E8 ? ? ? ? 84 C0 74 ? 41 FF C5 FF C6", 0x0401689D, 0x03E2C69D), - SIG_ENTRY_HOOKED(DIRECT, io_dispatcher_mount, "40 53 41 55 41 57 48 81 EC ? ? ? ? 48 8B 05", 0x02745980, 0x026CB8D0), - SIG_ENTRY_HOOKED(DIRECT, pak_precacher_add_new_block, "48 89 5C 24 ?? 48 89 74 24 ?? 48 89 7C 24 ?? 55 41 54 41 55 41 56 41 57 48 8D AC 24 ?? ?? ?? ?? 48 81 EC ?? ?? ?? ?? 48 8B 05 ?? ?? ?? ?? 48 33 C4 48 89 85 ?? ?? ?? ?? 45 33 C9", 0x00000000, 0x03E1CF90), - SIG_ENTRY_HOOKED(DIRECT, game_engine_tick, "48 8B C4 44 88 40 ? 48 89 48 ? 55 53", 0x0434A5A0, 0x04162180), - SIG_ENTRY_HOOKED(DIRECT, process_event, "40 55 56 57 41 54 41 55 41 56 41 57 48 81 EC ? ? ? ? 48 8D 6C 24 ? 48 89 9D ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C5 48 89 85 ? ? ? ? 8B 41", 0x029FDCD0, 0x02981E80), - SIG_ENTRY_HOOKED(DIRECT, ufunction_invoke, "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 48 89 7C 24 ? 41 56 48 83 EC ? 48 8B 59 ? 4D 8B F1", 0x0289B0B0, 0x0281F2B0), - SIG_ENTRY_HOOKED(DIRECT, process_mouse_move_event, "40 55 53 57 41 54 41 55 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 33 FF", 0x02B54DD0, 0x02ADF210), - SIG_ENTRY_HOOKED(DIRECT, process_mouse_button_down_event, "40 55 53 57 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? FF 81", 0x02B51260, 0x02ADB6A0), - SIG_ENTRY_HOOKED(CALL_ARG, process_mouse_button_up_event, "E8 ? ? ? ? 48 8B 9C 24 ? ? ? ? 84 C0 4C 8B B4 24", 0x02B59791, 0x02AE3BD1), - SIG_ENTRY_HOOKED(DIRECT, process_mouse_button_double_click_event, "40 55 53 56 57 41 55 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? ? ? ? 4D 8B F0", 0x02B53870, 0x02ADDCB0), - SIG_ENTRY_HOOKED(DIRECT, process_mouse_wheel_or_gesture_event, "40 55 56 57 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 84 24 ? ? ? ? 49 8B F8", 0x02B543D0, 0x02ADE810), - SIG_ENTRY_HOOKED(DIRECT, process_key_char_event, "48 89 5C 24 ? 55 56 57 41 56 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? FF 81 ? ? ? ? 45 33 FF", 0x02B4F960, 0x02AD9DA0), - SIG_ENTRY_HOOKED(DIRECT, process_key_down_event, "4C 8B DC 55 53 57 41 56 49 8D AB ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 49 89 73 ? 48 8B F9", 0x02B4FE70, 0x02ADA2B0), - SIG_ENTRY_HOOKED(DIRECT, process_key_up_event, "40 55 56 41 55 41 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? FF 81", 0x02B505E0, 0x02ADAA20), - SIG_ENTRY_HOOKED(DIRECT, process_analog_input_event, "4C 8B DC 55 53 57 41 57 49 8D AB ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? FF 81", 0x02B50A80, 0x02ADAEC0), - SIG_ENTRY_HOOKED(DIRECT, process_app_activation_event, "48 89 6C 24 ? 56 48 83 EC ? 80 3D ? ? ? ? 00 48 8D B1", 0x02B57D70, 0x02AE21B0), - SIG_ENTRY_HOOKED(DIRECT, d3d12_viewport_init, "48 89 74 24 ? 48 89 7C 24 ? 55 41 54 41 55 41 56 41 57 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 45 ? 4C 8B 69", 0x02D44E20, 0x02C98770), - SIG_ENTRY_NORMAL(LEA_ADDR, globals.uobjects, "4C 8D 1D ? ? ? ? 89 41", 0x00FDCEF0, 0x00FA15D0), - SIG_ENTRY_NORMAL(LEA_ADDR, globals.name_pool, "48 8D 05 ? ? ? ? EB ? 48 8D 0D ? ? ? ? E8 ? ? ? ? C6 05 ? ? ? ? ? 8B 80 ? ? ? ? 83 8B", 0x042D26E4, 0x040E9DA4), - SIG_ENTRY_NORMAL(MOV64_ADDR, globals.gworld_ptr, "48 8B 05 ? ? ? ? 4C 8D 44 24 ? 48 8D 54 24 ? 48 89 44 24 ? 48 8B CB", 0x047AD35A, 0x045C489A), - SIG_ENTRY_NORMAL(LEA_ADDR, globals.natives, "48 8D 05 ? ? ? ? B9 ? ? ? ? 0F 1F 40 ? 66 0F 1F 84 00 ? ? ? ? ? ? ? 48 89 50 ? 48 89 50 ? 48 8D 40 ? 48 89 50 ? 48 89 50 ? 48 89 50 ? 48 89 50 ? 48 89 50 ? 48 83 E9 ? 75 ? 8B 05 ? ? ? ? 33 C9", 0x00A062C7, 0x009BC917), - SIG_ENTRY_NORMAL(LEA_ADDR, globals.uobject_vtable, "48 8D 05 ? ? ? ? ? ? ? 48 8B FA 48 8B D9", 0x02A1A6E1, 0x0299E821), - SIG_ENTRY_NORMAL(MOV64_ADDR, globals.gameplay_tags_manager_ptr, "48 8B 0D ? ? ? ? B2 ? E8 ? ? ? ? 48 8B 0D ? ? ? ? E8", 0x03FC72E3, 0x03DDD933), - SIG_ENTRY_NORMAL(LEA_ADDR, globals.on_gameplay_tag_tree_changed, "48 8D 0D ? ? ? ? 41 C6 84 24", 0x03FC467D, 0x03DDACCD), - SIG_ENTRY_NORMAL(DIRECT, static_load_object, "4C 89 4C 24 ? 48 89 54 24 ? 48 89 4C 24 ? 55 53 56 57 41 54 41 55 41 56 41 57 48 8B EC 48 83 EC ? 33 FF 49 8B F0", 0x02A14930, 0x02998AC0), - SIG_ENTRY_NORMAL(DIRECT, static_construct_object, "48 89 5C 24 ? 48 89 6C 24 ? 48 89 74 24 ? 57 41 56 41 57 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 84 24 ? ? ? ? ? ? ? 48 8B D9", 0x02A1B9F0, 0x0299FB10), - SIG_ENTRY_NORMAL(DIRECT, create_default_object, "4C 8B DC 55 57 49 8D AB ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 48 83 B9 ? ? ? ? 00 48 8B F9 0F 85", 0x02896DE0, 0x0281B000), - SIG_ENTRY_NORMAL(DIRECT, static_load_class, "40 55 53 57 41 56 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 85 ? ? ? ? 8B BD", 0x02A15D70, 0x02999EF0), - SIG_ENTRY_NORMAL(DIRECT, fname_construct, "48 89 5C 24 ? 57 48 83 EC ? 48 8B D9 48 89 54 24 ? 33 C9", 0x027EF960, 0x027758B0), - SIG_ENTRY_NORMAL(DIRECT, add_uobject_delete_listener, "48 89 5C 24 ? 57 48 83 EC ? 48 8D 0D ? ? ? ? 48 8B FA FF 15", 0x02A0CFF0, 0x02991170), - SIG_ENTRY_NORMAL(CALL_ARG, tarray_grow, "E8 ? ? ? ? 48 8B 05 ? ? ? ? 48 8D 0D ? ? ? ? ? ? ? ? 48 8B 5C 24 ? 48 83 C4 ? 5F 48 FF 25 ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 40 53", 0x02A0D029, 0x029911A9), - SIG_ENTRY_NORMAL(CALL_ARG, tarray_shrink, "E8 ? ? ? ? 48 8D 0D ? ? ? ? 48 83 C4 ? 5B 48 FF 25 ? ? ? ? ? 8B 42", 0x02A0D107, 0x02991287), - SIG_ENTRY_NORMAL(DIRECT, get_mcast_sparse_delegate, "48 89 5C 24 ? 57 48 83 EC ? 48 8B F9 48 8B DA 48 8D 0D ? ? ? ? FF 15 ? ? ? ? 4C 8B C7", 0x02A06FF0, 0x0298B190), - SIG_ENTRY_NORMAL(DIRECT, fscript_map_add_pair, "40 55 56 57 48 8D AC 24 ? ? ? ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 45 ? 0F B6 41", 0x02A26830, 0x029AA910), - SIG_ENTRY_NORMAL(DIRECT, fscript_map_remove_pair, "48 89 5C 24 ? 48 89 74 24 ? 55 57 41 56 48 8B EC 48 83 EC ? 0F B6 41 ? 48 8B F2", 0x029EF0A0, 0x02972FE0), - SIG_ENTRY_NORMAL(DIRECT, generic_map_find, "48 89 5C 24 ? 48 89 74 24 ? 55 57 41 56 48 8D 6C 24 ? 48 81 EC ? ? ? ? 48 8B 05 ? ? ? ? 48 33 C4 48 89 45 ? 4D 8B F1", 0x041C7390, 0x03FDEB30), - SIG_ENTRY_NORMAL(DIRECT, add_tag_table_row, "48 8B C4 55 57 48 8D 68 ? 48 81 EC ? ? ? ? 48 89 58 ? 48 89 70 ? 4C 89 60 ? 4C 89 68 ? 4C 89 78", 0x03FC7410, 0x03DDDA60), - SIG_ENTRY_NORMAL(DIRECT, tmulticast_delegate_broadcast, "40 56 48 83 EC ? FF 41 ? 48 8B F1 8B 49 ? 48 89 5C 24", 0x00E6BEA0, 0x00E1FD30), - SIG_ENTRY_NORMAL(DIRECT, request_gameplay_tag, "40 53 55 56 57 48 83 EC ? 8B 81", 0x03FC8DC0, 0x03DDF410), - }; + for (int i = 0; i < count; ++i) { + signature_t *s = &sigs[i]; + sigscan_err_t rc = sigscan_scan_entry(globals.user_spans, globals.num_user_spans, globals.user_module_base, &s->entry); + if (rc != SIG_ERR_OK) { + if (rc == SIG_ERR_INVALID_PATTERN) { + LOG_ERROR("%s: %s: '%s'", s->entry.name, sig_err_to_str(rc), s->entry.pattern); + } else { + LOG_ERROR("%s: %s", s->entry.name, sig_err_to_str(rc)); + } + continue; + } + + if (s->patch) { + s->patch(&s->entry); + } + + s->resolved = true; + } +} + +static void +create_hooks(signature_t *sigs, int count) +{ + for (int i = 0; i < count; ++i) { + signature_t *s = &sigs[i]; + if (s->resolved && s->detour) { + if (hook_chain_create(&s->hook, s->entry.addr, s->detour, s->original)) { + s->hook_created = true; + } else { + LOG_ERROR("%s: failed to create hook", s->entry.name); + } + } + } +} +static void +enable_hooks(signature_t *sigs, int count) +{ + for (int i = 0; i < count; ++i) { + signature_t *s = &sigs[i]; + if (s->hook_created && !s->hook_enabled) { + if (hook_chain_enable(&s->hook)) { + s->hook_enabled = true; + } else { + LOG_ERROR("%s: failed to enable hook", s->entry.name); + } + } + } +} + +void +signatures_init(void) +{ void *user_module = (void *)GetModuleHandleA(NULL); if (!user_module) { LOG_ERROR("signatures: failed to get the main module base address"); - return false; + return; } globals.num_user_spans = sigscan_build_exec_spans(globals.user_spans, COUNTOF(globals.user_spans), user_module); globals.user_module_base = user_module; if (globals.num_user_spans <= 0 || !sigscan_snapshot_exec_spans(globals.user_spans, globals.num_user_spans, &globals.perm)) { LOG_ERROR("signatures: failed to snapshot executable sections"); - return false; + return; } - if (COUNTOF(sig_table) > COUNTOF(g_signature_hooks)) { - LOG_ERROR("signatures: hook storage is too small"); - return false; + for (int i = 0; i < SIG_KIND_MAX; ++i) { + scan_signatures(g_sig_table[i].sigs, g_sig_table[i].count); } - int resolved = 0; - for (int i = 0; i < COUNTOF(sig_table); ++i) { - sigscan_entry_t *entry = &sig_table[i].entry; - LPVOID detour = sig_table[i].detour; - LPVOID *original = sig_table[i].original; + for (int i = 0; i < SIG_KIND_MAX; ++i) { + create_hooks(g_sig_table[i].sigs, g_sig_table[i].count); + } - sigscan_err_t rc = sigscan_scan_entry(globals.user_spans, globals.num_user_spans, globals.user_module_base, entry); - if (rc < 0) { - if (rc == SIG_ERR_INVALID_PATTERN) { - LOG_ERROR("%s: %s: '%s'", entry->name, sig_err_to_str(rc), entry->pattern); - } else { - LOG_ERROR("%s: %s", entry->name, sig_err_to_str(rc)); - } - continue; - } + enable_hooks(g_sig_table[SIG_KIND_EARLY].sigs, g_sig_table[SIG_KIND_EARLY].count); + enable_hooks(g_sig_table[SIG_KIND_MAIN].sigs, g_sig_table[SIG_KIND_MAIN].count); - if (i == 0) { - patch_get_pak_signkey_helper(entry); - } + int max_resolved = 0; + int max_hooked = 0; + int num_resolved = 0; + int num_hooked = 0; - if (i == 1) { - patch_setup_signed_pak_reader(entry); - } + for (int i = 0; i < SIG_KIND_MAX; ++i) { + signature_t *sigs = g_sig_table[i].sigs; + for (int j = 0; j < g_sig_table[i].count; ++i) { + signature_t *s = &sigs[j]; - if (entry->addr && detour && original) { - if (!hook_chain_create(&g_signature_hooks[i], entry->addr, detour, original)) { - LOG_ERROR("%s: failed to create hook chain", entry->name); - continue; + max_resolved += 1; + if (s->resolved) { + num_resolved += 1; } - if (!hook_chain_enable(&g_signature_hooks[i])) { - hook_chain_remove(&g_signature_hooks[i]); - LOG_ERROR("%s: failed to enable hook chain", entry->name); - continue; - } + if (s->detour && s->original) { + max_hooked += 1; - LOG_DEBUG("[hooked] %p: %s", entry->addr, entry->name); - } else { - LOG_DEBUG("[normal] %p: %s", entry->addr, entry->name); + if (s->hook_created) { + num_hooked += 1; + } + } } - - resolved += 1; } - if (resolved != COUNTOF(sig_table)) { - LOG_ERROR("signatures: failed to resolve all entries (%d/%d)", resolved, COUNTOF(sig_table)); - return false; + if (num_resolved != max_resolved) { + LOG_ERROR("signatures: failed to resolve all entries (%d/%d)", num_resolved, max_resolved); + } else if (num_hooked != max_hooked) { + LOG_ERROR("signatures: failed to hook all entries (%d/%d)", num_hooked, max_hooked); + } else { + LOG_INFO("signatures: entries resolved (%d/%d), entries hooked (%d/%d)", num_resolved, max_resolved, num_hooked, max_hooked); } +} - LOG_INFO("signatures: resolved all entries (%d/%d)", resolved, COUNTOF(sig_table)); - return true; +void +enable_input_hooks(void) +{ + enable_hooks(g_sig_table[SIG_KIND_INPUT].sigs, g_sig_table[SIG_KIND_INPUT].count); } + From e9d4534d88852d18c86d17615a1af3ffa6e413a4 Mon Sep 17 00:00:00 2001 From: akmubi Date: Tue, 25 Aug 2026 18:42:28 +0500 Subject: [PATCH 2/8] *breaking change*: refactor input/keybind system, update mod API (v1.1.0) --- include/globals.h | 7 +- include/input.h | 35 +- include/mod_host.h | 16 +- include/unreal.h | 2 + mod/mod_api.h | 31 +- mod/mod_sdk.c | 57 ++- mod/mod_sdk.h | 8 +- src/builtin/ufunction_tracer.c | 2 +- src/builtin/uobject_search.c | 2 +- src/input.c | 897 +++++++++++++++++---------------- src/main.c | 148 ++++-- src/mod_host.c | 13 +- src/ui_manager.c | 6 +- 13 files changed, 685 insertions(+), 539 deletions(-) diff --git a/include/globals.h b/include/globals.h index f735b71..e43a637 100644 --- a/include/globals.h +++ b/include/globals.h @@ -2,6 +2,7 @@ #define GLOBALS_H #include "arena.h" +#include "input.h" #include "mod_manager.h" #include "sigscan.h" #include "ui_console.h" @@ -15,7 +16,6 @@ struct globals_s { arena_t perm; void *module; bool engine_inited; - bool input_inited; uint32_t game_thread_id; str_t game_dir; @@ -45,8 +45,9 @@ struct globals_s { uint64_t hwnd; /* input */ - bool prev_keys_down[INPUT_KEY_MAX]; - bool keys_down[INPUT_KEY_MAX]; + input_key_state_t keys[INPUT_KEY_MAX]; + bool consumed_down[INPUT_KEY_MAX]; + float wheel_delta; /* mod manager */ mod_manager_t mod_manager; diff --git a/include/input.h b/include/input.h index 8f12be3..6a3f9c5 100644 --- a/include/input.h +++ b/include/input.h @@ -118,6 +118,11 @@ enum { INPUT_KEY_LEFT_ALT, INPUT_KEY_RIGHT_ALT, + /* keyboard: virtual modifiers (either left or right) */ + INPUT_KEY_SHIFT, + INPUT_KEY_CTRL, + INPUT_KEY_ALT, + /* keyboard: common */ INPUT_KEY_SPACE, INPUT_KEY_ENTER, @@ -209,6 +214,13 @@ struct input_event_s { float analog_value; // ANALOG only (trigger value range: 0.0 .. 1.0, stick value range: -1.0 .. 1.0) }; +typedef struct input_key_state_s input_key_state_t; +struct input_key_state_s { + bool down; + bool pressed; + bool released; +}; + #define KEYBIND_MAX_KEYS (10) #define KEYBIND_NULL (keybind_t){.keys = {0}, .count = 0} @@ -227,9 +239,18 @@ input_key_kind_t input_key_from_str(str_t name); bool -input_key_is_down(input_key_kind_t kind); +input_key_is_down(input_key_kind_t key); +bool +input_key_is_pressed(input_key_kind_t key); bool -input_key_was_down(input_key_kind_t kind); +input_key_is_released(input_key_kind_t key); +bool +input_key_is_bindable(input_key_kind_t key); + +void +input_key_lost_focus(void); +void +input_key_clear_transient_state(void); str_t input_key_to_str(input_key_kind_t key); @@ -273,8 +294,6 @@ keybind_clear(keybind_t *bind); bool keybind_is_down(keybind_t bind); bool -keybind_was_down(keybind_t bind); -bool keybind_is_pressed(keybind_t bind); bool keybind_is_released(keybind_t bind); @@ -285,12 +304,6 @@ keybind_str_is_down(str_t keybind_str) return keybind_is_down(keybind_parse(keybind_str, KEYBIND_NULL)); } -static inline bool -keybind_str_was_down(str_t keybind_str) -{ - return keybind_was_down(keybind_parse(keybind_str, KEYBIND_NULL)); -} - static inline bool keybind_str_is_pressed(str_t keybind_str) { @@ -305,6 +318,6 @@ keybind_str_is_released(str_t keybind_str) /* NOTE: used for input events */ bool -input_event_covers_keybind(input_event_t *ev, keybind_t bind); +keybind_activated_by_event(keybind_t bind, input_event_t *ev); #endif /* INPUT_H */ diff --git a/include/mod_host.h b/include/mod_host.h index caa95ca..a66f721 100644 --- a/include/mod_host.h +++ b/include/mod_host.h @@ -11,7 +11,7 @@ #include "mod_manager.h" -#define MOD_HOST_ABI_VERSION MAKE_VERSION(1, 0, 1) +#define MOD_HOST_ABI_VERSION MAKE_VERSION(1, 1, 0) typedef struct host_tmp_arena_s host_tmp_arena_t; struct host_tmp_arena_s { @@ -91,7 +91,8 @@ struct mod_host_api_s { uint64_t (MOD_CALL *keybind_utf8_len) (keybind_t bind); uint64_t (MOD_CALL *keybind_utf8_write) (keybind_t bind, void *buf, uint64_t cap); bool (MOD_CALL *keybind_is_down) (keybind_t bind); - bool (MOD_CALL *keybind_was_down) (keybind_t bind); + bool (MOD_CALL *keybind_is_pressed) (keybind_t bind); + bool (MOD_CALL *keybind_is_released) (keybind_t bind); /* UOBJECT CREATE/DELETE LISTENERS */ @@ -119,16 +120,7 @@ struct mod_host_api_s { uint64_t (MOD_CALL *get_frame_counter)(void); float (MOD_CALL *get_fps)(void); }; -STATIC_ASSERT(offsetof(mod_host_api_t, map_add) == 0x1e8, "invalid host API offset"); -STATIC_ASSERT(offsetof(mod_host_api_t, map_remove) == 0x1f0, "invalid host API offset"); -STATIC_ASSERT(offsetof(mod_host_api_t, map_find) == 0x1f8, "invalid host API offset"); -STATIC_ASSERT(offsetof(mod_host_api_t, static_load_object) == 0x200, "invalid host API offset"); -STATIC_ASSERT(offsetof(mod_host_api_t, static_construct_object) == 0x208, "invalid host API offset"); -STATIC_ASSERT(offsetof(mod_host_api_t, add_gameplay_tag) == 0x210, "invalid host API offset"); -STATIC_ASSERT(offsetof(mod_host_api_t, check_gameplay_tag_exists) == 0x218, "invalid host API offset"); -STATIC_ASSERT(offsetof(mod_host_api_t, get_frame_counter) == 0x220, "invalid host API offset"); -STATIC_ASSERT(offsetof(mod_host_api_t, get_fps) == 0x228, "invalid host API offset"); -STATIC_ASSERT(sizeof(mod_host_api_t) == 0x230, "invalid host API size"); +STATIC_ASSERT(sizeof(mod_host_api_t) == 0x238, "invalid host API size"); const mod_host_api_t * mod_host_api_get(void); diff --git a/include/unreal.h b/include/unreal.h index 2f60592..02ba81b 100644 --- a/include/unreal.h +++ b/include/unreal.h @@ -5,6 +5,8 @@ #include "str.h" #include "types.h" +#define FNAME_NONE (fname_t){0} + #define FNAME_MAX_BLOCK_BITS 13 #define FNAME_BLOCK_OFFSET_BITS 16 #define FNAME_MAX_BLOCKS (1 << FNAME_MAX_BLOCK_BITS) diff --git a/mod/mod_api.h b/mod/mod_api.h index 0dffc11..e4eccc5 100644 --- a/mod/mod_api.h +++ b/mod/mod_api.h @@ -144,6 +144,11 @@ enum { INPUT_KEY_LEFT_ALT, INPUT_KEY_RIGHT_ALT, + /* keyboard: virtual modifiers (either left or right) */ + INPUT_KEY_SHIFT, + INPUT_KEY_CTRL, + INPUT_KEY_ALT, + /* keyboard: common */ INPUT_KEY_SPACE, INPUT_KEY_ENTER, @@ -191,10 +196,10 @@ enum { INPUT_KEY_MOUSE_WHEEL_DOWN, /* gamepad: buttons */ - INPUT_KEY_GAMEPAD_FACE_BOTTOM, // A / cross - INPUT_KEY_GAMEPAD_FACE_RIGHT, // B / circle - INPUT_KEY_GAMEPAD_FACE_LEFT, // X / square - INPUT_KEY_GAMEPAD_FACE_TOP, // Y / triangle + INPUT_KEY_GAMEPAD_FACE_BOTTOM, // A / cross + INPUT_KEY_GAMEPAD_FACE_RIGHT, // B / circle + INPUT_KEY_GAMEPAD_FACE_LEFT, // X / square + INPUT_KEY_GAMEPAD_FACE_TOP, // Y / triangle INPUT_KEY_GAMEPAD_LB, INPUT_KEY_GAMEPAD_RB, INPUT_KEY_GAMEPAD_LT, @@ -240,7 +245,7 @@ struct input_event_s { #ifdef __cplusplus # define KEYBIND_NULL (keybind_t){} #else -# define KEYBIND_NULL (keybind_t){0} +# define KEYBIND_NULL (keybind_t){.keys = {0}, .count = 0} #endif typedef struct keybind_s keybind_t; @@ -377,7 +382,7 @@ typedef void (MOD_CALL *mod_deinit_fn_t)(mod_t m); typedef void (MOD_CALL *mod_cmd_fn_t)(mod_t m, str_t name, str_t args, void *user); -#define MOD_ABI_VERSION MAKE_VERSION(1, 0, 1) +#define MOD_ABI_VERSION MAKE_VERSION(1, 1, 0) typedef struct mod_api_s mod_api_t; struct mod_api_s { @@ -468,7 +473,8 @@ struct mod_host_api_s { uint64_t (MOD_CALL *keybind_utf8_len) (keybind_t bind); uint64_t (MOD_CALL *keybind_utf8_write) (keybind_t bind, void *buf, uint64_t cap); bool (MOD_CALL *keybind_is_down) (keybind_t bind); - bool (MOD_CALL *keybind_was_down) (keybind_t bind); + bool (MOD_CALL *keybind_is_pressed) (keybind_t bind); + bool (MOD_CALL *keybind_is_released) (keybind_t bind); /* UOBJECT CREATE/DELETE LISTENERS */ @@ -496,16 +502,7 @@ struct mod_host_api_s { uint64_t (MOD_CALL *get_frame_counter)(void); float (MOD_CALL *get_fps)(void); }; -MOD_STATIC_ASSERT(offsetof(mod_host_api_t, map_add) == 0x1e8, "invalid host API offset"); -MOD_STATIC_ASSERT(offsetof(mod_host_api_t, map_remove) == 0x1f0, "invalid host API offset"); -MOD_STATIC_ASSERT(offsetof(mod_host_api_t, map_find) == 0x1f8, "invalid host API offset"); -MOD_STATIC_ASSERT(offsetof(mod_host_api_t, static_load_object) == 0x200, "invalid host API offset"); -MOD_STATIC_ASSERT(offsetof(mod_host_api_t, static_construct_object) == 0x208, "invalid host API offset"); -MOD_STATIC_ASSERT(offsetof(mod_host_api_t, add_gameplay_tag) == 0x210, "invalid host API offset"); -MOD_STATIC_ASSERT(offsetof(mod_host_api_t, check_gameplay_tag_exists) == 0x218, "invalid host API offset"); -MOD_STATIC_ASSERT(offsetof(mod_host_api_t, get_frame_counter) == 0x220, "invalid host API offset"); -MOD_STATIC_ASSERT(offsetof(mod_host_api_t, get_fps) == 0x228, "invalid host API offset"); -MOD_STATIC_ASSERT(sizeof(mod_host_api_t) == 0x230, "invalid host API size"); +MOD_STATIC_ASSERT(sizeof(mod_host_api_t) == 0x238, "invalid host API size"); #ifdef __cplusplus } diff --git a/mod/mod_sdk.c b/mod/mod_sdk.c index 99c95ef..c539ca2 100644 --- a/mod/mod_sdk.c +++ b/mod/mod_sdk.c @@ -562,23 +562,18 @@ mod_keybind_is_down(keybind_t bind) return (host) ? host->keybind_is_down(bind) : false; } -bool -mod_keybind_was_down(keybind_t bind) -{ - const mod_host_api_t *host = mod_sdk_host(); - return (host) ? host->keybind_was_down(bind) : false; -} - bool mod_keybind_is_pressed(keybind_t bind) { - return mod_keybind_is_down(bind) && !mod_keybind_was_down(bind); + const mod_host_api_t *host = mod_sdk_host(); + return (host) ? host->keybind_is_pressed(bind) : false; } bool mod_keybind_is_released(keybind_t bind) { - return !mod_keybind_is_down(bind) && mod_keybind_was_down(bind); + const mod_host_api_t *host = mod_sdk_host(); + return (host) ? host->keybind_is_released(bind) : false; } bool @@ -591,16 +586,6 @@ mod_keybind_str_is_down(str_t keybind_str) return mod_keybind_is_down(bind); } -bool -mod_keybind_str_was_down(str_t keybind_str) -{ - keybind_t bind = {0}; - if (!mod_keybind_parse(keybind_str, &bind)) { - return false; - } - return mod_keybind_was_down(bind); -} - bool mod_keybind_str_is_pressed(str_t keybind_str) { @@ -621,17 +606,39 @@ mod_keybind_str_is_released(str_t keybind_str) return mod_keybind_is_released(bind); } +static bool +mod_keybind_key_matches_event(input_key_kind_t bind_key, input_key_kind_t event_key) +{ + switch (bind_key) { + case INPUT_KEY_CTRL: return event_key == INPUT_KEY_LEFT_CTRL || event_key == INPUT_KEY_RIGHT_CTRL; + case INPUT_KEY_SHIFT: return event_key == INPUT_KEY_LEFT_SHIFT || event_key == INPUT_KEY_RIGHT_SHIFT; + case INPUT_KEY_ALT: return event_key == INPUT_KEY_LEFT_ALT || event_key == INPUT_KEY_RIGHT_ALT; + default: return bind_key == event_key; + } +} + bool -mod_input_event_covers_keybind(input_event_t *ev, keybind_t bind) +mod_keybind_activated_by_event(keybind_t bind, input_event_t *ev) { - bool has_event_key = false; + if (ev->kind != INPUT_EVENT_KEY_DOWN && + ev->kind != INPUT_EVENT_MOUSE_DOWN && + ev->kind != INPUT_EVENT_MOUSE_DBLCLICK) { + return false; + } + + if (ev->is_repeat) { + return false; + } + + bool involved = false; for (int i = 0; i < bind.count; ++i) { - if (bind.keys[i] == ev->key) { - has_event_key = true; + if (mod_keybind_key_matches_event(bind.keys[i], ev->key)) { + involved = true; break; } } - return has_event_key; + + return involved && mod_keybind_is_down(bind); } /* ========================================== HOOKING / SIGNATURE SCANNING ========================================== */ @@ -2956,4 +2963,4 @@ ini_split_lines(mod_arena_t arena, str_t text) mod_scratch_end(tmp); return out_array; -} \ No newline at end of file +} diff --git a/mod/mod_sdk.h b/mod/mod_sdk.h index 4961b40..1dcfbc4 100644 --- a/mod/mod_sdk.h +++ b/mod/mod_sdk.h @@ -335,9 +335,6 @@ mod_keybind_to_str(keybind_t bind, mod_arena_t arena); /* Returns whether the keybind is down in the current input state. */ bool mod_keybind_is_down(keybind_t bind); -/* Returns whether the keybind was down in the previous input state. */ -bool -mod_keybind_was_down(keybind_t bind); /* Returns true on the frame a keybind changes from up to down. */ bool mod_keybind_is_pressed(keybind_t bind); @@ -347,9 +344,6 @@ mod_keybind_is_released(keybind_t bind); /* Parses a keybind string and checks its current state. Invalid strings return false. */ bool mod_keybind_str_is_down(str_t keybind_str); -/* Parses a keybind string and checks its previous state. Invalid strings return false. */ -bool -mod_keybind_str_was_down(str_t keybind_str); /* Parses a keybind string and checks for an up to down transition. */ bool mod_keybind_str_is_pressed(str_t keybind_str); @@ -358,7 +352,7 @@ bool mod_keybind_str_is_released(str_t keybind_str); /* Returns whether the event key appears in the bind. It does not check event kind or other key states. */ bool -mod_input_event_covers_keybind(input_event_t *ev, keybind_t bind); +mod_keybind_activated_by_event(keybind_t bind, input_event_t *ev); /* ========================================== HOOKING / SIGNATURE SCANNING ========================================== */ diff --git a/src/builtin/ufunction_tracer.c b/src/builtin/ufunction_tracer.c index 7d6f8b7..3bf3d61 100644 --- a/src/builtin/ufunction_tracer.c +++ b/src/builtin/ufunction_tracer.c @@ -1380,7 +1380,7 @@ trace_input(mod_handle_t h, input_event_t *ev) } keybind_t toggle = mod_cfg_get_keybind(&globals.mod_manager, tool->cfg.open_window_h); - if (input_event_covers_keybind(ev, toggle)) { + if (keybind_activated_by_event(toggle, ev)) { if (keybind_is_pressed(toggle)) { tool->ui.closed = !tool->ui.closed; } diff --git a/src/builtin/uobject_search.c b/src/builtin/uobject_search.c index 8d2e609..3c44ceb 100644 --- a/src/builtin/uobject_search.c +++ b/src/builtin/uobject_search.c @@ -622,7 +622,7 @@ tool_input(mod_handle_t h, input_event_t *ev) } keybind_t toggle_keybind = mod_cfg_get_keybind(&globals.mod_manager, tool->cfg.open_window_h); - if (input_event_covers_keybind(ev, toggle_keybind)) { + if (keybind_activated_by_event(toggle_keybind, ev)) { if (keybind_is_pressed(toggle_keybind)) { tool->ui.closed = !tool->ui.closed; } diff --git a/src/input.c b/src/input.c index 1706a66..fddbcbc 100644 --- a/src/input.c +++ b/src/input.c @@ -1,4 +1,5 @@ #include "input.h" +#include "unreal.h" #include "arena.h" #include "globals.h" @@ -12,226 +13,181 @@ typedef struct input_key_map_entry_s input_key_map_entry_t; struct input_key_map_entry_s { - str_t ue_name; // FKey name (e.g. "A", "Gamepad_RightTrigger") - uint32_t cmp_idx; // resolved at init, 0 - unresolved + str_t str; // FKey name (e.g. "A", "Gamepad_RightTrigger") + fname_t name; // resolved at init }; static input_key_map_entry_t g_key_map[INPUT_KEY_MAX] = { /* keyboard: letters */ - [INPUT_KEY_A] = {STR_CLIT("A"), 0}, - [INPUT_KEY_B] = {STR_CLIT("B"), 0}, - [INPUT_KEY_C] = {STR_CLIT("C"), 0}, - [INPUT_KEY_D] = {STR_CLIT("D"), 0}, - [INPUT_KEY_E] = {STR_CLIT("E"), 0}, - [INPUT_KEY_F] = {STR_CLIT("F"), 0}, - [INPUT_KEY_G] = {STR_CLIT("G"), 0}, - [INPUT_KEY_H] = {STR_CLIT("H"), 0}, - [INPUT_KEY_I] = {STR_CLIT("I"), 0}, - [INPUT_KEY_J] = {STR_CLIT("J"), 0}, - [INPUT_KEY_K] = {STR_CLIT("K"), 0}, - [INPUT_KEY_L] = {STR_CLIT("L"), 0}, - [INPUT_KEY_M] = {STR_CLIT("M"), 0}, - [INPUT_KEY_N] = {STR_CLIT("N"), 0}, - [INPUT_KEY_O] = {STR_CLIT("O"), 0}, - [INPUT_KEY_P] = {STR_CLIT("P"), 0}, - [INPUT_KEY_Q] = {STR_CLIT("Q"), 0}, - [INPUT_KEY_R] = {STR_CLIT("R"), 0}, - [INPUT_KEY_S] = {STR_CLIT("S"), 0}, - [INPUT_KEY_T] = {STR_CLIT("T"), 0}, - [INPUT_KEY_U] = {STR_CLIT("U"), 0}, - [INPUT_KEY_V] = {STR_CLIT("V"), 0}, - [INPUT_KEY_W] = {STR_CLIT("W"), 0}, - [INPUT_KEY_X] = {STR_CLIT("X"), 0}, - [INPUT_KEY_Y] = {STR_CLIT("Y"), 0}, - [INPUT_KEY_Z] = {STR_CLIT("Z"), 0}, + [INPUT_KEY_A] = {STR_CLIT("A"), FNAME_NONE}, + [INPUT_KEY_B] = {STR_CLIT("B"), FNAME_NONE}, + [INPUT_KEY_C] = {STR_CLIT("C"), FNAME_NONE}, + [INPUT_KEY_D] = {STR_CLIT("D"), FNAME_NONE}, + [INPUT_KEY_E] = {STR_CLIT("E"), FNAME_NONE}, + [INPUT_KEY_F] = {STR_CLIT("F"), FNAME_NONE}, + [INPUT_KEY_G] = {STR_CLIT("G"), FNAME_NONE}, + [INPUT_KEY_H] = {STR_CLIT("H"), FNAME_NONE}, + [INPUT_KEY_I] = {STR_CLIT("I"), FNAME_NONE}, + [INPUT_KEY_J] = {STR_CLIT("J"), FNAME_NONE}, + [INPUT_KEY_K] = {STR_CLIT("K"), FNAME_NONE}, + [INPUT_KEY_L] = {STR_CLIT("L"), FNAME_NONE}, + [INPUT_KEY_M] = {STR_CLIT("M"), FNAME_NONE}, + [INPUT_KEY_N] = {STR_CLIT("N"), FNAME_NONE}, + [INPUT_KEY_O] = {STR_CLIT("O"), FNAME_NONE}, + [INPUT_KEY_P] = {STR_CLIT("P"), FNAME_NONE}, + [INPUT_KEY_Q] = {STR_CLIT("Q"), FNAME_NONE}, + [INPUT_KEY_R] = {STR_CLIT("R"), FNAME_NONE}, + [INPUT_KEY_S] = {STR_CLIT("S"), FNAME_NONE}, + [INPUT_KEY_T] = {STR_CLIT("T"), FNAME_NONE}, + [INPUT_KEY_U] = {STR_CLIT("U"), FNAME_NONE}, + [INPUT_KEY_V] = {STR_CLIT("V"), FNAME_NONE}, + [INPUT_KEY_W] = {STR_CLIT("W"), FNAME_NONE}, + [INPUT_KEY_X] = {STR_CLIT("X"), FNAME_NONE}, + [INPUT_KEY_Y] = {STR_CLIT("Y"), FNAME_NONE}, + [INPUT_KEY_Z] = {STR_CLIT("Z"), FNAME_NONE}, /* keyboard: punctuation */ - [INPUT_KEY_COMMA] = {STR_CLIT("Comma"), 0}, - [INPUT_KEY_PERIOD] = {STR_CLIT("Period"), 0}, - [INPUT_KEY_SLASH] = {STR_CLIT("Slash"), 0}, - [INPUT_KEY_SEMICOLON] = {STR_CLIT("Semicolon"), 0}, - [INPUT_KEY_APOSTROPHE] = {STR_CLIT("Apostrophe"), 0}, - [INPUT_KEY_LEFT_BRACKET] = {STR_CLIT("LeftBracket"), 0}, - [INPUT_KEY_RIGHT_BRACKET] = {STR_CLIT("RightBracket"), 0}, - [INPUT_KEY_BACKSLASH] = {STR_CLIT("Backslash"), 0}, - [INPUT_KEY_HYPHEN] = {STR_CLIT("Hyphen"), 0}, - [INPUT_KEY_EQUALS] = {STR_CLIT("Equals"), 0}, - [INPUT_KEY_BACKTICK] = {STR_CLIT("Tilde"), 0}, + [INPUT_KEY_COMMA] = {STR_CLIT("Comma"), FNAME_NONE}, + [INPUT_KEY_PERIOD] = {STR_CLIT("Period"), FNAME_NONE}, + [INPUT_KEY_SLASH] = {STR_CLIT("Slash"), FNAME_NONE}, + [INPUT_KEY_SEMICOLON] = {STR_CLIT("Semicolon"), FNAME_NONE}, + [INPUT_KEY_APOSTROPHE] = {STR_CLIT("Apostrophe"), FNAME_NONE}, + [INPUT_KEY_LEFT_BRACKET] = {STR_CLIT("LeftBracket"), FNAME_NONE}, + [INPUT_KEY_RIGHT_BRACKET] = {STR_CLIT("RightBracket"), FNAME_NONE}, + [INPUT_KEY_BACKSLASH] = {STR_CLIT("Backslash"), FNAME_NONE}, + [INPUT_KEY_HYPHEN] = {STR_CLIT("Hyphen"), FNAME_NONE}, + [INPUT_KEY_EQUALS] = {STR_CLIT("Equals"), FNAME_NONE}, + [INPUT_KEY_BACKTICK] = {STR_CLIT("Tilde"), FNAME_NONE}, /* keyboard: digits */ - [INPUT_KEY_1] = {STR_CLIT("One"), 0}, - [INPUT_KEY_2] = {STR_CLIT("Two"), 0}, - [INPUT_KEY_3] = {STR_CLIT("Three"), 0}, - [INPUT_KEY_4] = {STR_CLIT("Four"), 0}, - [INPUT_KEY_5] = {STR_CLIT("Five"), 0}, - [INPUT_KEY_6] = {STR_CLIT("Six"), 0}, - [INPUT_KEY_7] = {STR_CLIT("Seven"), 0}, - [INPUT_KEY_8] = {STR_CLIT("Eight"), 0}, - [INPUT_KEY_9] = {STR_CLIT("Nine"), 0}, - [INPUT_KEY_0] = {STR_CLIT("Zero"), 0}, + [INPUT_KEY_1] = {STR_CLIT("One"), FNAME_NONE}, + [INPUT_KEY_2] = {STR_CLIT("Two"), FNAME_NONE}, + [INPUT_KEY_3] = {STR_CLIT("Three"), FNAME_NONE}, + [INPUT_KEY_4] = {STR_CLIT("Four"), FNAME_NONE}, + [INPUT_KEY_5] = {STR_CLIT("Five"), FNAME_NONE}, + [INPUT_KEY_6] = {STR_CLIT("Six"), FNAME_NONE}, + [INPUT_KEY_7] = {STR_CLIT("Seven"), FNAME_NONE}, + [INPUT_KEY_8] = {STR_CLIT("Eight"), FNAME_NONE}, + [INPUT_KEY_9] = {STR_CLIT("Nine"), FNAME_NONE}, + [INPUT_KEY_0] = {STR_CLIT("Zero"), FNAME_NONE}, /* keyboard: function */ - [INPUT_KEY_F1] = {STR_CLIT("F1"), 0}, - [INPUT_KEY_F2] = {STR_CLIT("F2"), 0}, - [INPUT_KEY_F3] = {STR_CLIT("F3"), 0}, - [INPUT_KEY_F4] = {STR_CLIT("F4"), 0}, - [INPUT_KEY_F5] = {STR_CLIT("F5"), 0}, - [INPUT_KEY_F6] = {STR_CLIT("F6"), 0}, - [INPUT_KEY_F7] = {STR_CLIT("F7"), 0}, - [INPUT_KEY_F8] = {STR_CLIT("F8"), 0}, - [INPUT_KEY_F9] = {STR_CLIT("F9"), 0}, - [INPUT_KEY_F10] = {STR_CLIT("F10"), 0}, - [INPUT_KEY_F11] = {STR_CLIT("F11"), 0}, - [INPUT_KEY_F12] = {STR_CLIT("F12"), 0}, + [INPUT_KEY_F1] = {STR_CLIT("F1"), FNAME_NONE}, + [INPUT_KEY_F2] = {STR_CLIT("F2"), FNAME_NONE}, + [INPUT_KEY_F3] = {STR_CLIT("F3"), FNAME_NONE}, + [INPUT_KEY_F4] = {STR_CLIT("F4"), FNAME_NONE}, + [INPUT_KEY_F5] = {STR_CLIT("F5"), FNAME_NONE}, + [INPUT_KEY_F6] = {STR_CLIT("F6"), FNAME_NONE}, + [INPUT_KEY_F7] = {STR_CLIT("F7"), FNAME_NONE}, + [INPUT_KEY_F8] = {STR_CLIT("F8"), FNAME_NONE}, + [INPUT_KEY_F9] = {STR_CLIT("F9"), FNAME_NONE}, + [INPUT_KEY_F10] = {STR_CLIT("F10"), FNAME_NONE}, + [INPUT_KEY_F11] = {STR_CLIT("F11"), FNAME_NONE}, + [INPUT_KEY_F12] = {STR_CLIT("F12"), FNAME_NONE}, /* keyboard: navigation */ - [INPUT_KEY_UP] = {STR_CLIT("Up"), 0}, - [INPUT_KEY_DOWN] = {STR_CLIT("Down"), 0}, - [INPUT_KEY_LEFT] = {STR_CLIT("Left"), 0}, - [INPUT_KEY_RIGHT] = {STR_CLIT("Right"), 0}, - [INPUT_KEY_HOME] = {STR_CLIT("Home"), 0}, - [INPUT_KEY_END] = {STR_CLIT("End"), 0}, - [INPUT_KEY_PAGE_UP] = {STR_CLIT("PageUp"), 0}, - [INPUT_KEY_PAGE_DOWN] = {STR_CLIT("PageDown"), 0}, + [INPUT_KEY_UP] = {STR_CLIT("Up"), FNAME_NONE}, + [INPUT_KEY_DOWN] = {STR_CLIT("Down"), FNAME_NONE}, + [INPUT_KEY_LEFT] = {STR_CLIT("Left"), FNAME_NONE}, + [INPUT_KEY_RIGHT] = {STR_CLIT("Right"), FNAME_NONE}, + [INPUT_KEY_HOME] = {STR_CLIT("Home"), FNAME_NONE}, + [INPUT_KEY_END] = {STR_CLIT("End"), FNAME_NONE}, + [INPUT_KEY_PAGE_UP] = {STR_CLIT("PageUp"), FNAME_NONE}, + [INPUT_KEY_PAGE_DOWN] = {STR_CLIT("PageDown"), FNAME_NONE}, /* keyboard: modifiers */ - [INPUT_KEY_LEFT_SHIFT] = {STR_CLIT("LeftShift"), 0}, - [INPUT_KEY_RIGHT_SHIFT] = {STR_CLIT("RightShift"), 0}, - [INPUT_KEY_LEFT_CTRL] = {STR_CLIT("LeftControl"), 0}, - [INPUT_KEY_RIGHT_CTRL] = {STR_CLIT("RightControl"), 0}, - [INPUT_KEY_LEFT_ALT] = {STR_CLIT("LeftAlt"), 0}, - [INPUT_KEY_RIGHT_ALT] = {STR_CLIT("RightAlt"), 0}, + [INPUT_KEY_LEFT_SHIFT] = {STR_CLIT("LeftShift"), FNAME_NONE}, + [INPUT_KEY_RIGHT_SHIFT] = {STR_CLIT("RightShift"), FNAME_NONE}, + [INPUT_KEY_LEFT_CTRL] = {STR_CLIT("LeftControl"), FNAME_NONE}, + [INPUT_KEY_RIGHT_CTRL] = {STR_CLIT("RightControl"), FNAME_NONE}, + [INPUT_KEY_LEFT_ALT] = {STR_CLIT("LeftAlt"), FNAME_NONE}, + [INPUT_KEY_RIGHT_ALT] = {STR_CLIT("RightAlt"), FNAME_NONE}, + + /* keyboard: virtual modifiers (either left or right) */ + [INPUT_KEY_SHIFT] = {STR_CLIT("Shift"), FNAME_NONE}, // not actual name + [INPUT_KEY_CTRL] = {STR_CLIT("Ctrl"), FNAME_NONE}, // not actual name + [INPUT_KEY_ALT] = {STR_CLIT("Alt"), FNAME_NONE}, // not actual name /* keyboard: common */ - [INPUT_KEY_SPACE] = {STR_CLIT("SpaceBar"), 0}, - [INPUT_KEY_ENTER] = {STR_CLIT("Enter"), 0}, - [INPUT_KEY_ESCAPE] = {STR_CLIT("Escape"), 0}, - [INPUT_KEY_TAB] = {STR_CLIT("Tab"), 0}, - [INPUT_KEY_BACKSPACE] = {STR_CLIT("BackSpace"), 0}, - [INPUT_KEY_CAPS_LOCK] = {STR_CLIT("CapsLock"), 0}, - [INPUT_KEY_INSERT] = {STR_CLIT("Insert"), 0}, - [INPUT_KEY_DELETE] = {STR_CLIT("Delete"), 0}, + [INPUT_KEY_SPACE] = {STR_CLIT("SpaceBar"), FNAME_NONE}, + [INPUT_KEY_ENTER] = {STR_CLIT("Enter"), FNAME_NONE}, + [INPUT_KEY_ESCAPE] = {STR_CLIT("Escape"), FNAME_NONE}, + [INPUT_KEY_TAB] = {STR_CLIT("Tab"), FNAME_NONE}, + [INPUT_KEY_BACKSPACE] = {STR_CLIT("BackSpace"), FNAME_NONE}, + [INPUT_KEY_CAPS_LOCK] = {STR_CLIT("CapsLock"), FNAME_NONE}, + [INPUT_KEY_INSERT] = {STR_CLIT("Insert"), FNAME_NONE}, + [INPUT_KEY_DELETE] = {STR_CLIT("Delete"), FNAME_NONE}, /* keyboard: numpad */ - [INPUT_KEY_NUM_0] = {STR_CLIT("NumPadZero"), 0}, - [INPUT_KEY_NUM_1] = {STR_CLIT("NumPadOne"), 0}, - [INPUT_KEY_NUM_2] = {STR_CLIT("NumPadTwo"), 0}, - [INPUT_KEY_NUM_3] = {STR_CLIT("NumPadThree"), 0}, - [INPUT_KEY_NUM_4] = {STR_CLIT("NumPadFour"), 0}, - [INPUT_KEY_NUM_5] = {STR_CLIT("NumPadFive"), 0}, - [INPUT_KEY_NUM_6] = {STR_CLIT("NumPadSix"), 0}, - [INPUT_KEY_NUM_7] = {STR_CLIT("NumPadSeven"), 0}, - [INPUT_KEY_NUM_8] = {STR_CLIT("NumPadEight"), 0}, - [INPUT_KEY_NUM_9] = {STR_CLIT("NumPadNine"), 0}, - [INPUT_KEY_NUM_MULTIPLY] = {STR_CLIT("Multiply"), 0}, - [INPUT_KEY_NUM_ADD] = {STR_CLIT("Add"), 0}, - [INPUT_KEY_NUM_SUBTRACT] = {STR_CLIT("Subtract"), 0}, - [INPUT_KEY_NUM_DECIMAL] = {STR_CLIT("Decimal"), 0}, - [INPUT_KEY_NUM_DIVIDE] = {STR_CLIT("Divide"), 0}, + [INPUT_KEY_NUM_0] = {STR_CLIT("NumPadZero"), FNAME_NONE}, + [INPUT_KEY_NUM_1] = {STR_CLIT("NumPadOne"), FNAME_NONE}, + [INPUT_KEY_NUM_2] = {STR_CLIT("NumPadTwo"), FNAME_NONE}, + [INPUT_KEY_NUM_3] = {STR_CLIT("NumPadThree"), FNAME_NONE}, + [INPUT_KEY_NUM_4] = {STR_CLIT("NumPadFour"), FNAME_NONE}, + [INPUT_KEY_NUM_5] = {STR_CLIT("NumPadFive"), FNAME_NONE}, + [INPUT_KEY_NUM_6] = {STR_CLIT("NumPadSix"), FNAME_NONE}, + [INPUT_KEY_NUM_7] = {STR_CLIT("NumPadSeven"), FNAME_NONE}, + [INPUT_KEY_NUM_8] = {STR_CLIT("NumPadEight"), FNAME_NONE}, + [INPUT_KEY_NUM_9] = {STR_CLIT("NumPadNine"), FNAME_NONE}, + [INPUT_KEY_NUM_MULTIPLY] = {STR_CLIT("Multiply"), FNAME_NONE}, + [INPUT_KEY_NUM_ADD] = {STR_CLIT("Add"), FNAME_NONE}, + [INPUT_KEY_NUM_SUBTRACT] = {STR_CLIT("Subtract"), FNAME_NONE}, + [INPUT_KEY_NUM_DECIMAL] = {STR_CLIT("Decimal"), FNAME_NONE}, + [INPUT_KEY_NUM_DIVIDE] = {STR_CLIT("Divide"), FNAME_NONE}, /* keyboard: misc */ - [INPUT_KEY_PAUSE] = {STR_CLIT("Pause"), 0}, - [INPUT_KEY_PRINT_SCREEN] = {STR_CLIT("PrintScreen"), 0}, - [INPUT_KEY_SCROLL_LOCK] = {STR_CLIT("ScrollLock"), 0}, - [INPUT_KEY_NUM_LOCK] = {STR_CLIT("NumLock"), 0}, + [INPUT_KEY_PAUSE] = {STR_CLIT("Pause"), FNAME_NONE}, + [INPUT_KEY_PRINT_SCREEN] = {STR_CLIT("PrintScreen"), FNAME_NONE}, + [INPUT_KEY_SCROLL_LOCK] = {STR_CLIT("ScrollLock"), FNAME_NONE}, + [INPUT_KEY_NUM_LOCK] = {STR_CLIT("NumLock"), FNAME_NONE}, /* mouse: buttons */ - [INPUT_KEY_MOUSE_LEFT] = {STR_CLIT("LeftMouseButton"), 0}, - [INPUT_KEY_MOUSE_RIGHT] = {STR_CLIT("RightMouseButton"), 0}, - [INPUT_KEY_MOUSE_MIDDLE] = {STR_CLIT("MiddleMouseButton"), 0}, - [INPUT_KEY_MOUSE_THUMB_1] = {STR_CLIT("ThumbMouseButton"), 0}, - [INPUT_KEY_MOUSE_THUMB_2] = {STR_CLIT("ThumbMouseButton2"), 0}, + [INPUT_KEY_MOUSE_LEFT] = {STR_CLIT("LeftMouseButton"), FNAME_NONE}, + [INPUT_KEY_MOUSE_RIGHT] = {STR_CLIT("RightMouseButton"), FNAME_NONE}, + [INPUT_KEY_MOUSE_MIDDLE] = {STR_CLIT("MiddleMouseButton"), FNAME_NONE}, + [INPUT_KEY_MOUSE_THUMB_1] = {STR_CLIT("ThumbMouseButton"), FNAME_NONE}, + [INPUT_KEY_MOUSE_THUMB_2] = {STR_CLIT("ThumbMouseButton2"), FNAME_NONE}, /* mouse: axes */ - [INPUT_KEY_MOUSE_X] = {STR_CLIT("MouseX"), 0}, - [INPUT_KEY_MOUSE_Y] = {STR_CLIT("MouseY"), 0}, - [INPUT_KEY_MOUSE_WHEEL_UP] = {STR_CLIT("MouseWheelUp"), 0}, // not an actual name - [INPUT_KEY_MOUSE_WHEEL_DOWN] = {STR_CLIT("MouseWheelDown"), 0}, // not an actual name + [INPUT_KEY_MOUSE_X] = {STR_CLIT("MouseX"), FNAME_NONE}, + [INPUT_KEY_MOUSE_Y] = {STR_CLIT("MouseY"), FNAME_NONE}, + [INPUT_KEY_MOUSE_WHEEL_UP] = {STR_CLIT("MouseWheelUp"), FNAME_NONE}, // not an actual name + [INPUT_KEY_MOUSE_WHEEL_DOWN] = {STR_CLIT("MouseWheelDown"), FNAME_NONE}, // not an actual name /* gamepad: buttons */ - [INPUT_KEY_GAMEPAD_FACE_BOTTOM] = {STR_CLIT("Gamepad_FaceButton_Bottom"), 0}, - [INPUT_KEY_GAMEPAD_FACE_RIGHT] = {STR_CLIT("Gamepad_FaceButton_Right"), 0}, - [INPUT_KEY_GAMEPAD_FACE_LEFT] = {STR_CLIT("Gamepad_FaceButton_Left"), 0}, - [INPUT_KEY_GAMEPAD_FACE_TOP] = {STR_CLIT("Gamepad_FaceButton_Top"), 0}, - [INPUT_KEY_GAMEPAD_LB] = {STR_CLIT("Gamepad_LeftShoulder"), 0}, - [INPUT_KEY_GAMEPAD_RB] = {STR_CLIT("Gamepad_RightShoulder"), 0}, - [INPUT_KEY_GAMEPAD_LT] = {STR_CLIT("Gamepad_LeftTrigger"), 0}, - [INPUT_KEY_GAMEPAD_RT] = {STR_CLIT("Gamepad_RightTrigger"), 0}, - [INPUT_KEY_GAMEPAD_L3] = {STR_CLIT("Gamepad_LeftThumbstick"), 0}, - [INPUT_KEY_GAMEPAD_R3] = {STR_CLIT("Gamepad_RightThumbstick"), 0}, - [INPUT_KEY_GAMEPAD_SPECIAL_LEFT] = {STR_CLIT("Gamepad_Special_Left"), 0}, - [INPUT_KEY_GAMEPAD_SPECIAL_RIGHT] = {STR_CLIT("Gamepad_Special_Right"), 0}, - [INPUT_KEY_GAMEPAD_DPAD_UP] = {STR_CLIT("Gamepad_DPad_Up"), 0}, - [INPUT_KEY_GAMEPAD_DPAD_DOWN] = {STR_CLIT("Gamepad_DPad_Down"), 0}, - [INPUT_KEY_GAMEPAD_DPAD_LEFT] = {STR_CLIT("Gamepad_DPad_Left"), 0}, - [INPUT_KEY_GAMEPAD_DPAD_RIGHT] = {STR_CLIT("Gamepad_DPad_Right"), 0}, + [INPUT_KEY_GAMEPAD_FACE_BOTTOM] = {STR_CLIT("Gamepad_FaceButton_Bottom"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_FACE_RIGHT] = {STR_CLIT("Gamepad_FaceButton_Right"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_FACE_LEFT] = {STR_CLIT("Gamepad_FaceButton_Left"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_FACE_TOP] = {STR_CLIT("Gamepad_FaceButton_Top"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_LB] = {STR_CLIT("Gamepad_LeftShoulder"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_RB] = {STR_CLIT("Gamepad_RightShoulder"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_LT] = {STR_CLIT("Gamepad_LeftTrigger"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_RT] = {STR_CLIT("Gamepad_RightTrigger"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_L3] = {STR_CLIT("Gamepad_LeftThumbstick"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_R3] = {STR_CLIT("Gamepad_RightThumbstick"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_SPECIAL_LEFT] = {STR_CLIT("Gamepad_Special_Left"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_SPECIAL_RIGHT] = {STR_CLIT("Gamepad_Special_Right"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_DPAD_UP] = {STR_CLIT("Gamepad_DPad_Up"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_DPAD_DOWN] = {STR_CLIT("Gamepad_DPad_Down"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_DPAD_LEFT] = {STR_CLIT("Gamepad_DPad_Left"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_DPAD_RIGHT] = {STR_CLIT("Gamepad_DPad_Right"), FNAME_NONE}, /* gamepad: axes */ - [INPUT_KEY_GAMEPAD_LEFT_X] = {STR_CLIT("Gamepad_LeftX"), 0}, - [INPUT_KEY_GAMEPAD_LEFT_Y] = {STR_CLIT("Gamepad_LeftY"), 0}, - [INPUT_KEY_GAMEPAD_RIGHT_X] = {STR_CLIT("Gamepad_RightX"), 0}, - [INPUT_KEY_GAMEPAD_RIGHT_Y] = {STR_CLIT("Gamepad_RightY"), 0}, - [INPUT_KEY_GAMEPAD_LT_AXIS] = {STR_CLIT("Gamepad_LeftTriggerAxis"), 0}, - [INPUT_KEY_GAMEPAD_RT_AXIS] = {STR_CLIT("Gamepad_RightTriggerAxis"), 0}, + [INPUT_KEY_GAMEPAD_LEFT_X] = {STR_CLIT("Gamepad_LeftX"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_LEFT_Y] = {STR_CLIT("Gamepad_LeftY"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_RIGHT_X] = {STR_CLIT("Gamepad_RightX"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_RIGHT_Y] = {STR_CLIT("Gamepad_RightY"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_LT_AXIS] = {STR_CLIT("Gamepad_LeftTriggerAxis"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_RT_AXIS] = {STR_CLIT("Gamepad_RightTriggerAxis"), FNAME_NONE}, }; void input_key_map_init(void) { - static bool map_inited = false; - if (!map_inited) { - MASSERT(globals.name_pool != NULL, "name pool is missing"); + MASSERT(globals.name_pool != NULL, "name pool is missing"); - /* resolve each UE4 key name to its cmp_idx by scanning the name pool */ - fname_entry_allocator_t *entries = &globals.name_pool->entries; - uint32_t num_blocks = entries->current_block + 1; - - for (uint32_t block = 0; block < num_blocks && block < FNAME_MAX_BLOCKS; ++block) { - uint8_t *base = entries->blocks[block]; - if (!base) { - continue; - } - - uint32_t end_offset = (1u << FNAME_BLOCK_OFFSET_BITS) * 2; - if (block == entries->current_block) { - end_offset = entries->current_byte_cursor; - } - - uint32_t byte_offset = 0; - while (byte_offset < end_offset) { - fname_entry_t *entry = (fname_entry_t *)(base + byte_offset); - uint16_t len = entry->header.len; - - if (len > 0 && !entry->header.is_wide) { - for (int i = 0; i < COUNTOF(g_key_map); ++i) { - if (g_key_map[i].cmp_idx != 0) { - continue; // already resolved - } - - str_t ue_name = g_key_map[i].ue_name; - str_t entry_name = str_make(entry->name.ansi, (uint64_t)len); - - if (str_equal(ue_name, entry_name, 0)) { - uint32_t cmp_idx = (block << FNAME_BLOCK_OFFSET_BITS) | (byte_offset / 2); - g_key_map[i].cmp_idx = cmp_idx; - - // LOG_DEBUG("%.*s: cmp_idx: %d, block: %d, byte_offset: %d", STR_ARG(ue_name), cmp_idx, block, - // byte_offset); - break; - } - } - } - - /* advance to next entry: header (2 bytes) + payload, aligned to 2 */ - uint32_t entry_size = sizeof(fname_entry_header_t); - - entry_size += entry->header.is_wide ? (len * 2) : len; - entry_size = ALIGN_UP(entry_size, 2); - byte_offset += entry_size; - } - } - - map_inited = true; + for (int i = 0; i < COUNTOF(g_key_map); ++i) { + g_key_map[i].name = unreal_fname_from_str(g_key_map[i].str, FNAME_FIND); } } @@ -239,7 +195,7 @@ input_key_kind_t input_key_from_fname(fname_t key_name) { for (int i = 0; i < COUNTOF(g_key_map); ++i) { - if (key_name.cmp_idx == g_key_map[i].cmp_idx) { + if (unreal_fname_equal(key_name, g_key_map[i].name, true)) { return (input_key_kind_t)(i); } } @@ -250,7 +206,7 @@ input_key_kind_t input_key_from_str(str_t name) { for (int i = 0; i < COUNTOF(g_key_map); ++i) { - if (str_equal(name, g_key_map[i].ue_name, 0)) { + if (str_equal(name, g_key_map[i].str, 0)) { return (input_key_kind_t)(i); } } @@ -258,21 +214,61 @@ input_key_from_str(str_t name) } bool -input_key_is_down(input_key_kind_t kind) +input_key_is_down(input_key_kind_t key) { - if (kind >= INPUT_KEY_MAX) { - return false; - } - return globals.keys_down[kind]; + return key > INPUT_KEY_NONE && key < INPUT_KEY_MAX && globals.keys[key].down; } bool -input_key_was_down(input_key_kind_t kind) +input_key_is_pressed(input_key_kind_t key) { - if (kind >= INPUT_KEY_MAX) { - return false; + return key > INPUT_KEY_NONE && key < INPUT_KEY_MAX && globals.keys[key].pressed; +} + +bool +input_key_is_released(input_key_kind_t key) +{ + return key > INPUT_KEY_NONE && key < INPUT_KEY_MAX && globals.keys[key].released; +} + +bool +input_key_is_bindable(input_key_kind_t key) +{ + switch (key) { + case INPUT_KEY_MOUSE_X: + case INPUT_KEY_MOUSE_Y: + case INPUT_KEY_GAMEPAD_LEFT_X: + case INPUT_KEY_GAMEPAD_LEFT_Y: + case INPUT_KEY_GAMEPAD_RIGHT_X: + case INPUT_KEY_GAMEPAD_RIGHT_Y: + case INPUT_KEY_GAMEPAD_LT_AXIS: + case INPUT_KEY_GAMEPAD_RT_AXIS: + return false; + + default: + return key > INPUT_KEY_NONE && key < INPUT_KEY_MAX; + } +} + +void +input_key_lost_focus(void) +{ + for (int i = 0; i < INPUT_KEY_MAX; ++i) { + globals.keys[i].released = globals.keys[i].down; + globals.keys[i].pressed = false; + globals.keys[i].down = false; + globals.consumed_down[i] = false; + } +} + +void +input_key_clear_transient_state(void) +{ + for (int i = 0; i < INPUT_KEY_MAX; ++i) { + globals.keys[i].pressed = false; + globals.keys[i].released = false; } - return globals.prev_keys_down[kind]; + globals.wheel_delta = 0.0f; } str_t @@ -281,7 +277,7 @@ input_key_to_str(input_key_kind_t key) if (key >= INPUT_KEY_MAX) { return (str_t){0}; } - return g_key_map[key].ue_name; + return g_key_map[key].str; } str_t @@ -527,230 +523,230 @@ static const key_alias_t g_key_aliases[] = { {STR_CLIT("9"), INPUT_KEY_9}, /* function keys */ - {STR_CLIT("F1"), INPUT_KEY_F1}, - {STR_CLIT("F2"), INPUT_KEY_F2}, - {STR_CLIT("F3"), INPUT_KEY_F3}, - {STR_CLIT("F4"), INPUT_KEY_F4}, - {STR_CLIT("F5"), INPUT_KEY_F5}, - {STR_CLIT("F6"), INPUT_KEY_F6}, - {STR_CLIT("F7"), INPUT_KEY_F7}, - {STR_CLIT("F8"), INPUT_KEY_F8}, - {STR_CLIT("F9"), INPUT_KEY_F9}, + {STR_CLIT("F1"), INPUT_KEY_F1 }, + {STR_CLIT("F2"), INPUT_KEY_F2 }, + {STR_CLIT("F3"), INPUT_KEY_F3 }, + {STR_CLIT("F4"), INPUT_KEY_F4 }, + {STR_CLIT("F5"), INPUT_KEY_F5 }, + {STR_CLIT("F6"), INPUT_KEY_F6 }, + {STR_CLIT("F7"), INPUT_KEY_F7 }, + {STR_CLIT("F8"), INPUT_KEY_F8 }, + {STR_CLIT("F9"), INPUT_KEY_F9 }, {STR_CLIT("F10"), INPUT_KEY_F10}, {STR_CLIT("F11"), INPUT_KEY_F11}, {STR_CLIT("F12"), INPUT_KEY_F12}, /* modifiers - first entry is canonical display name */ - {STR_CLIT("Ctrl"), INPUT_KEY_LEFT_CTRL}, - {STR_CLIT("LCtrl"), INPUT_KEY_LEFT_CTRL}, - {STR_CLIT("LeftCtrl"), INPUT_KEY_LEFT_CTRL}, - {STR_CLIT("left_ctrl"), INPUT_KEY_LEFT_CTRL}, - {STR_CLIT("Control"), INPUT_KEY_LEFT_CTRL}, - {STR_CLIT("LControl"), INPUT_KEY_LEFT_CTRL}, - {STR_CLIT("LeftControl"), INPUT_KEY_LEFT_CTRL}, - {STR_CLIT("left_control"), INPUT_KEY_LEFT_CTRL}, - {STR_CLIT("RCtrl"), INPUT_KEY_RIGHT_CTRL}, - {STR_CLIT("RightCtrl"), INPUT_KEY_RIGHT_CTRL}, - {STR_CLIT("right_ctrl"), INPUT_KEY_RIGHT_CTRL}, - {STR_CLIT("RControl"), INPUT_KEY_RIGHT_CTRL}, - {STR_CLIT("RightControl"), INPUT_KEY_RIGHT_CTRL}, - {STR_CLIT("right_control"), INPUT_KEY_RIGHT_CTRL}, - {STR_CLIT("Shift"), INPUT_KEY_LEFT_SHIFT}, - {STR_CLIT("LShift"), INPUT_KEY_LEFT_SHIFT}, - {STR_CLIT("LeftShift"), INPUT_KEY_LEFT_SHIFT}, - {STR_CLIT("left_shift"), INPUT_KEY_LEFT_SHIFT}, - {STR_CLIT("RShift"), INPUT_KEY_RIGHT_SHIFT}, - {STR_CLIT("RightShift"), INPUT_KEY_RIGHT_SHIFT}, - {STR_CLIT("right_shift"), INPUT_KEY_RIGHT_SHIFT}, - {STR_CLIT("Alt"), INPUT_KEY_LEFT_ALT}, - {STR_CLIT("LAlt"), INPUT_KEY_LEFT_ALT}, - {STR_CLIT("LeftAlt"), INPUT_KEY_LEFT_ALT}, - {STR_CLIT("left_alt"), INPUT_KEY_LEFT_ALT}, - {STR_CLIT("RAlt"), INPUT_KEY_RIGHT_ALT}, - {STR_CLIT("RightAlt"), INPUT_KEY_RIGHT_ALT}, - {STR_CLIT("right_alt"), INPUT_KEY_RIGHT_ALT}, + {STR_CLIT("Ctrl"), INPUT_KEY_CTRL }, + {STR_CLIT("LCtrl"), INPUT_KEY_LEFT_CTRL }, + {STR_CLIT("LeftCtrl"), INPUT_KEY_LEFT_CTRL }, + {STR_CLIT("left_ctrl"), INPUT_KEY_LEFT_CTRL }, + {STR_CLIT("Control"), INPUT_KEY_LEFT_CTRL }, + {STR_CLIT("LControl"), INPUT_KEY_LEFT_CTRL }, + {STR_CLIT("LeftControl"), INPUT_KEY_LEFT_CTRL }, + {STR_CLIT("left_control"), INPUT_KEY_LEFT_CTRL }, + {STR_CLIT("RCtrl"), INPUT_KEY_RIGHT_CTRL }, + {STR_CLIT("RightCtrl"), INPUT_KEY_RIGHT_CTRL }, + {STR_CLIT("right_ctrl"), INPUT_KEY_RIGHT_CTRL }, + {STR_CLIT("RControl"), INPUT_KEY_RIGHT_CTRL }, + {STR_CLIT("RightControl"), INPUT_KEY_RIGHT_CTRL }, + {STR_CLIT("right_control"), INPUT_KEY_RIGHT_CTRL }, + {STR_CLIT("Shift"), INPUT_KEY_SHIFT }, + {STR_CLIT("LShift"), INPUT_KEY_LEFT_SHIFT }, + {STR_CLIT("LeftShift"), INPUT_KEY_LEFT_SHIFT }, + {STR_CLIT("left_shift"), INPUT_KEY_LEFT_SHIFT }, + {STR_CLIT("RShift"), INPUT_KEY_RIGHT_SHIFT}, + {STR_CLIT("RightShift"), INPUT_KEY_RIGHT_SHIFT}, + {STR_CLIT("right_shift"), INPUT_KEY_RIGHT_SHIFT}, + {STR_CLIT("Alt"), INPUT_KEY_ALT }, + {STR_CLIT("LAlt"), INPUT_KEY_LEFT_ALT }, + {STR_CLIT("LeftAlt"), INPUT_KEY_LEFT_ALT }, + {STR_CLIT("left_alt"), INPUT_KEY_LEFT_ALT }, + {STR_CLIT("RAlt"), INPUT_KEY_RIGHT_ALT }, + {STR_CLIT("RightAlt"), INPUT_KEY_RIGHT_ALT }, + {STR_CLIT("right_alt"), INPUT_KEY_RIGHT_ALT }, /* common keys */ - {STR_CLIT("Space"), INPUT_KEY_SPACE}, - {STR_CLIT("Enter"), INPUT_KEY_ENTER}, - {STR_CLIT("Return"), INPUT_KEY_ENTER}, - {STR_CLIT("Esc"), INPUT_KEY_ESCAPE}, - {STR_CLIT("Escape"), INPUT_KEY_ESCAPE}, - {STR_CLIT("Tab"), INPUT_KEY_TAB}, + {STR_CLIT("Space"), INPUT_KEY_SPACE }, + {STR_CLIT("Enter"), INPUT_KEY_ENTER }, + {STR_CLIT("Return"), INPUT_KEY_ENTER }, + {STR_CLIT("Esc"), INPUT_KEY_ESCAPE }, + {STR_CLIT("Escape"), INPUT_KEY_ESCAPE }, + {STR_CLIT("Tab"), INPUT_KEY_TAB }, {STR_CLIT("Backspace"), INPUT_KEY_BACKSPACE}, - {STR_CLIT("BS"), INPUT_KEY_BACKSPACE}, - {STR_CLIT("CapsLock"), INPUT_KEY_CAPS_LOCK}, - {STR_CLIT("Caps"), INPUT_KEY_CAPS_LOCK}, - {STR_CLIT("Insert"), INPUT_KEY_INSERT}, - {STR_CLIT("Ins"), INPUT_KEY_INSERT}, - {STR_CLIT("Delete"), INPUT_KEY_DELETE}, - {STR_CLIT("Del"), INPUT_KEY_DELETE}, + {STR_CLIT("BS"), INPUT_KEY_BACKSPACE}, + {STR_CLIT("CapsLock"), INPUT_KEY_CAPS_LOCK}, + {STR_CLIT("Caps"), INPUT_KEY_CAPS_LOCK}, + {STR_CLIT("Insert"), INPUT_KEY_INSERT }, + {STR_CLIT("Ins"), INPUT_KEY_INSERT }, + {STR_CLIT("Delete"), INPUT_KEY_DELETE }, + {STR_CLIT("Del"), INPUT_KEY_DELETE }, /* navigation */ - {STR_CLIT("Up"), INPUT_KEY_UP}, - {STR_CLIT("Down"), INPUT_KEY_DOWN}, - {STR_CLIT("Left"), INPUT_KEY_LEFT}, - {STR_CLIT("Right"), INPUT_KEY_RIGHT}, - {STR_CLIT("Home"), INPUT_KEY_HOME}, - {STR_CLIT("End"), INPUT_KEY_END}, - {STR_CLIT("PageUp"), INPUT_KEY_PAGE_UP}, - {STR_CLIT("page_up"), INPUT_KEY_PAGE_UP}, - {STR_CLIT("PgUp"), INPUT_KEY_PAGE_UP}, - {STR_CLIT("PageDown"), INPUT_KEY_PAGE_DOWN}, + {STR_CLIT("Up"), INPUT_KEY_UP }, + {STR_CLIT("Down"), INPUT_KEY_DOWN }, + {STR_CLIT("Left"), INPUT_KEY_LEFT }, + {STR_CLIT("Right"), INPUT_KEY_RIGHT }, + {STR_CLIT("Home"), INPUT_KEY_HOME }, + {STR_CLIT("End"), INPUT_KEY_END }, + {STR_CLIT("PageUp"), INPUT_KEY_PAGE_UP }, + {STR_CLIT("page_up"), INPUT_KEY_PAGE_UP }, + {STR_CLIT("PgUp"), INPUT_KEY_PAGE_UP }, + {STR_CLIT("PageDown"), INPUT_KEY_PAGE_DOWN}, {STR_CLIT("page_down"), INPUT_KEY_PAGE_DOWN}, - {STR_CLIT("PgDn"), INPUT_KEY_PAGE_DOWN}, - {STR_CLIT("PgDown"), INPUT_KEY_PAGE_DOWN}, + {STR_CLIT("PgDn"), INPUT_KEY_PAGE_DOWN}, + {STR_CLIT("PgDown"), INPUT_KEY_PAGE_DOWN}, /* punctuation */ - {STR_CLIT(","), INPUT_KEY_COMMA}, - {STR_CLIT("Comma"), INPUT_KEY_COMMA}, - {STR_CLIT("."), INPUT_KEY_PERIOD}, - {STR_CLIT("Period"), INPUT_KEY_PERIOD}, - {STR_CLIT("Dot"), INPUT_KEY_PERIOD}, - {STR_CLIT("/"), INPUT_KEY_SLASH}, - {STR_CLIT("Slash"), INPUT_KEY_SLASH}, - {STR_CLIT(";"), INPUT_KEY_SEMICOLON}, - {STR_CLIT("Semicolon"), INPUT_KEY_SEMICOLON}, - {STR_CLIT("'"), INPUT_KEY_APOSTROPHE}, - {STR_CLIT("Apostrophe"), INPUT_KEY_APOSTROPHE}, - {STR_CLIT("\""), INPUT_KEY_APOSTROPHE}, - {STR_CLIT("Quote"), INPUT_KEY_APOSTROPHE}, - {STR_CLIT("["), INPUT_KEY_LEFT_BRACKET}, - {STR_CLIT("LBracket"), INPUT_KEY_LEFT_BRACKET}, - {STR_CLIT("LeftBracket"), INPUT_KEY_LEFT_BRACKET}, - {STR_CLIT("left_bracket"), INPUT_KEY_LEFT_BRACKET}, - {STR_CLIT("]"), INPUT_KEY_RIGHT_BRACKET}, - {STR_CLIT("RBracket"), INPUT_KEY_RIGHT_BRACKET}, - {STR_CLIT("RightBracket"), INPUT_KEY_RIGHT_BRACKET}, + {STR_CLIT(","), INPUT_KEY_COMMA }, + {STR_CLIT("Comma"), INPUT_KEY_COMMA }, + {STR_CLIT("."), INPUT_KEY_PERIOD }, + {STR_CLIT("Period"), INPUT_KEY_PERIOD }, + {STR_CLIT("Dot"), INPUT_KEY_PERIOD }, + {STR_CLIT("/"), INPUT_KEY_SLASH }, + {STR_CLIT("Slash"), INPUT_KEY_SLASH }, + {STR_CLIT(";"), INPUT_KEY_SEMICOLON }, + {STR_CLIT("Semicolon"), INPUT_KEY_SEMICOLON }, + {STR_CLIT("'"), INPUT_KEY_APOSTROPHE }, + {STR_CLIT("Apostrophe"), INPUT_KEY_APOSTROPHE }, + {STR_CLIT("\""), INPUT_KEY_APOSTROPHE }, + {STR_CLIT("Quote"), INPUT_KEY_APOSTROPHE }, + {STR_CLIT("["), INPUT_KEY_LEFT_BRACKET }, + {STR_CLIT("LBracket"), INPUT_KEY_LEFT_BRACKET }, + {STR_CLIT("LeftBracket"), INPUT_KEY_LEFT_BRACKET }, + {STR_CLIT("left_bracket"), INPUT_KEY_LEFT_BRACKET }, + {STR_CLIT("]"), INPUT_KEY_RIGHT_BRACKET}, + {STR_CLIT("RBracket"), INPUT_KEY_RIGHT_BRACKET}, + {STR_CLIT("RightBracket"), INPUT_KEY_RIGHT_BRACKET}, {STR_CLIT("right_bracket"), INPUT_KEY_RIGHT_BRACKET}, - {STR_CLIT("\\"), INPUT_KEY_BACKSLASH}, - {STR_CLIT("Backslash"), INPUT_KEY_BACKSLASH}, - {STR_CLIT("-"), INPUT_KEY_HYPHEN}, - {STR_CLIT("Minus"), INPUT_KEY_HYPHEN}, - {STR_CLIT("Hyphen"), INPUT_KEY_HYPHEN}, - {STR_CLIT("="), INPUT_KEY_EQUALS}, - {STR_CLIT("Equals"), INPUT_KEY_EQUALS}, - {STR_CLIT("`"), INPUT_KEY_BACKTICK}, - {STR_CLIT("Backtick"), INPUT_KEY_BACKTICK}, + {STR_CLIT("\\"), INPUT_KEY_BACKSLASH }, + {STR_CLIT("Backslash"), INPUT_KEY_BACKSLASH }, + {STR_CLIT("-"), INPUT_KEY_HYPHEN }, + {STR_CLIT("Minus"), INPUT_KEY_HYPHEN }, + {STR_CLIT("Hyphen"), INPUT_KEY_HYPHEN }, + {STR_CLIT("="), INPUT_KEY_EQUALS }, + {STR_CLIT("Equals"), INPUT_KEY_EQUALS }, + {STR_CLIT("`"), INPUT_KEY_BACKTICK }, + {STR_CLIT("Backtick"), INPUT_KEY_BACKTICK }, /* misc */ - {STR_CLIT("Pause"), INPUT_KEY_PAUSE}, - {STR_CLIT("Break"), INPUT_KEY_PAUSE}, - {STR_CLIT("PrintScreen"), INPUT_KEY_PRINT_SCREEN}, + {STR_CLIT("Pause"), INPUT_KEY_PAUSE }, + {STR_CLIT("Break"), INPUT_KEY_PAUSE }, + {STR_CLIT("PrintScreen"), INPUT_KEY_PRINT_SCREEN}, {STR_CLIT("print_screen"), INPUT_KEY_PRINT_SCREEN}, - {STR_CLIT("PrtSc"), INPUT_KEY_PRINT_SCREEN}, - {STR_CLIT("ScrollLock"), INPUT_KEY_SCROLL_LOCK}, - {STR_CLIT("scroll_lock"), INPUT_KEY_SCROLL_LOCK}, - {STR_CLIT("ScrLk"), INPUT_KEY_SCROLL_LOCK}, - {STR_CLIT("NumLock"), INPUT_KEY_NUM_LOCK}, - {STR_CLIT("num_lock"), INPUT_KEY_NUM_LOCK}, - {STR_CLIT("NumLk"), INPUT_KEY_NUM_LOCK}, + {STR_CLIT("PrtSc"), INPUT_KEY_PRINT_SCREEN}, + {STR_CLIT("ScrollLock"), INPUT_KEY_SCROLL_LOCK }, + {STR_CLIT("scroll_lock"), INPUT_KEY_SCROLL_LOCK }, + {STR_CLIT("ScrLk"), INPUT_KEY_SCROLL_LOCK }, + {STR_CLIT("NumLock"), INPUT_KEY_NUM_LOCK }, + {STR_CLIT("num_lock"), INPUT_KEY_NUM_LOCK }, + {STR_CLIT("NumLk"), INPUT_KEY_NUM_LOCK }, /* numpad */ - {STR_CLIT("Num0"), INPUT_KEY_NUM_0}, - {STR_CLIT("Num1"), INPUT_KEY_NUM_1}, - {STR_CLIT("Num2"), INPUT_KEY_NUM_2}, - {STR_CLIT("Num3"), INPUT_KEY_NUM_3}, - {STR_CLIT("Num4"), INPUT_KEY_NUM_4}, - {STR_CLIT("Num5"), INPUT_KEY_NUM_5}, - {STR_CLIT("Num6"), INPUT_KEY_NUM_6}, - {STR_CLIT("Num7"), INPUT_KEY_NUM_7}, - {STR_CLIT("Num8"), INPUT_KEY_NUM_8}, - {STR_CLIT("Num9"), INPUT_KEY_NUM_9}, - {STR_CLIT("NumMul"), INPUT_KEY_NUM_MULTIPLY}, - {STR_CLIT("num_mul"), INPUT_KEY_NUM_MULTIPLY}, - {STR_CLIT("NumMultiply"), INPUT_KEY_NUM_MULTIPLY}, - {STR_CLIT("NumAdd"), INPUT_KEY_NUM_ADD}, - {STR_CLIT("num_add"), INPUT_KEY_NUM_ADD}, - {STR_CLIT("NumSub"), INPUT_KEY_NUM_SUBTRACT}, - {STR_CLIT("num_sub"), INPUT_KEY_NUM_SUBTRACT}, + {STR_CLIT("Num0"), INPUT_KEY_NUM_0 }, + {STR_CLIT("Num1"), INPUT_KEY_NUM_1 }, + {STR_CLIT("Num2"), INPUT_KEY_NUM_2 }, + {STR_CLIT("Num3"), INPUT_KEY_NUM_3 }, + {STR_CLIT("Num4"), INPUT_KEY_NUM_4 }, + {STR_CLIT("Num5"), INPUT_KEY_NUM_5 }, + {STR_CLIT("Num6"), INPUT_KEY_NUM_6 }, + {STR_CLIT("Num7"), INPUT_KEY_NUM_7 }, + {STR_CLIT("Num8"), INPUT_KEY_NUM_8 }, + {STR_CLIT("Num9"), INPUT_KEY_NUM_9 }, + {STR_CLIT("NumMul"), INPUT_KEY_NUM_MULTIPLY}, + {STR_CLIT("num_mul"), INPUT_KEY_NUM_MULTIPLY}, + {STR_CLIT("NumMultiply"), INPUT_KEY_NUM_MULTIPLY}, + {STR_CLIT("NumAdd"), INPUT_KEY_NUM_ADD }, + {STR_CLIT("num_add"), INPUT_KEY_NUM_ADD }, + {STR_CLIT("NumSub"), INPUT_KEY_NUM_SUBTRACT}, + {STR_CLIT("num_sub"), INPUT_KEY_NUM_SUBTRACT}, {STR_CLIT("NumSubstract"), INPUT_KEY_NUM_SUBTRACT}, - {STR_CLIT("NumDec"), INPUT_KEY_NUM_DECIMAL}, - {STR_CLIT("num_dec"), INPUT_KEY_NUM_DECIMAL}, - {STR_CLIT("NumDecimal"), INPUT_KEY_NUM_DECIMAL}, - {STR_CLIT("NumDiv"), INPUT_KEY_NUM_DIVIDE}, - {STR_CLIT("num_div"), INPUT_KEY_NUM_DIVIDE}, - {STR_CLIT("NumDivde"), INPUT_KEY_NUM_DIVIDE}, - {STR_CLIT("NumPlus"), INPUT_KEY_NUM_ADD}, - {STR_CLIT("num_plus"), INPUT_KEY_NUM_ADD}, + {STR_CLIT("NumDec"), INPUT_KEY_NUM_DECIMAL }, + {STR_CLIT("num_dec"), INPUT_KEY_NUM_DECIMAL }, + {STR_CLIT("NumDecimal"), INPUT_KEY_NUM_DECIMAL }, + {STR_CLIT("NumDiv"), INPUT_KEY_NUM_DIVIDE }, + {STR_CLIT("num_div"), INPUT_KEY_NUM_DIVIDE }, + {STR_CLIT("NumDivde"), INPUT_KEY_NUM_DIVIDE }, + {STR_CLIT("NumPlus"), INPUT_KEY_NUM_ADD }, + {STR_CLIT("num_plus"), INPUT_KEY_NUM_ADD }, /* mouse */ - {STR_CLIT("LMB"), INPUT_KEY_MOUSE_LEFT}, - {STR_CLIT("MouseLeft"), INPUT_KEY_MOUSE_LEFT}, - {STR_CLIT("mouse_left"), INPUT_KEY_MOUSE_LEFT}, - {STR_CLIT("RMB"), INPUT_KEY_MOUSE_RIGHT}, - {STR_CLIT("MouseRight"), INPUT_KEY_MOUSE_RIGHT}, - {STR_CLIT("mouse_right"), INPUT_KEY_MOUSE_RIGHT}, - {STR_CLIT("MMB"), INPUT_KEY_MOUSE_MIDDLE}, - {STR_CLIT("MouseMiddle"), INPUT_KEY_MOUSE_MIDDLE}, - {STR_CLIT("mouse_middle"), INPUT_KEY_MOUSE_MIDDLE}, - {STR_CLIT("Mouse4"), INPUT_KEY_MOUSE_THUMB_1}, - {STR_CLIT("mouse_4"), INPUT_KEY_MOUSE_THUMB_1}, - {STR_CLIT("Thumb1"), INPUT_KEY_MOUSE_THUMB_1}, - {STR_CLIT("Mouse5"), INPUT_KEY_MOUSE_THUMB_2}, - {STR_CLIT("mouse_5"), INPUT_KEY_MOUSE_THUMB_2}, - {STR_CLIT("Thumb2"), INPUT_KEY_MOUSE_THUMB_2}, - {STR_CLIT("WheelUp"), INPUT_KEY_MOUSE_WHEEL_UP}, - {STR_CLIT("MouseWheelUp"), INPUT_KEY_MOUSE_WHEEL_UP}, - {STR_CLIT("mouse_wheel_up"), INPUT_KEY_MOUSE_WHEEL_UP}, - {STR_CLIT("WheelDown"), INPUT_KEY_MOUSE_WHEEL_DOWN}, - {STR_CLIT("MouseWheelDown"), INPUT_KEY_MOUSE_WHEEL_DOWN}, + {STR_CLIT("LMB"), INPUT_KEY_MOUSE_LEFT }, + {STR_CLIT("MouseLeft"), INPUT_KEY_MOUSE_LEFT }, + {STR_CLIT("mouse_left"), INPUT_KEY_MOUSE_LEFT }, + {STR_CLIT("RMB"), INPUT_KEY_MOUSE_RIGHT }, + {STR_CLIT("MouseRight"), INPUT_KEY_MOUSE_RIGHT }, + {STR_CLIT("mouse_right"), INPUT_KEY_MOUSE_RIGHT }, + {STR_CLIT("MMB"), INPUT_KEY_MOUSE_MIDDLE }, + {STR_CLIT("MouseMiddle"), INPUT_KEY_MOUSE_MIDDLE }, + {STR_CLIT("mouse_middle"), INPUT_KEY_MOUSE_MIDDLE }, + {STR_CLIT("Mouse4"), INPUT_KEY_MOUSE_THUMB_1 }, + {STR_CLIT("mouse_4"), INPUT_KEY_MOUSE_THUMB_1 }, + {STR_CLIT("Thumb1"), INPUT_KEY_MOUSE_THUMB_1 }, + {STR_CLIT("Mouse5"), INPUT_KEY_MOUSE_THUMB_2 }, + {STR_CLIT("mouse_5"), INPUT_KEY_MOUSE_THUMB_2 }, + {STR_CLIT("Thumb2"), INPUT_KEY_MOUSE_THUMB_2 }, + {STR_CLIT("WheelUp"), INPUT_KEY_MOUSE_WHEEL_UP }, + {STR_CLIT("MouseWheelUp"), INPUT_KEY_MOUSE_WHEEL_UP }, + {STR_CLIT("mouse_wheel_up"), INPUT_KEY_MOUSE_WHEEL_UP }, + {STR_CLIT("WheelDown"), INPUT_KEY_MOUSE_WHEEL_DOWN}, + {STR_CLIT("MouseWheelDown"), INPUT_KEY_MOUSE_WHEEL_DOWN}, {STR_CLIT("mouse_wheel_down"), INPUT_KEY_MOUSE_WHEEL_DOWN}, /* gamepad */ - {STR_CLIT("ButtonA"), INPUT_KEY_GAMEPAD_FACE_BOTTOM}, - {STR_CLIT("button_a"), INPUT_KEY_GAMEPAD_FACE_BOTTOM}, - {STR_CLIT("Cross"), INPUT_KEY_GAMEPAD_FACE_BOTTOM}, - {STR_CLIT("GamepadButtonBottom"), INPUT_KEY_GAMEPAD_FACE_BOTTOM}, - {STR_CLIT("ButtonB"), INPUT_KEY_GAMEPAD_FACE_RIGHT}, - {STR_CLIT("button_b"), INPUT_KEY_GAMEPAD_FACE_RIGHT}, - {STR_CLIT("Circle"), INPUT_KEY_GAMEPAD_FACE_RIGHT}, - {STR_CLIT("GamepadButtonRight"), INPUT_KEY_GAMEPAD_FACE_RIGHT}, - {STR_CLIT("ButtonX"), INPUT_KEY_GAMEPAD_FACE_LEFT}, - {STR_CLIT("button_x"), INPUT_KEY_GAMEPAD_FACE_LEFT}, - {STR_CLIT("Square"), INPUT_KEY_GAMEPAD_FACE_LEFT}, - {STR_CLIT("GamepadButtonLeft"), INPUT_KEY_GAMEPAD_FACE_LEFT}, - {STR_CLIT("ButtonY"), INPUT_KEY_GAMEPAD_FACE_TOP}, - {STR_CLIT("button_y"), INPUT_KEY_GAMEPAD_FACE_TOP}, - {STR_CLIT("Triangle"), INPUT_KEY_GAMEPAD_FACE_TOP}, - {STR_CLIT("GamepadButtonTop"), INPUT_KEY_GAMEPAD_FACE_TOP}, - {STR_CLIT("LB"), INPUT_KEY_GAMEPAD_LB}, - {STR_CLIT("L1"), INPUT_KEY_GAMEPAD_LB}, - {STR_CLIT("LeftBumper"), INPUT_KEY_GAMEPAD_LB}, - {STR_CLIT("left_bumper"), INPUT_KEY_GAMEPAD_LB}, - {STR_CLIT("RB"), INPUT_KEY_GAMEPAD_RB}, - {STR_CLIT("R1"), INPUT_KEY_GAMEPAD_RB}, - {STR_CLIT("RightBumper"), INPUT_KEY_GAMEPAD_RB}, - {STR_CLIT("right_bumper"), INPUT_KEY_GAMEPAD_RB}, - {STR_CLIT("LT"), INPUT_KEY_GAMEPAD_LT}, - {STR_CLIT("L2"), INPUT_KEY_GAMEPAD_LT}, - {STR_CLIT("LeftTrigger"), INPUT_KEY_GAMEPAD_LT}, - {STR_CLIT("left_trigger"), INPUT_KEY_GAMEPAD_LT}, - {STR_CLIT("RT"), INPUT_KEY_GAMEPAD_RT}, - {STR_CLIT("R2"), INPUT_KEY_GAMEPAD_RT}, - {STR_CLIT("RightTrigger"), INPUT_KEY_GAMEPAD_RT}, - {STR_CLIT("right_trigger"), INPUT_KEY_GAMEPAD_RT}, - {STR_CLIT("LS"), INPUT_KEY_GAMEPAD_L3}, - {STR_CLIT("L3"), INPUT_KEY_GAMEPAD_L3}, - {STR_CLIT("LeftStick"), INPUT_KEY_GAMEPAD_L3}, - {STR_CLIT("left_stick"), INPUT_KEY_GAMEPAD_L3}, - {STR_CLIT("RS"), INPUT_KEY_GAMEPAD_R3}, - {STR_CLIT("R3"), INPUT_KEY_GAMEPAD_R3}, - {STR_CLIT("RightStick"), INPUT_KEY_GAMEPAD_R3}, - {STR_CLIT("right_stick"), INPUT_KEY_GAMEPAD_R3}, - {STR_CLIT("Start"), INPUT_KEY_GAMEPAD_SPECIAL_RIGHT}, - {STR_CLIT("Options"), INPUT_KEY_GAMEPAD_SPECIAL_RIGHT}, - {STR_CLIT("Select"), INPUT_KEY_GAMEPAD_SPECIAL_LEFT}, - {STR_CLIT("Share"), INPUT_KEY_GAMEPAD_SPECIAL_LEFT}, - {STR_CLIT("Back"), INPUT_KEY_GAMEPAD_SPECIAL_LEFT}, - {STR_CLIT("DPadUp"), INPUT_KEY_GAMEPAD_DPAD_UP}, - {STR_CLIT("dpad_up"), INPUT_KEY_GAMEPAD_DPAD_UP}, - {STR_CLIT("DPadDown"), INPUT_KEY_GAMEPAD_DPAD_DOWN}, - {STR_CLIT("dpad_down"), INPUT_KEY_GAMEPAD_DPAD_DOWN}, - {STR_CLIT("DPadLeft"), INPUT_KEY_GAMEPAD_DPAD_LEFT}, - {STR_CLIT("dpad_left"), INPUT_KEY_GAMEPAD_DPAD_LEFT}, - {STR_CLIT("DPadRight"), INPUT_KEY_GAMEPAD_DPAD_RIGHT}, - {STR_CLIT("dpad_right"), INPUT_KEY_GAMEPAD_DPAD_RIGHT}, + {STR_CLIT("ButtonA"), INPUT_KEY_GAMEPAD_FACE_BOTTOM }, + {STR_CLIT("button_a"), INPUT_KEY_GAMEPAD_FACE_BOTTOM }, + {STR_CLIT("Cross"), INPUT_KEY_GAMEPAD_FACE_BOTTOM }, + {STR_CLIT("GamepadButtonBottom"), INPUT_KEY_GAMEPAD_FACE_BOTTOM }, + {STR_CLIT("ButtonB"), INPUT_KEY_GAMEPAD_FACE_RIGHT }, + {STR_CLIT("button_b"), INPUT_KEY_GAMEPAD_FACE_RIGHT }, + {STR_CLIT("Circle"), INPUT_KEY_GAMEPAD_FACE_RIGHT }, + {STR_CLIT("GamepadButtonRight"), INPUT_KEY_GAMEPAD_FACE_RIGHT }, + {STR_CLIT("ButtonX"), INPUT_KEY_GAMEPAD_FACE_LEFT }, + {STR_CLIT("button_x"), INPUT_KEY_GAMEPAD_FACE_LEFT }, + {STR_CLIT("Square"), INPUT_KEY_GAMEPAD_FACE_LEFT }, + {STR_CLIT("GamepadButtonLeft"), INPUT_KEY_GAMEPAD_FACE_LEFT }, + {STR_CLIT("ButtonY"), INPUT_KEY_GAMEPAD_FACE_TOP }, + {STR_CLIT("button_y"), INPUT_KEY_GAMEPAD_FACE_TOP }, + {STR_CLIT("Triangle"), INPUT_KEY_GAMEPAD_FACE_TOP }, + {STR_CLIT("GamepadButtonTop"), INPUT_KEY_GAMEPAD_FACE_TOP }, + {STR_CLIT("LB"), INPUT_KEY_GAMEPAD_LB }, + {STR_CLIT("L1"), INPUT_KEY_GAMEPAD_LB }, + {STR_CLIT("LeftBumper"), INPUT_KEY_GAMEPAD_LB }, + {STR_CLIT("left_bumper"), INPUT_KEY_GAMEPAD_LB }, + {STR_CLIT("RB"), INPUT_KEY_GAMEPAD_RB }, + {STR_CLIT("R1"), INPUT_KEY_GAMEPAD_RB }, + {STR_CLIT("RightBumper"), INPUT_KEY_GAMEPAD_RB }, + {STR_CLIT("right_bumper"), INPUT_KEY_GAMEPAD_RB }, + {STR_CLIT("LT"), INPUT_KEY_GAMEPAD_LT }, + {STR_CLIT("L2"), INPUT_KEY_GAMEPAD_LT }, + {STR_CLIT("LeftTrigger"), INPUT_KEY_GAMEPAD_LT }, + {STR_CLIT("left_trigger"), INPUT_KEY_GAMEPAD_LT }, + {STR_CLIT("RT"), INPUT_KEY_GAMEPAD_RT }, + {STR_CLIT("R2"), INPUT_KEY_GAMEPAD_RT }, + {STR_CLIT("RightTrigger"), INPUT_KEY_GAMEPAD_RT }, + {STR_CLIT("right_trigger"), INPUT_KEY_GAMEPAD_RT }, + {STR_CLIT("LS"), INPUT_KEY_GAMEPAD_L3 }, + {STR_CLIT("L3"), INPUT_KEY_GAMEPAD_L3 }, + {STR_CLIT("LeftStick"), INPUT_KEY_GAMEPAD_L3 }, + {STR_CLIT("left_stick"), INPUT_KEY_GAMEPAD_L3 }, + {STR_CLIT("RS"), INPUT_KEY_GAMEPAD_R3 }, + {STR_CLIT("R3"), INPUT_KEY_GAMEPAD_R3 }, + {STR_CLIT("RightStick"), INPUT_KEY_GAMEPAD_R3 }, + {STR_CLIT("right_stick"), INPUT_KEY_GAMEPAD_R3 }, + {STR_CLIT("Start"), INPUT_KEY_GAMEPAD_SPECIAL_RIGHT}, + {STR_CLIT("Options"), INPUT_KEY_GAMEPAD_SPECIAL_RIGHT}, + {STR_CLIT("Select"), INPUT_KEY_GAMEPAD_SPECIAL_LEFT }, + {STR_CLIT("Share"), INPUT_KEY_GAMEPAD_SPECIAL_LEFT }, + {STR_CLIT("Back"), INPUT_KEY_GAMEPAD_SPECIAL_LEFT }, + {STR_CLIT("DPadUp"), INPUT_KEY_GAMEPAD_DPAD_UP }, + {STR_CLIT("dpad_up"), INPUT_KEY_GAMEPAD_DPAD_UP }, + {STR_CLIT("DPadDown"), INPUT_KEY_GAMEPAD_DPAD_DOWN }, + {STR_CLIT("dpad_down"), INPUT_KEY_GAMEPAD_DPAD_DOWN }, + {STR_CLIT("DPadLeft"), INPUT_KEY_GAMEPAD_DPAD_LEFT }, + {STR_CLIT("dpad_left"), INPUT_KEY_GAMEPAD_DPAD_LEFT }, + {STR_CLIT("DPadRight"), INPUT_KEY_GAMEPAD_DPAD_RIGHT }, + {STR_CLIT("dpad_right"), INPUT_KEY_GAMEPAD_DPAD_RIGHT }, }; keybind_t @@ -758,6 +754,11 @@ keybind_parse(str_t str, keybind_t default_val) { keybind_t result = {0}; + str = str_trim_space(str); + if (str_is_empty(str) || str_equal(str, STR_LIT("None"), STR_CMP_FLAG_IGNORE_CASE)) { + return KEYBIND_NULL; + } + uint64_t pos = 0; while (pos < str.len) { /* find next '+' or end */ @@ -768,8 +769,7 @@ keybind_parse(str_t str, keybind_t default_val) str_t token = str_trim_space(str_slice(str, pos, end)); if (token.len == 0) { - pos = end + 1; - continue; + return default_val; } if (result.count >= KEYBIND_MAX_KEYS) { @@ -793,6 +793,12 @@ keybind_parse(str_t str, keybind_t default_val) return default_val; } + for (int i = 0; i < result.count; ++i) { + if (result.keys[i] == key) { + return default_val; + } + } + result.keys[result.count++] = key; pos = end + 1; } @@ -885,14 +891,16 @@ keybind_to_str(keybind_t bind, arena_t *arena) bool keybind_is_valid(keybind_t bind) { - bool is_empty = true; + if (bind.count == 0 || bind.count > KEYBIND_MAX_KEYS) { + return false; + } + for (int i = 0; i < bind.count; ++i) { - if (bind.keys[i] != INPUT_KEY_NONE) { - is_empty = false; - break; + if (bind.keys[i] == INPUT_KEY_NONE || bind.keys[i] >= INPUT_KEY_MAX) { + return false; } } - return !is_empty; + return true; } bool @@ -902,11 +910,21 @@ keybind_equal(keybind_t a, keybind_t b) return false; } - for (uint8_t i = 0; i < a.count; ++i) { - if (a.keys[i] != b.keys[i]) { + for (int i = 0; i < a.count; ++i) { + bool found = false; + + for (int j = 0; j < b.count; ++j) { + if (a.keys[i] == b.keys[j]) { + found = true; + break; + } + } + + if (!found) { return false; } } + return true; } @@ -925,51 +943,80 @@ keybind_is_down(keybind_t bind) return false; } + bool all_down = true; for (int i = 0; i < bind.count; ++i) { - if (!input_key_is_down(bind.keys[i])) { - return false; - } + all_down = all_down && input_key_is_down(bind.keys[i]); } - return true; + return all_down; } bool -keybind_was_down(keybind_t bind) +keybind_is_pressed(keybind_t bind) { - if (bind.count == 0) { + if (!keybind_is_down(bind)) { return false; } for (int i = 0; i < bind.count; ++i) { - if (!input_key_was_down(bind.keys[i])) { - return false; + if (input_key_is_pressed(bind.keys[i])) { + return true; } } - return true; + return false; } bool -keybind_is_pressed(keybind_t bind) +keybind_is_released(keybind_t bind) { - return keybind_is_down(bind) && !keybind_was_down(bind); + if (bind.count == 0) { + return false; + } + + bool all_down_or_released = true; + bool any_released = false; + + for (int i = 0; i < bind.count; ++i) { + bool released = input_key_is_released(bind.keys[i]); + + all_down_or_released = all_down_or_released && (input_key_is_down(bind.keys[i]) || released); + any_released = any_released || released; + } + + return all_down_or_released && any_released; } -bool -keybind_is_released(keybind_t bind) +static bool +keybind_key_matches_event(input_key_kind_t bind_key, input_key_kind_t event_key) { - return !keybind_is_down(bind) && keybind_was_down(bind); + switch (bind_key) { + case INPUT_KEY_CTRL: return event_key == INPUT_KEY_LEFT_CTRL || event_key == INPUT_KEY_RIGHT_CTRL; + case INPUT_KEY_SHIFT: return event_key == INPUT_KEY_LEFT_SHIFT || event_key == INPUT_KEY_RIGHT_SHIFT; + case INPUT_KEY_ALT: return event_key == INPUT_KEY_LEFT_ALT || event_key == INPUT_KEY_RIGHT_ALT; + default: return bind_key == event_key; + } } bool -input_event_covers_keybind(input_event_t *ev, keybind_t bind) +keybind_activated_by_event(keybind_t bind, input_event_t *ev) { - bool has_event_key = false; + if (ev->kind != INPUT_EVENT_KEY_DOWN && + ev->kind != INPUT_EVENT_MOUSE_DOWN && + ev->kind != INPUT_EVENT_MOUSE_DBLCLICK) { + return false; + } + + if (ev->is_repeat) { + return false; + } + + bool involved = false; for (int i = 0; i < bind.count; ++i) { - if (bind.keys[i] == ev->key) { - has_event_key = true; + if (keybind_key_matches_event(bind.keys[i], ev->key)) { + involved = true; break; } } - return has_event_key; + + return involved && keybind_is_down(bind); } diff --git a/src/main.c b/src/main.c index 16c94ee..0f613e6 100644 --- a/src/main.c +++ b/src/main.c @@ -92,9 +92,12 @@ on_engine_init(void) LOG_INFO("Engine initialized"); globals.game_thread_id = GetCurrentThreadId(); - LOG_DEBUG("on_engine_init: Thread ID: %lu", GetCurrentThreadId()); + input_key_map_init(); + LOG_INFO("Input key map initialized"); + + enable_input_hooks(); unreal_common_collect(&globals.unreal); mod_manager_start_dlls(&globals.mod_manager); @@ -158,8 +161,6 @@ on_engine_tick_pre(float delta) static void on_engine_tick_post(void) { - mem_copy(globals.prev_keys_down, globals.keys_down, sizeof(globals.prev_keys_down)); - if (globals.ui_manager.inited && globals.ui_manager.ctx) { unsigned int vw = 0; unsigned int vh = 0; @@ -172,40 +173,120 @@ on_engine_tick_post(void) scratch_reset(); globals.frame_counter += 1; + input_key_clear_transient_state(); } -static bool -on_input_event(input_event_t *ev) +static void +input_key_down(input_key_state_t *key) +{ + if (!key->down) { + key->pressed = true; + } + key->down = true; +} + +static void +input_key_up(input_key_state_t *key) +{ + if (key->down) { + key->released = true; + } + key->down = false; +} + +static void +input_sync_virtual_modifier(input_key_kind_t key) { - if (!globals.input_inited) { - input_key_map_init(); - globals.input_inited = true; + input_key_kind_t virt, left, right; + + switch (key) { + case INPUT_KEY_LEFT_CTRL: + case INPUT_KEY_RIGHT_CTRL: { + virt = INPUT_KEY_CTRL; + left = INPUT_KEY_LEFT_CTRL; + right = INPUT_KEY_RIGHT_CTRL; + break; + } + + case INPUT_KEY_LEFT_SHIFT: + case INPUT_KEY_RIGHT_SHIFT: { + virt = INPUT_KEY_SHIFT; + left = INPUT_KEY_LEFT_SHIFT; + right = INPUT_KEY_RIGHT_SHIFT; + break; + } - LOG_INFO("Input key map initialized"); + case INPUT_KEY_LEFT_ALT: + case INPUT_KEY_RIGHT_ALT: { + virt = INPUT_KEY_ALT; + left = INPUT_KEY_LEFT_ALT; + right = INPUT_KEY_RIGHT_ALT; + break; + } + + default: { + return; + } } - globals.keys_down[INPUT_KEY_MOUSE_WHEEL_UP] = false; - globals.keys_down[INPUT_KEY_MOUSE_WHEEL_DOWN] = false; + input_key_state_t *state = &globals.keys[virt]; + bool down = globals.keys[left].down || globals.keys[right].down; + + if (down) { + input_key_down(state); + } else { + input_key_up(state); + } +} + +static bool +on_input_event(input_event_t *ev) +{ + ASSERT(ev->key < INPUT_KEY_MAX); switch (ev->kind) { - case INPUT_EVENT_KEY_DOWN: - case INPUT_EVENT_MOUSE_DOWN: - globals.keys_down[ev->key] = true; - break; - case INPUT_EVENT_KEY_UP: - case INPUT_EVENT_MOUSE_UP: - globals.keys_down[ev->key] = false; - break; - case INPUT_EVENT_MOUSE_WHEEL: - /* MOUSE_WHEEL_UP or MOUSE_WHEEL_DOWN */ - globals.keys_down[ev->key] = true; - break; - case INPUT_EVENT_APP_ACTIVATION: - if (!ev->app_activated) { - /* out of focus, all held keys should be released */ - mem_zero(globals.keys_down, sizeof(globals.keys_down)); + case INPUT_EVENT_KEY_DOWN: + case INPUT_EVENT_MOUSE_DOWN: + case INPUT_EVENT_MOUSE_DBLCLICK: { + if (ev->key != INPUT_KEY_NONE) { + input_key_down(&globals.keys[ev->key]); + input_sync_virtual_modifier(ev->key); + } + break; } - break; + + case INPUT_EVENT_KEY_UP: + case INPUT_EVENT_MOUSE_UP: { + if (ev->key != INPUT_KEY_NONE) { + input_key_up(&globals.keys[ev->key]); + input_sync_virtual_modifier(ev->key); + } + break; + } + + case INPUT_EVENT_MOUSE_WHEEL: { + if (ev->wheel_delta > 0.0f) { + globals.keys[INPUT_KEY_MOUSE_WHEEL_UP].pressed = true; + } else if (ev->wheel_delta < 0.0f) { + globals.keys[INPUT_KEY_MOUSE_WHEEL_DOWN].pressed = true; + } + globals.wheel_delta += ev->wheel_delta; + break; + } + + case INPUT_EVENT_APP_ACTIVATION: { + if (!ev->app_activated) { + /* out of focus, all held keys should be released */ + input_key_lost_focus(); + } + break; + } + } + + bool force_consume = false; + if ((ev->kind == INPUT_EVENT_KEY_UP || ev->kind == INPUT_EVENT_MOUSE_UP) && ev->key != INPUT_KEY_NONE && globals.consumed_down[ev->key]) { + globals.consumed_down[ev->key] = false; + force_consume = true; } bool consumed = false; @@ -226,8 +307,6 @@ on_input_event(input_event_t *ev) consumer = "UI (post)"; } - (void)consumer; - #if 0 if (ev->kind != INPUT_EVENT_MOUSE_MOVE) { str_t kind_str = input_event_kind_to_str(ev->kind); @@ -237,8 +316,15 @@ on_input_event(input_event_t *ev) LOG_WARN("%-10s: kind: %.*s, key: %.*s", consumer, STR_ARG(kind_str), STR_ARG(input_key_to_str(ev->key))); } } +#else + (void)consumer; #endif - return consumed; + + if (consumed && (ev->kind == INPUT_EVENT_KEY_DOWN || ev->kind == INPUT_EVENT_MOUSE_DOWN) && ev->key != INPUT_KEY_NONE) { + globals.consumed_down[ev->key] = true; + } + + return consumed || force_consume; } static DWORD WINAPI diff --git a/src/mod_host.c b/src/mod_host.c index d5c60c2..8f05e99 100644 --- a/src/mod_host.c +++ b/src/mod_host.c @@ -385,9 +385,15 @@ host_keybind_is_down(keybind_t bind) } static bool MOD_CALL -host_keybind_was_down(keybind_t bind) +host_keybind_is_pressed(keybind_t bind) { - return keybind_was_down(bind); + return keybind_is_pressed(bind); +} + +static bool MOD_CALL +host_keybind_is_released(keybind_t bind) +{ + return keybind_is_released(bind); } static bool MOD_CALL @@ -581,7 +587,8 @@ static const mod_host_api_t g_host_api = { .keybind_utf8_len = host_keybind_utf8_len, .keybind_utf8_write = host_keybind_utf8_write, .keybind_is_down = host_keybind_is_down, - .keybind_was_down = host_keybind_was_down, + .keybind_is_pressed = host_keybind_is_pressed, + .keybind_is_released = host_keybind_is_released, /* UOBJECT CREATE/DELETE LISTENERS */ diff --git a/src/ui_manager.c b/src/ui_manager.c index 4fc78a3..ef4d8dc 100644 --- a/src/ui_manager.c +++ b/src/ui_manager.c @@ -628,7 +628,7 @@ feed_input(ui_manager_t *manager, input_event_t *ev) int down = (ev->kind == INPUT_EVENT_KEY_DOWN) ? 1 : 0; nk_input_key(ctx, NK_KEY_SHIFT, (ev->modifiers & INPUT_MOD_SHIFT) != 0); - nk_input_key(ctx, NK_KEY_CTRL, (ev->modifiers & INPUT_MOD_CTRL) != 0); + nk_input_key(ctx, NK_KEY_CTRL, (ev->modifiers & INPUT_MOD_CTRL) != 0); switch (ev->key) { case INPUT_KEY_BACKSPACE: { @@ -752,14 +752,14 @@ ui_manager_on_input_event_pre(ui_manager_t *manager, uint64_t frame_counter, inp return true; } - if (input_event_covers_keybind(ev, manager->mod_manager->cfg.overlay_toggle)) { + if (keybind_activated_by_event(manager->mod_manager->cfg.overlay_toggle, ev)) { if (keybind_is_pressed(manager->mod_manager->cfg.overlay_toggle)) { ui_mod_manager_toggle(&manager->main); } return true; } - if (input_event_covers_keybind(ev, manager->console.cfg.toggle_bind)) { + if (keybind_activated_by_event(manager->console.cfg.toggle_bind, ev)) { if (keybind_is_pressed(manager->console.cfg.toggle_bind)) { ui_console_toggle(&manager->console); } From 7119798d86104d0bb3819f5bed117f861ba4f72c Mon Sep 17 00:00:00 2001 From: akmubi Date: Tue, 25 Aug 2026 19:31:49 +0500 Subject: [PATCH 3/8] fix build error --- src/input.c | 272 ++++++++++++++++++++++++++-------------------------- 1 file changed, 136 insertions(+), 136 deletions(-) diff --git a/src/input.c b/src/input.c index fddbcbc..4e6975d 100644 --- a/src/input.c +++ b/src/input.c @@ -19,166 +19,166 @@ struct input_key_map_entry_s { static input_key_map_entry_t g_key_map[INPUT_KEY_MAX] = { /* keyboard: letters */ - [INPUT_KEY_A] = {STR_CLIT("A"), FNAME_NONE}, - [INPUT_KEY_B] = {STR_CLIT("B"), FNAME_NONE}, - [INPUT_KEY_C] = {STR_CLIT("C"), FNAME_NONE}, - [INPUT_KEY_D] = {STR_CLIT("D"), FNAME_NONE}, - [INPUT_KEY_E] = {STR_CLIT("E"), FNAME_NONE}, - [INPUT_KEY_F] = {STR_CLIT("F"), FNAME_NONE}, - [INPUT_KEY_G] = {STR_CLIT("G"), FNAME_NONE}, - [INPUT_KEY_H] = {STR_CLIT("H"), FNAME_NONE}, - [INPUT_KEY_I] = {STR_CLIT("I"), FNAME_NONE}, - [INPUT_KEY_J] = {STR_CLIT("J"), FNAME_NONE}, - [INPUT_KEY_K] = {STR_CLIT("K"), FNAME_NONE}, - [INPUT_KEY_L] = {STR_CLIT("L"), FNAME_NONE}, - [INPUT_KEY_M] = {STR_CLIT("M"), FNAME_NONE}, - [INPUT_KEY_N] = {STR_CLIT("N"), FNAME_NONE}, - [INPUT_KEY_O] = {STR_CLIT("O"), FNAME_NONE}, - [INPUT_KEY_P] = {STR_CLIT("P"), FNAME_NONE}, - [INPUT_KEY_Q] = {STR_CLIT("Q"), FNAME_NONE}, - [INPUT_KEY_R] = {STR_CLIT("R"), FNAME_NONE}, - [INPUT_KEY_S] = {STR_CLIT("S"), FNAME_NONE}, - [INPUT_KEY_T] = {STR_CLIT("T"), FNAME_NONE}, - [INPUT_KEY_U] = {STR_CLIT("U"), FNAME_NONE}, - [INPUT_KEY_V] = {STR_CLIT("V"), FNAME_NONE}, - [INPUT_KEY_W] = {STR_CLIT("W"), FNAME_NONE}, - [INPUT_KEY_X] = {STR_CLIT("X"), FNAME_NONE}, - [INPUT_KEY_Y] = {STR_CLIT("Y"), FNAME_NONE}, - [INPUT_KEY_Z] = {STR_CLIT("Z"), FNAME_NONE}, + [INPUT_KEY_A] = {.str = STR_CLIT("A")}, + [INPUT_KEY_B] = {.str = STR_CLIT("B")}, + [INPUT_KEY_C] = {.str = STR_CLIT("C")}, + [INPUT_KEY_D] = {.str = STR_CLIT("D")}, + [INPUT_KEY_E] = {.str = STR_CLIT("E")}, + [INPUT_KEY_F] = {.str = STR_CLIT("F")}, + [INPUT_KEY_G] = {.str = STR_CLIT("G")}, + [INPUT_KEY_H] = {.str = STR_CLIT("H")}, + [INPUT_KEY_I] = {.str = STR_CLIT("I")}, + [INPUT_KEY_J] = {.str = STR_CLIT("J")}, + [INPUT_KEY_K] = {.str = STR_CLIT("K")}, + [INPUT_KEY_L] = {.str = STR_CLIT("L")}, + [INPUT_KEY_M] = {.str = STR_CLIT("M")}, + [INPUT_KEY_N] = {.str = STR_CLIT("N")}, + [INPUT_KEY_O] = {.str = STR_CLIT("O")}, + [INPUT_KEY_P] = {.str = STR_CLIT("P")}, + [INPUT_KEY_Q] = {.str = STR_CLIT("Q")}, + [INPUT_KEY_R] = {.str = STR_CLIT("R")}, + [INPUT_KEY_S] = {.str = STR_CLIT("S")}, + [INPUT_KEY_T] = {.str = STR_CLIT("T")}, + [INPUT_KEY_U] = {.str = STR_CLIT("U")}, + [INPUT_KEY_V] = {.str = STR_CLIT("V")}, + [INPUT_KEY_W] = {.str = STR_CLIT("W")}, + [INPUT_KEY_X] = {.str = STR_CLIT("X")}, + [INPUT_KEY_Y] = {.str = STR_CLIT("Y")}, + [INPUT_KEY_Z] = {.str = STR_CLIT("Z")}, /* keyboard: punctuation */ - [INPUT_KEY_COMMA] = {STR_CLIT("Comma"), FNAME_NONE}, - [INPUT_KEY_PERIOD] = {STR_CLIT("Period"), FNAME_NONE}, - [INPUT_KEY_SLASH] = {STR_CLIT("Slash"), FNAME_NONE}, - [INPUT_KEY_SEMICOLON] = {STR_CLIT("Semicolon"), FNAME_NONE}, - [INPUT_KEY_APOSTROPHE] = {STR_CLIT("Apostrophe"), FNAME_NONE}, - [INPUT_KEY_LEFT_BRACKET] = {STR_CLIT("LeftBracket"), FNAME_NONE}, - [INPUT_KEY_RIGHT_BRACKET] = {STR_CLIT("RightBracket"), FNAME_NONE}, - [INPUT_KEY_BACKSLASH] = {STR_CLIT("Backslash"), FNAME_NONE}, - [INPUT_KEY_HYPHEN] = {STR_CLIT("Hyphen"), FNAME_NONE}, - [INPUT_KEY_EQUALS] = {STR_CLIT("Equals"), FNAME_NONE}, - [INPUT_KEY_BACKTICK] = {STR_CLIT("Tilde"), FNAME_NONE}, + [INPUT_KEY_COMMA] = {.str = STR_CLIT("Comma") }, + [INPUT_KEY_PERIOD] = {.str = STR_CLIT("Period") }, + [INPUT_KEY_SLASH] = {.str = STR_CLIT("Slash") }, + [INPUT_KEY_SEMICOLON] = {.str = STR_CLIT("Semicolon") }, + [INPUT_KEY_APOSTROPHE] = {.str = STR_CLIT("Apostrophe") }, + [INPUT_KEY_LEFT_BRACKET] = {.str = STR_CLIT("LeftBracket") }, + [INPUT_KEY_RIGHT_BRACKET] = {.str = STR_CLIT("RightBracket")}, + [INPUT_KEY_BACKSLASH] = {.str = STR_CLIT("Backslash") }, + [INPUT_KEY_HYPHEN] = {.str = STR_CLIT("Hyphen") }, + [INPUT_KEY_EQUALS] = {.str = STR_CLIT("Equals") }, + [INPUT_KEY_BACKTICK] = {.str = STR_CLIT("Tilde") }, /* keyboard: digits */ - [INPUT_KEY_1] = {STR_CLIT("One"), FNAME_NONE}, - [INPUT_KEY_2] = {STR_CLIT("Two"), FNAME_NONE}, - [INPUT_KEY_3] = {STR_CLIT("Three"), FNAME_NONE}, - [INPUT_KEY_4] = {STR_CLIT("Four"), FNAME_NONE}, - [INPUT_KEY_5] = {STR_CLIT("Five"), FNAME_NONE}, - [INPUT_KEY_6] = {STR_CLIT("Six"), FNAME_NONE}, - [INPUT_KEY_7] = {STR_CLIT("Seven"), FNAME_NONE}, - [INPUT_KEY_8] = {STR_CLIT("Eight"), FNAME_NONE}, - [INPUT_KEY_9] = {STR_CLIT("Nine"), FNAME_NONE}, - [INPUT_KEY_0] = {STR_CLIT("Zero"), FNAME_NONE}, + [INPUT_KEY_1] = {.str = STR_CLIT("One") }, + [INPUT_KEY_2] = {.str = STR_CLIT("Two") }, + [INPUT_KEY_3] = {.str = STR_CLIT("Three")}, + [INPUT_KEY_4] = {.str = STR_CLIT("Four") }, + [INPUT_KEY_5] = {.str = STR_CLIT("Five") }, + [INPUT_KEY_6] = {.str = STR_CLIT("Six") }, + [INPUT_KEY_7] = {.str = STR_CLIT("Seven")}, + [INPUT_KEY_8] = {.str = STR_CLIT("Eight")}, + [INPUT_KEY_9] = {.str = STR_CLIT("Nine") }, + [INPUT_KEY_0] = {.str = STR_CLIT("Zero") }, /* keyboard: function */ - [INPUT_KEY_F1] = {STR_CLIT("F1"), FNAME_NONE}, - [INPUT_KEY_F2] = {STR_CLIT("F2"), FNAME_NONE}, - [INPUT_KEY_F3] = {STR_CLIT("F3"), FNAME_NONE}, - [INPUT_KEY_F4] = {STR_CLIT("F4"), FNAME_NONE}, - [INPUT_KEY_F5] = {STR_CLIT("F5"), FNAME_NONE}, - [INPUT_KEY_F6] = {STR_CLIT("F6"), FNAME_NONE}, - [INPUT_KEY_F7] = {STR_CLIT("F7"), FNAME_NONE}, - [INPUT_KEY_F8] = {STR_CLIT("F8"), FNAME_NONE}, - [INPUT_KEY_F9] = {STR_CLIT("F9"), FNAME_NONE}, - [INPUT_KEY_F10] = {STR_CLIT("F10"), FNAME_NONE}, - [INPUT_KEY_F11] = {STR_CLIT("F11"), FNAME_NONE}, - [INPUT_KEY_F12] = {STR_CLIT("F12"), FNAME_NONE}, + [INPUT_KEY_F1] = {.str = STR_CLIT("F1") }, + [INPUT_KEY_F2] = {.str = STR_CLIT("F2") }, + [INPUT_KEY_F3] = {.str = STR_CLIT("F3") }, + [INPUT_KEY_F4] = {.str = STR_CLIT("F4") }, + [INPUT_KEY_F5] = {.str = STR_CLIT("F5") }, + [INPUT_KEY_F6] = {.str = STR_CLIT("F6") }, + [INPUT_KEY_F7] = {.str = STR_CLIT("F7") }, + [INPUT_KEY_F8] = {.str = STR_CLIT("F8") }, + [INPUT_KEY_F9] = {.str = STR_CLIT("F9") }, + [INPUT_KEY_F10] = {.str = STR_CLIT("F10")}, + [INPUT_KEY_F11] = {.str = STR_CLIT("F11")}, + [INPUT_KEY_F12] = {.str = STR_CLIT("F12")}, /* keyboard: navigation */ - [INPUT_KEY_UP] = {STR_CLIT("Up"), FNAME_NONE}, - [INPUT_KEY_DOWN] = {STR_CLIT("Down"), FNAME_NONE}, - [INPUT_KEY_LEFT] = {STR_CLIT("Left"), FNAME_NONE}, - [INPUT_KEY_RIGHT] = {STR_CLIT("Right"), FNAME_NONE}, - [INPUT_KEY_HOME] = {STR_CLIT("Home"), FNAME_NONE}, - [INPUT_KEY_END] = {STR_CLIT("End"), FNAME_NONE}, - [INPUT_KEY_PAGE_UP] = {STR_CLIT("PageUp"), FNAME_NONE}, - [INPUT_KEY_PAGE_DOWN] = {STR_CLIT("PageDown"), FNAME_NONE}, + [INPUT_KEY_UP] = {.str = STR_CLIT("Up") }, + [INPUT_KEY_DOWN] = {.str = STR_CLIT("Down") }, + [INPUT_KEY_LEFT] = {.str = STR_CLIT("Left") }, + [INPUT_KEY_RIGHT] = {.str = STR_CLIT("Right") }, + [INPUT_KEY_HOME] = {.str = STR_CLIT("Home") }, + [INPUT_KEY_END] = {.str = STR_CLIT("End") }, + [INPUT_KEY_PAGE_UP] = {.str = STR_CLIT("PageUp") }, + [INPUT_KEY_PAGE_DOWN] = {.str = STR_CLIT("PageDown")}, /* keyboard: modifiers */ - [INPUT_KEY_LEFT_SHIFT] = {STR_CLIT("LeftShift"), FNAME_NONE}, - [INPUT_KEY_RIGHT_SHIFT] = {STR_CLIT("RightShift"), FNAME_NONE}, - [INPUT_KEY_LEFT_CTRL] = {STR_CLIT("LeftControl"), FNAME_NONE}, - [INPUT_KEY_RIGHT_CTRL] = {STR_CLIT("RightControl"), FNAME_NONE}, - [INPUT_KEY_LEFT_ALT] = {STR_CLIT("LeftAlt"), FNAME_NONE}, - [INPUT_KEY_RIGHT_ALT] = {STR_CLIT("RightAlt"), FNAME_NONE}, - - /* keyboard: virtual modifiers (either left or right) */ - [INPUT_KEY_SHIFT] = {STR_CLIT("Shift"), FNAME_NONE}, // not actual name - [INPUT_KEY_CTRL] = {STR_CLIT("Ctrl"), FNAME_NONE}, // not actual name - [INPUT_KEY_ALT] = {STR_CLIT("Alt"), FNAME_NONE}, // not actual name + [INPUT_KEY_LEFT_SHIFT] = {.str = STR_CLIT("LeftShift") }, + [INPUT_KEY_RIGHT_SHIFT] = {.str = STR_CLIT("RightShift") }, + [INPUT_KEY_LEFT_CTRL] = {.str = STR_CLIT("LeftControl") }, + [INPUT_KEY_RIGHT_CTRL] = {.str = STR_CLIT("RightControl")}, + [INPUT_KEY_LEFT_ALT] = {.str = STR_CLIT("LeftAlt") }, + [INPUT_KEY_RIGHT_ALT] = {.str = STR_CLIT("RightAlt") }, + + /* keyboard: virtual modifiers (either left o right) */ + [INPUT_KEY_SHIFT] = {.str = STR_CLIT("Shift")}, // not actual name + [INPUT_KEY_CTRL] = {.str = STR_CLIT("Ctrl") }, // not actual name + [INPUT_KEY_ALT] = {.str = STR_CLIT("Alt") }, // not actual name /* keyboard: common */ - [INPUT_KEY_SPACE] = {STR_CLIT("SpaceBar"), FNAME_NONE}, - [INPUT_KEY_ENTER] = {STR_CLIT("Enter"), FNAME_NONE}, - [INPUT_KEY_ESCAPE] = {STR_CLIT("Escape"), FNAME_NONE}, - [INPUT_KEY_TAB] = {STR_CLIT("Tab"), FNAME_NONE}, - [INPUT_KEY_BACKSPACE] = {STR_CLIT("BackSpace"), FNAME_NONE}, - [INPUT_KEY_CAPS_LOCK] = {STR_CLIT("CapsLock"), FNAME_NONE}, - [INPUT_KEY_INSERT] = {STR_CLIT("Insert"), FNAME_NONE}, - [INPUT_KEY_DELETE] = {STR_CLIT("Delete"), FNAME_NONE}, + [INPUT_KEY_SPACE] = {.str = STR_CLIT("SpaceBar") }, + [INPUT_KEY_ENTER] = {.str = STR_CLIT("Enter") }, + [INPUT_KEY_ESCAPE] = {.str = STR_CLIT("Escape") }, + [INPUT_KEY_TAB] = {.str = STR_CLIT("Tab") }, + [INPUT_KEY_BACKSPACE] = {.str = STR_CLIT("BackSpace")}, + [INPUT_KEY_CAPS_LOCK] = {.str = STR_CLIT("CapsLock") }, + [INPUT_KEY_INSERT] = {.str = STR_CLIT("Insert") }, + [INPUT_KEY_DELETE] = {.str = STR_CLIT("Delete") }, /* keyboard: numpad */ - [INPUT_KEY_NUM_0] = {STR_CLIT("NumPadZero"), FNAME_NONE}, - [INPUT_KEY_NUM_1] = {STR_CLIT("NumPadOne"), FNAME_NONE}, - [INPUT_KEY_NUM_2] = {STR_CLIT("NumPadTwo"), FNAME_NONE}, - [INPUT_KEY_NUM_3] = {STR_CLIT("NumPadThree"), FNAME_NONE}, - [INPUT_KEY_NUM_4] = {STR_CLIT("NumPadFour"), FNAME_NONE}, - [INPUT_KEY_NUM_5] = {STR_CLIT("NumPadFive"), FNAME_NONE}, - [INPUT_KEY_NUM_6] = {STR_CLIT("NumPadSix"), FNAME_NONE}, - [INPUT_KEY_NUM_7] = {STR_CLIT("NumPadSeven"), FNAME_NONE}, - [INPUT_KEY_NUM_8] = {STR_CLIT("NumPadEight"), FNAME_NONE}, - [INPUT_KEY_NUM_9] = {STR_CLIT("NumPadNine"), FNAME_NONE}, - [INPUT_KEY_NUM_MULTIPLY] = {STR_CLIT("Multiply"), FNAME_NONE}, - [INPUT_KEY_NUM_ADD] = {STR_CLIT("Add"), FNAME_NONE}, - [INPUT_KEY_NUM_SUBTRACT] = {STR_CLIT("Subtract"), FNAME_NONE}, - [INPUT_KEY_NUM_DECIMAL] = {STR_CLIT("Decimal"), FNAME_NONE}, - [INPUT_KEY_NUM_DIVIDE] = {STR_CLIT("Divide"), FNAME_NONE}, + [INPUT_KEY_NUM_0] = {.str = STR_CLIT("NumPadZero") }, + [INPUT_KEY_NUM_1] = {.str = STR_CLIT("NumPadOne") }, + [INPUT_KEY_NUM_2] = {.str = STR_CLIT("NumPadTwo") }, + [INPUT_KEY_NUM_3] = {.str = STR_CLIT("NumPadThree")}, + [INPUT_KEY_NUM_4] = {.str = STR_CLIT("NumPadFour") }, + [INPUT_KEY_NUM_5] = {.str = STR_CLIT("NumPadFive") }, + [INPUT_KEY_NUM_6] = {.str = STR_CLIT("NumPadSix") }, + [INPUT_KEY_NUM_7] = {.str = STR_CLIT("NumPadSeven")}, + [INPUT_KEY_NUM_8] = {.str = STR_CLIT("NumPadEight")}, + [INPUT_KEY_NUM_9] = {.str = STR_CLIT("NumPadNine") }, + [INPUT_KEY_NUM_MULTIPLY] = {.str = STR_CLIT("Multiply") }, + [INPUT_KEY_NUM_ADD] = {.str = STR_CLIT("Add") }, + [INPUT_KEY_NUM_SUBTRACT] = {.str = STR_CLIT("Subtract") }, + [INPUT_KEY_NUM_DECIMAL] = {.str = STR_CLIT("Decimal") }, + [INPUT_KEY_NUM_DIVIDE] = {.str = STR_CLIT("Divide") }, /* keyboard: misc */ - [INPUT_KEY_PAUSE] = {STR_CLIT("Pause"), FNAME_NONE}, - [INPUT_KEY_PRINT_SCREEN] = {STR_CLIT("PrintScreen"), FNAME_NONE}, - [INPUT_KEY_SCROLL_LOCK] = {STR_CLIT("ScrollLock"), FNAME_NONE}, - [INPUT_KEY_NUM_LOCK] = {STR_CLIT("NumLock"), FNAME_NONE}, + [INPUT_KEY_PAUSE] = {.str = STR_CLIT("Pause") }, + [INPUT_KEY_PRINT_SCREEN] = {.str = STR_CLIT("PrintScreen")}, + [INPUT_KEY_SCROLL_LOCK] = {.str = STR_CLIT("ScrollLock") }, + [INPUT_KEY_NUM_LOCK] = {.str = STR_CLIT("NumLock") }, /* mouse: buttons */ - [INPUT_KEY_MOUSE_LEFT] = {STR_CLIT("LeftMouseButton"), FNAME_NONE}, - [INPUT_KEY_MOUSE_RIGHT] = {STR_CLIT("RightMouseButton"), FNAME_NONE}, - [INPUT_KEY_MOUSE_MIDDLE] = {STR_CLIT("MiddleMouseButton"), FNAME_NONE}, - [INPUT_KEY_MOUSE_THUMB_1] = {STR_CLIT("ThumbMouseButton"), FNAME_NONE}, - [INPUT_KEY_MOUSE_THUMB_2] = {STR_CLIT("ThumbMouseButton2"), FNAME_NONE}, + [INPUT_KEY_MOUSE_LEFT] = {.str = STR_CLIT("LeftMouseButton") }, + [INPUT_KEY_MOUSE_RIGHT] = {.str = STR_CLIT("RightMouseButton") }, + [INPUT_KEY_MOUSE_MIDDLE] = {.str = STR_CLIT("MiddleMouseButton")}, + [INPUT_KEY_MOUSE_THUMB_1] = {.str = STR_CLIT("ThumbMouseButton") }, + [INPUT_KEY_MOUSE_THUMB_2] = {.str = STR_CLIT("ThumbMouseButton2")}, /* mouse: axes */ - [INPUT_KEY_MOUSE_X] = {STR_CLIT("MouseX"), FNAME_NONE}, - [INPUT_KEY_MOUSE_Y] = {STR_CLIT("MouseY"), FNAME_NONE}, - [INPUT_KEY_MOUSE_WHEEL_UP] = {STR_CLIT("MouseWheelUp"), FNAME_NONE}, // not an actual name - [INPUT_KEY_MOUSE_WHEEL_DOWN] = {STR_CLIT("MouseWheelDown"), FNAME_NONE}, // not an actual name + [INPUT_KEY_MOUSE_X] = {.str = STR_CLIT("MouseX") }, + [INPUT_KEY_MOUSE_Y] = {.str = STR_CLIT("MouseY") }, + [INPUT_KEY_MOUSE_WHEEL_UP] = {.str = STR_CLIT("MouseWheelUp") }, // not an actual name + [INPUT_KEY_MOUSE_WHEEL_DOWN] = {.str = STR_CLIT("MouseWheelDown")}, // not an actual name /* gamepad: buttons */ - [INPUT_KEY_GAMEPAD_FACE_BOTTOM] = {STR_CLIT("Gamepad_FaceButton_Bottom"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_FACE_RIGHT] = {STR_CLIT("Gamepad_FaceButton_Right"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_FACE_LEFT] = {STR_CLIT("Gamepad_FaceButton_Left"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_FACE_TOP] = {STR_CLIT("Gamepad_FaceButton_Top"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_LB] = {STR_CLIT("Gamepad_LeftShoulder"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_RB] = {STR_CLIT("Gamepad_RightShoulder"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_LT] = {STR_CLIT("Gamepad_LeftTrigger"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_RT] = {STR_CLIT("Gamepad_RightTrigger"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_L3] = {STR_CLIT("Gamepad_LeftThumbstick"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_R3] = {STR_CLIT("Gamepad_RightThumbstick"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_SPECIAL_LEFT] = {STR_CLIT("Gamepad_Special_Left"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_SPECIAL_RIGHT] = {STR_CLIT("Gamepad_Special_Right"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_DPAD_UP] = {STR_CLIT("Gamepad_DPad_Up"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_DPAD_DOWN] = {STR_CLIT("Gamepad_DPad_Down"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_DPAD_LEFT] = {STR_CLIT("Gamepad_DPad_Left"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_DPAD_RIGHT] = {STR_CLIT("Gamepad_DPad_Right"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_FACE_BOTTOM] = {.str = STR_CLIT("Gamepad_FaceButton_Bottom")}, + [INPUT_KEY_GAMEPAD_FACE_RIGHT] = {.str = STR_CLIT("Gamepad_FaceButton_Right") }, + [INPUT_KEY_GAMEPAD_FACE_LEFT] = {.str = STR_CLIT("Gamepad_FaceButton_Left") }, + [INPUT_KEY_GAMEPAD_FACE_TOP] = {.str = STR_CLIT("Gamepad_FaceButton_Top") }, + [INPUT_KEY_GAMEPAD_LB] = {.str = STR_CLIT("Gamepad_LeftShoulder") }, + [INPUT_KEY_GAMEPAD_RB] = {.str = STR_CLIT("Gamepad_RightShoulder") }, + [INPUT_KEY_GAMEPAD_LT] = {.str = STR_CLIT("Gamepad_LeftTrigger") }, + [INPUT_KEY_GAMEPAD_RT] = {.str = STR_CLIT("Gamepad_RightTrigger") }, + [INPUT_KEY_GAMEPAD_L3] = {.str = STR_CLIT("Gamepad_LeftThumbstick") }, + [INPUT_KEY_GAMEPAD_R3] = {.str = STR_CLIT("Gamepad_RightThumbstick") }, + [INPUT_KEY_GAMEPAD_SPECIAL_LEFT] = {.str = STR_CLIT("Gamepad_Special_Left") }, + [INPUT_KEY_GAMEPAD_SPECIAL_RIGHT] = {.str = STR_CLIT("Gamepad_Special_Right") }, + [INPUT_KEY_GAMEPAD_DPAD_UP] = {.str = STR_CLIT("Gamepad_DPad_Up") }, + [INPUT_KEY_GAMEPAD_DPAD_DOWN] = {.str = STR_CLIT("Gamepad_DPad_Down") }, + [INPUT_KEY_GAMEPAD_DPAD_LEFT] = {.str = STR_CLIT("Gamepad_DPad_Left") }, + [INPUT_KEY_GAMEPAD_DPAD_RIGHT] = {.str = STR_CLIT("Gamepad_DPad_Right") }, /* gamepad: axes */ - [INPUT_KEY_GAMEPAD_LEFT_X] = {STR_CLIT("Gamepad_LeftX"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_LEFT_Y] = {STR_CLIT("Gamepad_LeftY"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_RIGHT_X] = {STR_CLIT("Gamepad_RightX"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_RIGHT_Y] = {STR_CLIT("Gamepad_RightY"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_LT_AXIS] = {STR_CLIT("Gamepad_LeftTriggerAxis"), FNAME_NONE}, - [INPUT_KEY_GAMEPAD_RT_AXIS] = {STR_CLIT("Gamepad_RightTriggerAxis"), FNAME_NONE}, + [INPUT_KEY_GAMEPAD_LEFT_X] = {.str = STR_CLIT("Gamepad_LeftX") }, + [INPUT_KEY_GAMEPAD_LEFT_Y] = {.str = STR_CLIT("Gamepad_LeftY") }, + [INPUT_KEY_GAMEPAD_RIGHT_X] = {.str = STR_CLIT("Gamepad_RightX") }, + [INPUT_KEY_GAMEPAD_RIGHT_Y] = {.str = STR_CLIT("Gamepad_RightY") }, + [INPUT_KEY_GAMEPAD_LT_AXIS] = {.str = STR_CLIT("Gamepad_LeftTriggerAxis") }, + [INPUT_KEY_GAMEPAD_RT_AXIS] = {.str = STR_CLIT("Gamepad_RightTriggerAxis")}, }; void From 8adeacdc71c3478ee8ac9608a536dd0066068d33 Mon Sep 17 00:00:00 2001 From: akmubi Date: Wed, 26 Aug 2026 05:15:52 +0500 Subject: [PATCH 4/8] fix keybind parsing and comparison issues --- src/input.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/src/input.c b/src/input.c index 4e6975d..e89d451 100644 --- a/src/input.c +++ b/src/input.c @@ -772,6 +772,11 @@ keybind_parse(str_t str, keybind_t default_val) return default_val; } + /* A separator must always be followed by another non-empty token. */ + if (end < str.len && end + 1 == str.len) { + return default_val; + } + if (result.count >= KEYBIND_MAX_KEYS) { return default_val; } @@ -899,6 +904,12 @@ keybind_is_valid(keybind_t bind) if (bind.keys[i] == INPUT_KEY_NONE || bind.keys[i] >= INPUT_KEY_MAX) { return false; } + + for (int j = 0; j < i; ++j) { + if (bind.keys[i] == bind.keys[j]) { + return false; + } + } } return true; } @@ -906,16 +917,19 @@ keybind_is_valid(keybind_t bind) bool keybind_equal(keybind_t a, keybind_t b) { - if (a.count != b.count) { + if (a.count != b.count || a.count > KEYBIND_MAX_KEYS) { return false; } + bool matched[KEYBIND_MAX_KEYS] = {0}; + for (int i = 0; i < a.count; ++i) { bool found = false; for (int j = 0; j < b.count; ++j) { - if (a.keys[i] == b.keys[j]) { - found = true; + if (!matched[j] && a.keys[i] == b.keys[j]) { + matched[j] = true; + found = true; break; } } From 047bda6cddd0dffac1b4a0df9e6af7e093ffdf38 Mon Sep 17 00:00:00 2001 From: akmubi Date: Wed, 26 Aug 2026 09:39:03 +0500 Subject: [PATCH 5/8] update template mod --- mod/main.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mod/main.c b/mod/main.c index 85bf548..5b188f0 100644 --- a/mod/main.c +++ b/mod/main.c @@ -1,9 +1,11 @@ #include "mod_sdk.h" +#include "mod_unreal.h" static bool mod_init(const mod_host_api_t *host, mod_t m) { mod_sdk_init(host, m); + unreal_cache_objects(); MOD_LOG_INFO("Hello from example mod!!!"); return true; @@ -18,4 +20,4 @@ MOD_ENTRY() }; return &api; -} \ No newline at end of file +} From 295705b84eceaaf58a2f6bc4dd75e452b87c0260 Mon Sep 17 00:00:00 2001 From: akmubi Date: Wed, 26 Aug 2026 11:40:52 +0500 Subject: [PATCH 6/8] replace 'Reload' button with 'Unload/Load' --- src/ui_mod_manager.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/ui_mod_manager.c b/src/ui_mod_manager.c index 10c5c56..c6b4f16 100644 --- a/src/ui_mod_manager.c +++ b/src/ui_mod_manager.c @@ -856,8 +856,14 @@ draw_code_section(ui_mod_manager_t *ui, struct nk_context *ctx, mod_manager_t *m nk_tooltip(ctx, "Stop - Unload - Load - Start"); } - if (ui_button_str(ctx, STR_LIT("Reload"))) { - mod_dll_reload(manager, mod_handle_make(m)); + if (m->dll.state == MOD_DLL_STATE_LOADED) { + if (ui_button_str(ctx, STR_LIT("Unload"))) { + mod_dll_unload(manager, mod_handle_make(m)); + } + } else if (m->dll.state == MOD_DLL_STATE_UNLOADED) { + if (ui_button_str(ctx, STR_LIT("Load"))) { + mod_dll_unload(manager, mod_handle_make(m)); + } } nk_tree_pop(ctx); From f31bae16001a36660eacf4527f92154631eea1c1 Mon Sep 17 00:00:00 2001 From: akmubi Date: Wed, 26 Aug 2026 05:58:03 +0500 Subject: [PATCH 7/8] add unreal_uobject_find_first_of_by_name --- include/unreal.h | 2 ++ mod/mod_unreal.c | 26 ++++++++++++++++++++++++++ mod/mod_unreal.h | 3 +++ src/unreal.c | 26 ++++++++++++++++++++++++++ 4 files changed, 57 insertions(+) diff --git a/include/unreal.h b/include/unreal.h index 02ba81b..d6e63ed 100644 --- a/include/unreal.h +++ b/include/unreal.h @@ -3130,6 +3130,8 @@ uclass_t * unreal_uclass_find(str_t name, bool ignore_case, bool exact_match); uobject_t * unreal_uobject_find_first_of(uclass_t *cls); +uobject_t * +unreal_uobject_find_first_of_by_name(uclass_t *cls, str_t name, bool ignore_case, bool exact_match); uclass_t * unreal_uobject_find_class_by_full_name(str_t full_name); uobject_t * diff --git a/mod/mod_unreal.c b/mod/mod_unreal.c index b5ff2a8..8acedac 100644 --- a/mod/mod_unreal.c +++ b/mod/mod_unreal.c @@ -938,6 +938,32 @@ unreal_uobject_find_first_of(uclass_t *cls) return NULL; } +uobject_t * +unreal_uobject_find_first_of_by_name(uclass_t *cls, str_t name, bool ignore_case, bool exact_match) +{ + if (!cls) { + return NULL; + } + + for (int i = 0; i < unreal_uobject_array_count(); ++i) { + uobject_t *obj = unreal_uobject_array_get_obj(i); + if (!obj) { + continue; + } + + + if (!unreal_uobject_is_a(obj, cls) || !unreal_uobject_is_default(obj)) { + continue; + } + + if (unreal_fname_match_text(obj->name, name, ignore_case, exact_match)) { + return obj; + } + } + + return NULL; +} + uclass_t * unreal_uobject_find_class_by_full_name(str_t full_name) { diff --git a/mod/mod_unreal.h b/mod/mod_unreal.h index f127a1e..e9ce8b1 100644 --- a/mod/mod_unreal.h +++ b/mod/mod_unreal.h @@ -2930,6 +2930,9 @@ unreal_uclass_find(str_t name, bool ignore_case, bool exact_match); /* Returns the first non-default object that is an instance of cls or one of its subclasses. */ uobject_t * unreal_uobject_find_first_of(uclass_t *cls); +/* Returns the first non-default object with a specified name that is an instance of cls or one of its subclasses. */ +uobject_t * +unreal_uobject_find_first_of_by_name(uclass_t *cls, str_t name, bool ignore_case, bool exact_match); /* Finds a UClass by an exact, case-sensitive dotted outer-chain name. */ uclass_t * unreal_uobject_find_class_by_full_name(str_t full_name); diff --git a/src/unreal.c b/src/unreal.c index e45f591..a1ce032 100644 --- a/src/unreal.c +++ b/src/unreal.c @@ -1288,6 +1288,32 @@ unreal_uobject_find_first_of(uclass_t *cls) return NULL; } +uobject_t * +unreal_uobject_find_first_of_by_name(uclass_t *cls, str_t name, bool ignore_case, bool exact_match) +{ + if (!cls) { + return NULL; + } + + for (int i = 0; i < unreal_uobject_array_count(); ++i) { + uobject_t *obj = unreal_uobject_array_get_obj(i); + if (!obj) { + continue; + } + + + if (!unreal_uobject_is_a(obj, cls) || !unreal_uobject_is_default(obj)) { + continue; + } + + if (unreal_fname_match_text(obj->name, name, ignore_case, exact_match)) { + return obj; + } + } + + return NULL; +} + uclass_t * unreal_uobject_find_class_by_full_name(str_t full_name) { From ce26db67f0226ab4629a3216efd5bf91376a3cae Mon Sep 17 00:00:00 2001 From: akmubi Date: Thu, 27 Aug 2026 04:10:03 +0500 Subject: [PATCH 8/8] make builtin tools disabled by default --- src/mod_manager.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/mod_manager.c b/src/mod_manager.c index a6fabde..16aa5ae 100644 --- a/src/mod_manager.c +++ b/src/mod_manager.c @@ -3183,8 +3183,13 @@ mod_manager_startup_load_cfg(mod_manager_t *manager) if (!mod_manager_load_cfg(manager)) { dir_create_recursive(path_join(tmp.arena, manager->game_dir, CONFIG_MOD_MANAGER_MOD_DIR_NAME)); + str_t default_disabled_mods[] = { + STR_LIT("uobject-search"), + STR_LIT("ufunction-tracer"), + }; + mod_cfg_string_set(&manager->cfg.root_mod_dir, CONFIG_MOD_MANAGER_MOD_DIR_NAME); - manager->disabled_mods = (str_array_t){0}; + manager->disabled_mods = str_array_make(default_disabled_mods, COUNTOF(default_disabled_mods)); manager->mod_order.initial_ids = (str_array_t){0}; manager->mod_order.count = 0; manager->cfg.overlay_toggle = keybind_parse(CONFIG_NK_OVERLAY_DEFAULT_TOGGLE_KEY, KEYBIND_NULL);