From 7be3953d6b67b409598e43102e99df1aa86132e2 Mon Sep 17 00:00:00 2001 From: wins1ey Date: Wed, 5 Jun 2024 15:22:00 +0100 Subject: [PATCH 01/16] Improve auto splitter order of execution and console output --- src/auto-splitter.c | 66 ++++++++++++++++++++++++--------------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index fd067de..f3b9b1c 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -269,16 +269,23 @@ void state(lua_State* L) call_va(L, "state", ""); } -void update(lua_State* L) +bool update(lua_State* L) { - call_va(L, "update", ""); + bool ret; + if (call_va(L, "update", ">b", &ret)) { + lua_pop(L, 1); + return ret; + } + lua_pop(L, 1); + return true; } void start(lua_State* L) { bool ret; - if (call_va(L, "start", ">b", &ret)) { - atomic_store(&call_start, ret); + if (call_va(L, "start", ">b", &ret) && ret) { + atomic_store(&call_start, true); + printf("start: true\n"); } lua_pop(L, 1); // Remove the return value from the stack } @@ -286,8 +293,9 @@ void start(lua_State* L) void split(lua_State* L) { bool ret; - if (call_va(L, "split", ">b", &ret)) { - atomic_store(&call_split, ret); + if (call_va(L, "split", ">b", &ret) && ret) { + atomic_store(&call_split, true); + printf("split: true\n"); } lua_pop(L, 1); // Remove the return value from the stack } @@ -298,20 +306,24 @@ void is_loading(lua_State* L) if (call_va(L, "isLoading", ">b", &loading)) { if (loading != prev_is_loading) { atomic_store(&toggle_loading, true); + printf("isLoading: %s\n", loading ? "true" : "false"); prev_is_loading = !prev_is_loading; } } lua_pop(L, 1); // Remove the return value from the stack } -void reset(lua_State* L) +bool reset(lua_State* L) { - bool shouldReset; - if (call_va(L, "reset", ">b", &shouldReset)) { - if (shouldReset) - atomic_store(&call_reset, true); + bool should_reset; + if (call_va(L, "reset", ">b", &should_reset) && should_reset) { + atomic_store(&call_reset, true); + printf("reset: true\n"); + lua_pop(L, 1); + return true; } - lua_pop(L, 1); // Remove the return value from the stack + lua_pop(L, 1); + return false; } void run_auto_splitter() @@ -398,24 +410,18 @@ void run_auto_splitter() state(L); } - if (update_exists) { - update(L); - } - - if (start_exists) { - start(L); - } - - if (split_exists) { - split(L); - } - - if (is_loading_exists) { - is_loading(L); - } - - if (reset_exists) { - reset(L); + if (!update_exists || update(L)) { + if (is_loading_exists) { + is_loading(L); + } + if (start_exists) { + start(L); + } + if (!reset_exists || !reset(L)) { + if (split_exists) { + split(L); + } + } } // Clear the memory maps cache if needed From b3d6be65dd3030038f8ff67c525e6e6a891633fc Mon Sep 17 00:00:00 2001 From: wins1ey Date: Wed, 5 Jun 2024 16:07:45 +0100 Subject: [PATCH 02/16] Update auto splitter to only run 'start', 'reset' and 'split' if timer has started --- src/auto-splitter.c | 9 +++++---- src/auto-splitter.h | 1 + src/main.c | 1 + 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index f3b9b1c..23bbc72 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -21,6 +21,7 @@ char auto_splitter_file[PATH_MAX]; int refresh_rate = 60; int maps_cache_cycles = 0; // 0=off, 1=current cycle, +1=multiple cycles int maps_cache_cycles_value = 0; // same as `maps_cache_cycles` but this one represents the current value rather than the reference from the script +atomic_bool timer_started = false; atomic_bool auto_splitter_enabled = true; atomic_bool call_start = false; atomic_bool call_split = false; @@ -411,13 +412,13 @@ void run_auto_splitter() } if (!update_exists || update(L)) { + if (!atomic_load(&timer_started) && start_exists) { + start(L); + } if (is_loading_exists) { is_loading(L); } - if (start_exists) { - start(L); - } - if (!reset_exists || !reset(L)) { + if (atomic_load(&timer_started) && (!reset_exists || !reset(L))) { if (split_exists) { split(L); } diff --git a/src/auto-splitter.h b/src/auto-splitter.h index d43c962..11853b7 100644 --- a/src/auto-splitter.h +++ b/src/auto-splitter.h @@ -4,6 +4,7 @@ #include #include +extern atomic_bool timer_started; extern atomic_bool auto_splitter_enabled; extern atomic_bool call_start; extern atomic_bool call_split; diff --git a/src/main.c b/src/main.c index b9b8eee..4527800 100644 --- a/src/main.c +++ b/src/main.c @@ -150,6 +150,7 @@ static gboolean ls_app_window_step(gpointer data) if (win->timer) { ls_timer_step(win->timer, now); + atomic_store(&timer_started, win->timer->started); if (atomic_load(&auto_splitter_enabled)) { if (atomic_load(&call_start) && !win->timer->loading) { timer_start(win); From 922228340456ecf81265c26d81aa52b300844f90 Mon Sep 17 00:00:00 2001 From: wins1ey Date: Wed, 5 Jun 2024 17:18:09 +0100 Subject: [PATCH 03/16] Refactor run_auto_splitter into more functions --- src/auto-splitter.c | 94 ++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 44 deletions(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index 23bbc72..0cba5b1 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -327,6 +327,48 @@ bool reset(lua_State* L) return false; } +bool lua_function_exists(lua_State* L, const char* func_name) +{ + lua_getglobal(L, func_name); + bool exists = lua_isfunction(L, -1); + lua_pop(L, 1); + return exists; +} + +void run_auto_splitter_cycle( + lua_State* L, + bool state_exists, + bool start_exists, + bool split_exists, + bool is_loading_exists, + bool reset_exists, + bool update_exists) +{ + if (state_exists) { + state(L); + } + + if (update_exists && !update(L)) { + return; + } + + if (is_loading_exists) { + is_loading(L); + } + + if (!atomic_load(&timer_started)) { + if (start_exists) { + start(L); + } + } else { + if (!reset_exists || !reset(L)) { + if (split_exists) { + split(L); + } + } + } +} + void run_auto_splitter() { lua_State* L = luaL_newstate(); @@ -364,33 +406,13 @@ void run_auto_splitter() return; } - lua_getglobal(L, "state"); - bool state_exists = lua_isfunction(L, -1); - lua_pop(L, 1); // Remove 'state' from the stack - - lua_getglobal(L, "start"); - bool start_exists = lua_isfunction(L, -1); - lua_pop(L, 1); // Remove 'start' from the stack - - lua_getglobal(L, "split"); - bool split_exists = lua_isfunction(L, -1); - lua_pop(L, 1); // Remove 'split' from the stack - - lua_getglobal(L, "isLoading"); - bool is_loading_exists = lua_isfunction(L, -1); - lua_pop(L, 1); // Remove 'isLoading' from the stack - - lua_getglobal(L, "startup"); - bool startup_exists = lua_isfunction(L, -1); - lua_pop(L, 1); // Remove 'startup' from the stack - - lua_getglobal(L, "reset"); - bool reset_exists = lua_isfunction(L, -1); - lua_pop(L, 1); // Remove 'reset' from the stack - - lua_getglobal(L, "update"); - bool update_exists = lua_isfunction(L, -1); - lua_pop(L, 1); // Remove 'update' from the stack + bool state_exists = lua_function_exists(L, "state"); + bool start_exists = lua_function_exists(L, "start"); + bool split_exists = lua_function_exists(L, "split"); + bool is_loading_exists = lua_function_exists(L, "isLoading"); + bool startup_exists = lua_function_exists(L, "startup"); + bool reset_exists = lua_function_exists(L, "reset"); + bool update_exists = lua_function_exists(L, "update"); if (startup_exists) { startup(L); @@ -407,23 +429,7 @@ void run_auto_splitter() break; } - if (state_exists) { - state(L); - } - - if (!update_exists || update(L)) { - if (!atomic_load(&timer_started) && start_exists) { - start(L); - } - if (is_loading_exists) { - is_loading(L); - } - if (atomic_load(&timer_started) && (!reset_exists || !reset(L))) { - if (split_exists) { - split(L); - } - } - } + run_auto_splitter_cycle(L, state_exists, start_exists, split_exists, is_loading_exists, reset_exists, update_exists); // Clear the memory maps cache if needed maps_cache_cycles_value--; From a42a53a258ce5b11633f084b744087507efd0971 Mon Sep 17 00:00:00 2001 From: wins1ey Date: Wed, 5 Jun 2024 18:37:50 +0100 Subject: [PATCH 04/16] Add onSplit, onStart and onReset functions Also refactored some of the other functions and removed unneeded ones. --- src/auto-splitter.c | 68 +++++++++++++++++++++++++++++---------------- 1 file changed, 44 insertions(+), 24 deletions(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index 0cba5b1..9d43518 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -248,8 +248,7 @@ bool call_va(lua_State* L, const char* func, const char* sig, ...) void startup(lua_State* L) { - lua_getglobal(L, "startup"); - lua_pcall(L, 0, 0, 0); + call_va(L, "startup", ""); lua_getglobal(L, "refreshRate"); if (lua_isnumber(L, -1)) { @@ -265,11 +264,6 @@ void startup(lua_State* L) lua_pop(L, 1); // Remove 'mapsCacheCycles' from the stack } -void state(lua_State* L) -{ - call_va(L, "state", ""); -} - bool update(lua_State* L) { bool ret; @@ -281,33 +275,39 @@ bool update(lua_State* L) return true; } -void start(lua_State* L) +bool start(lua_State* L) { bool ret; if (call_va(L, "start", ">b", &ret) && ret) { atomic_store(&call_start, true); printf("start: true\n"); + lua_pop(L, 1); + return true; } lua_pop(L, 1); // Remove the return value from the stack + return false; } -void split(lua_State* L) +bool split(lua_State* L) { bool ret; if (call_va(L, "split", ">b", &ret) && ret) { atomic_store(&call_split, true); printf("split: true\n"); + lua_pop(L, 1); + return true; } lua_pop(L, 1); // Remove the return value from the stack + return false; } void is_loading(lua_State* L) { - bool loading; - if (call_va(L, "isLoading", ">b", &loading)) { - if (loading != prev_is_loading) { + bool ret; + if (call_va(L, "isLoading", ">b", &ret)) { + if (ret != prev_is_loading) { atomic_store(&toggle_loading, true); - printf("isLoading: %s\n", loading ? "true" : "false"); + printf("isLoading: %s\n", ret ? "true" : "false"); prev_is_loading = !prev_is_loading; } } @@ -316,8 +316,8 @@ void is_loading(lua_State* L) bool reset(lua_State* L) { - bool should_reset; - if (call_va(L, "reset", ">b", &should_reset) && should_reset) { + bool ret; + if (call_va(L, "reset", ">b", &ret) && ret) { atomic_store(&call_reset, true); printf("reset: true\n"); lua_pop(L, 1); @@ -339,13 +339,16 @@ void run_auto_splitter_cycle( lua_State* L, bool state_exists, bool start_exists, + bool on_start_exists, bool split_exists, + bool on_split_exists, bool is_loading_exists, bool reset_exists, + bool on_reset_exists, bool update_exists) { if (state_exists) { - state(L); + call_va(L, "state", ""); } if (update_exists && !update(L)) { @@ -357,15 +360,19 @@ void run_auto_splitter_cycle( } if (!atomic_load(&timer_started)) { - if (start_exists) { - start(L); - } - } else { - if (!reset_exists || !reset(L)) { - if (split_exists) { - split(L); + if (start_exists && start(L)) { + if (on_start_exists) { + call_va(L, "onStart", ""); } } + } else if (reset_exists && reset(L)) { + if (on_reset_exists) { + call_va(L, "onReset", ""); + } + } else if (split_exists && split(L)) { + if (on_split_exists) { + call_va(L, "onSplit", ""); + } } } @@ -408,10 +415,13 @@ void run_auto_splitter() bool state_exists = lua_function_exists(L, "state"); bool start_exists = lua_function_exists(L, "start"); + bool on_start_exists = lua_function_exists(L, "onStart"); bool split_exists = lua_function_exists(L, "split"); + bool on_split_exists = lua_function_exists(L, "onSplit"); bool is_loading_exists = lua_function_exists(L, "isLoading"); bool startup_exists = lua_function_exists(L, "startup"); bool reset_exists = lua_function_exists(L, "reset"); + bool on_reset_exists = lua_function_exists(L, "onReset"); bool update_exists = lua_function_exists(L, "update"); if (startup_exists) { @@ -429,7 +439,17 @@ void run_auto_splitter() break; } - run_auto_splitter_cycle(L, state_exists, start_exists, split_exists, is_loading_exists, reset_exists, update_exists); + run_auto_splitter_cycle( + L, + state_exists, + start_exists, + on_start_exists, + split_exists, + on_split_exists, + is_loading_exists, + reset_exists, + on_reset_exists, + update_exists); // Clear the memory maps cache if needed maps_cache_cycles_value--; From 042b7b2d72be185250597b3b1b013899105b32d1 Mon Sep 17 00:00:00 2001 From: wins1ey Date: Wed, 5 Jun 2024 20:29:21 +0100 Subject: [PATCH 05/16] Update runtime so process is defined as variable Define `process = "{game_name}"` in startup instead of calling `process()` at the top of the auto splitter. The auto splitter script will now keep running if the game is closed and will try and find the new game process before continuing, instead of running the entire script again from the start. --- src/auto-splitter.c | 20 +++++++++++++++++--- src/process.c | 2 +- src/process.h | 1 + 3 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index 9d43518..baa4c07 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -262,6 +262,16 @@ void startup(lua_State* L) maps_cache_cycles_value = maps_cache_cycles; } lua_pop(L, 1); // Remove 'mapsCacheCycles' from the stack + + lua_getglobal(L, "process"); + if (lua_isstring(L, -1)) { + process.name = lua_tostring(L, -1); + lua_pop(L, 1); + if (process.name != NULL) { + find_process_id(L); + } + } + lua_pop(L, 1); // Remove 'process' from the stack } bool update(lua_State* L) @@ -381,8 +391,6 @@ void run_auto_splitter() lua_State* L = luaL_newstate(); luaL_openlibs(L); disable_functions(L, disabled_functions); - lua_pushcfunction(L, find_process_id); - lua_setglobal(L, "process"); lua_pushcfunction(L, read_address); lua_setglobal(L, "readAddress"); lua_pushcfunction(L, getPid); @@ -423,6 +431,7 @@ void run_auto_splitter() bool reset_exists = lua_function_exists(L, "reset"); bool on_reset_exists = lua_function_exists(L, "onReset"); bool update_exists = lua_function_exists(L, "update"); + bool init_exists = lua_function_exists(L, "init"); if (startup_exists) { startup(L); @@ -435,8 +444,13 @@ void run_auto_splitter() struct timespec clock_start; clock_gettime(CLOCK_MONOTONIC, &clock_start); - if (!atomic_load(&auto_splitter_enabled) || strcmp(current_file, auto_splitter_file) != 0 || !process_exists() || process.pid == 0) { + if (!atomic_load(&auto_splitter_enabled) || strcmp(current_file, auto_splitter_file) != 0) { break; + } else if (!process_exists() || process.pid == 0) { + find_process_id(L); + if (init_exists) { + call_va(L, "init", ""); + } } run_auto_splitter_cycle( diff --git a/src/process.c b/src/process.c index fc8d7a7..f1da7f7 100644 --- a/src/process.c +++ b/src/process.c @@ -7,6 +7,7 @@ #include #include +#include #include "auto-splitter.h" #include "process.h" @@ -104,7 +105,6 @@ void stock_process_id(const char* pid_command) int find_process_id(lua_State* L) { - process.name = lua_tostring(L, 1); char command[256]; printf("\033[2J\033[1;1H"); // Clear the console snprintf(command, sizeof(command), "pgrep \"%.*s\"", (int)strnlen(process.name, 15), process.name); diff --git a/src/process.h b/src/process.h index 27a5fa7..28000e9 100644 --- a/src/process.h +++ b/src/process.h @@ -6,6 +6,7 @@ #include #include +#include struct game_process { const char* name; From 3ddba87a907db51c24029ce0a7527f4b084e434b Mon Sep 17 00:00:00 2001 From: wins1ey Date: Wed, 5 Jun 2024 21:47:34 +0100 Subject: [PATCH 06/16] Improve console output when finding process --- src/auto-splitter.c | 9 +++------ src/process.c | 24 +++++++++++++----------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index baa4c07..799840b 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -444,13 +444,10 @@ void run_auto_splitter() struct timespec clock_start; clock_gettime(CLOCK_MONOTONIC, &clock_start); - if (!atomic_load(&auto_splitter_enabled) || strcmp(current_file, auto_splitter_file) != 0) { + if (!atomic_load(&auto_splitter_enabled) || strcmp(current_file, auto_splitter_file) != 0 || (!process_exists() && !find_process_id(L))) { break; - } else if (!process_exists() || process.pid == 0) { - find_process_id(L); - if (init_exists) { - call_va(L, "init", ""); - } + } else if (init_exists) { + call_va(L, "init", ""); } run_auto_splitter_cycle( diff --git a/src/process.c b/src/process.c index f1da7f7..91bfd9a 100644 --- a/src/process.c +++ b/src/process.c @@ -76,7 +76,7 @@ uintptr_t find_base_address(const char* module) return 0; } -void stock_process_id(const char* pid_command) +int stock_process_id(const char* pid_command) { char pid_output[PATH_MAX + 100]; pid_output[0] = '\0'; @@ -84,7 +84,6 @@ void stock_process_id(const char* pid_command) while (atomic_load(&auto_splitter_enabled)) { execute_command(pid_command, pid_output); process.pid = strtoul(pid_output, NULL, 10); - printf("\033[2J\033[1;1H"); // Clear the console if (process.pid) { size_t newlinePos = strcspn(pid_output, "\n"); if (newlinePos != strlen(pid_output) - 1 && pid_output[0] != '\0') { @@ -92,26 +91,29 @@ void stock_process_id(const char* pid_command) } break; } else { - printf("%s isn't running.\n", process.name); + printf("%s isn't running.\r", process.name); + fflush(stdout); usleep(100000); // Sleep for 100ms } } - printf("Process: %s\n", process.name); - printf("PID: %u\n", process.pid); - process.base_address = find_base_address(NULL); - process.dll_address = process.base_address; + if (process.pid) { + printf("\r\033[KProcess: %s\n", process.name); + printf("PID: %u\n", process.pid); + process.base_address = find_base_address(NULL); + process.dll_address = process.base_address; + return 1; + } + printf("\nCouldn't find process: %s\n", process.name); + return 0; } int find_process_id(lua_State* L) { char command[256]; - printf("\033[2J\033[1;1H"); // Clear the console snprintf(command, sizeof(command), "pgrep \"%.*s\"", (int)strnlen(process.name, 15), process.name); - stock_process_id(command); - - return 0; + return stock_process_id(command); } int getPid(lua_State* L) From 64005a55aa2009e989d0b31ff3b7316f950451a8 Mon Sep 17 00:00:00 2001 From: wins1ey Date: Wed, 5 Jun 2024 22:36:18 +0100 Subject: [PATCH 07/16] Update lua runtime to open libs after file This stops the user from calling any function, for e.g. 'print', outside of the functions stated in the documentation --- src/auto-splitter.c | 29 ++++++++++++----------------- 1 file changed, 12 insertions(+), 17 deletions(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index 799840b..f4fa436 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -389,22 +389,10 @@ void run_auto_splitter_cycle( void run_auto_splitter() { lua_State* L = luaL_newstate(); - luaL_openlibs(L); - disable_functions(L, disabled_functions); - lua_pushcfunction(L, read_address); - lua_setglobal(L, "readAddress"); - lua_pushcfunction(L, getPid); - lua_setglobal(L, "getPID"); - - char current_file[PATH_MAX]; - strcpy(current_file, auto_splitter_file); // Load the Lua file if (luaL_loadfile(L, auto_splitter_file) != LUA_OK) { - // Error loading the file - const char* error_msg = lua_tostring(L, -1); - lua_pop(L, 1); // Remove the error message from the stack - fprintf(stderr, "Lua syntax error: %s\n", error_msg); + fprintf(stderr, "Lua syntax error: %s\n", lua_tostring(L, -1)); lua_close(L); atomic_store(&auto_splitter_enabled, false); return; @@ -412,15 +400,22 @@ void run_auto_splitter() // Execute the Lua file if (lua_pcall(L, 0, LUA_MULTRET, 0) != LUA_OK) { - // Error executing the file - const char* error_msg = lua_tostring(L, -1); - lua_pop(L, 1); // Remove the error message from the stack - fprintf(stderr, "Lua runtime error: %s\n", error_msg); + fprintf(stderr, "Lua runtime error: %s\n", lua_tostring(L, -1)); lua_close(L); atomic_store(&auto_splitter_enabled, false); return; } + luaL_openlibs(L); + disable_functions(L, disabled_functions); + lua_pushcfunction(L, read_address); + lua_setglobal(L, "readAddress"); + lua_pushcfunction(L, getPid); + lua_setglobal(L, "getPID"); + + char current_file[PATH_MAX]; + strcpy(current_file, auto_splitter_file); + bool state_exists = lua_function_exists(L, "state"); bool start_exists = lua_function_exists(L, "start"); bool on_start_exists = lua_function_exists(L, "onStart"); From e1a030f20e6cb938046b4c86b52e01170d839921 Mon Sep 17 00:00:00 2001 From: wins1ey Date: Thu, 6 Jun 2024 14:34:36 +0100 Subject: [PATCH 08/16] Fix auto splitter runtime to call init when game opens --- src/auto-splitter.c | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index f4fa436..391a118 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -246,7 +246,7 @@ bool call_va(lua_State* L, const char* func, const char* sig, ...) return true; } -void startup(lua_State* L) +bool startup(lua_State* L) { call_va(L, "startup", ""); @@ -266,12 +266,13 @@ void startup(lua_State* L) lua_getglobal(L, "process"); if (lua_isstring(L, -1)) { process.name = lua_tostring(L, -1); - lua_pop(L, 1); - if (process.name != NULL) { - find_process_id(L); + if (process.name != NULL && find_process_id(L)) { + lua_pop(L, 1); + return true; } } lua_pop(L, 1); // Remove 'process' from the stack + return false; } bool update(lua_State* L) @@ -428,8 +429,10 @@ void run_auto_splitter() bool update_exists = lua_function_exists(L, "update"); bool init_exists = lua_function_exists(L, "init"); - if (startup_exists) { - startup(L); + if (startup_exists && startup(L)) { + if (init_exists) { + call_va(L, "init", ""); + } } printf("Refresh rate: %d\n", refresh_rate); @@ -439,10 +442,10 @@ void run_auto_splitter() struct timespec clock_start; clock_gettime(CLOCK_MONOTONIC, &clock_start); - if (!atomic_load(&auto_splitter_enabled) || strcmp(current_file, auto_splitter_file) != 0 || (!process_exists() && !find_process_id(L))) { - break; - } else if (init_exists) { + if (!process_exists() && find_process_id(L) && init_exists) { call_va(L, "init", ""); + } else if (!atomic_load(&auto_splitter_enabled) || strcmp(current_file, auto_splitter_file) != 0) { + break; } run_auto_splitter_cycle( From 162611db256e46d44559587a95e7b869c2034052 Mon Sep 17 00:00:00 2001 From: wins1ey Date: Thu, 6 Jun 2024 23:01:21 +0100 Subject: [PATCH 09/16] Set memory maps in auto splitter instead of calling readAddress --- src/auto-splitter.c | 52 ++++++--- src/memory.c | 267 +++++++++++++++++++++++++++++++------------- src/memory.h | 17 ++- 3 files changed, 240 insertions(+), 96 deletions(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index 391a118..20f11c9 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -275,6 +275,28 @@ bool startup(lua_State* L) return false; } +bool state(lua_State* L) +{ + lua_newtable(L); + lua_setglobal(L, "memory"); + call_va(L, "state", ""); + lua_getglobal(L, "process"); + if (lua_isstring(L, -1)) { + process.name = lua_tostring(L, -1); + if (process.name != NULL && find_process_id(L)) { + lua_pop(L, 1); + lua_newtable(L); + lua_setglobal(L, "old"); + lua_newtable(L); + lua_setglobal(L, "current"); + store_memory_tables(L); + return true; + } + } + lua_pop(L, 1); // Remove 'process' from the stack + return false; +} + bool update(lua_State* L) { bool ret; @@ -358,14 +380,14 @@ void run_auto_splitter_cycle( bool on_reset_exists, bool update_exists) { - if (state_exists) { - call_va(L, "state", ""); - } - if (update_exists && !update(L)) { return; } + if (state_exists) { + read_address(L); + } + if (is_loading_exists) { is_loading(L); } @@ -385,6 +407,14 @@ void run_auto_splitter_cycle( call_va(L, "onSplit", ""); } } + + // Clear the memory maps cache if needed + maps_cache_cycles_value--; + if (maps_cache_cycles_value < 1) { + p_maps_cache_size = 0; // We dont need to "empty" the list as the elements after index 0 are considered invalid + maps_cache_cycles_value = maps_cache_cycles; + // printf("Cleared maps cache\n"); + } } void run_auto_splitter() @@ -409,8 +439,6 @@ void run_auto_splitter() luaL_openlibs(L); disable_functions(L, disabled_functions); - lua_pushcfunction(L, read_address); - lua_setglobal(L, "readAddress"); lua_pushcfunction(L, getPid); lua_setglobal(L, "getPID"); @@ -438,6 +466,10 @@ void run_auto_splitter() printf("Refresh rate: %d\n", refresh_rate); int rate = 1000000 / refresh_rate; + if (state_exists && !state(L)) { + state_exists = false; + } + while (1) { struct timespec clock_start; clock_gettime(CLOCK_MONOTONIC, &clock_start); @@ -460,14 +492,6 @@ void run_auto_splitter() on_reset_exists, update_exists); - // Clear the memory maps cache if needed - maps_cache_cycles_value--; - if (maps_cache_cycles_value < 1) { - p_maps_cache_size = 0; // We dont need to "empty" the list as the elements after index 0 are considered invalid - maps_cache_cycles_value = maps_cache_cycles; - // printf("Cleared maps cache\n"); - } - struct timespec clock_end; clock_gettime(CLOCK_MONOTONIC, &clock_end); long long duration = (clock_end.tv_sec - clock_start.tv_sec) * 1000000 + (clock_end.tv_nsec - clock_start.tv_nsec) / 1000; diff --git a/src/memory.c b/src/memory.c index 92191f1..8e996fd 100644 --- a/src/memory.c +++ b/src/memory.c @@ -11,7 +11,8 @@ #include "memory.h" #include "process.h" -bool memory_error; +MemoryTable memory_tables[MAX_MEMORY_TABLES]; +int memory_table_count = 0; extern game_process process; #define READ_MEMORY_FUNCTION(value_type) \ @@ -30,7 +31,6 @@ extern game_process process; ssize_t mem_n_read = process_vm_readv(process.pid, &mem_local, 1, &mem_remote, 1, 0); \ if (mem_n_read == -1) { \ *err = (int32_t)errno; \ - memory_error = true; \ } else if (mem_n_read != (ssize_t)mem_remote.iov_len) { \ printf("Error reading process memory: short read of %ld bytes\n", (long)mem_n_read); \ exit(1); \ @@ -51,7 +51,7 @@ READ_MEMORY_FUNCTION(float) READ_MEMORY_FUNCTION(double) READ_MEMORY_FUNCTION(bool) -char* read_memory_string(uint64_t mem_address, int buffer_size) +char* read_memory_string(uint64_t mem_address, int buffer_size, int32_t* err) { char* buffer = (char*)malloc(buffer_size); if (buffer == NULL) { @@ -70,6 +70,7 @@ char* read_memory_string(uint64_t mem_address, int buffer_size) ssize_t mem_n_read = process_vm_readv(process.pid, &mem_local, 1, &mem_remote, 1, 0); if (mem_n_read == -1) { buffer[0] = '\0'; + *err = (int32_t)errno; } else if (mem_n_read != (ssize_t)mem_remote.iov_len) { printf("Error reading process memory: short read of %ld bytes\n", (long)mem_n_read); exit(1); @@ -92,7 +93,7 @@ bool handle_memory_error(uint32_t err) printf("EFAULT: Invalid memory space/address\n"); break; case EINVAL: - printf("EINVAL: An error ocurred while reading memory\n"); + printf("EINVAL: An error occurred while reading memory\n"); break; case ENOMEM: printf("ENOMEM: Please get more memory\n"); @@ -107,92 +108,198 @@ bool handle_memory_error(uint32_t err) return true; } -int read_address(lua_State* L) +void read_address(lua_State* L) { - memory_error = false; - uint64_t address; - const char* value_type = lua_tostring(L, 1); - int i; - - if (lua_isnumber(L, 2)) { - address = process.base_address + lua_tointeger(L, 2); - i = 3; - } else { - const char* module = lua_tostring(L, 2); - if (strcmp(process.name, module) != 0) { - process.dll_address = find_base_address(module); - } - address = process.dll_address + lua_tointeger(L, 3); - i = 4; - } + for (int i = 0; i < memory_table_count; i++) { + uint64_t address = 0; + int error = 0; + const MemoryTable* table = &memory_tables[i]; - int error = 0; + const char* table_name = table->name; + const char* value_type = table->type; - for (; i <= lua_gettop(L); i++) { - if (address <= UINT32_MAX) { - address = read_memory_uint32_t((uint64_t)address, &error); - if (memory_error) - break; + if (table->module) { + if (strcmp(process.name, table->module) != 0) { + process.dll_address = find_base_address(table->module); + } + address = process.dll_address + table->address; } else { - address = read_memory_uint64_t(address, &error); - if (memory_error) - break; + address = process.base_address + table->address; + } + + for (int i = 0; i < table->offset_count; i++) { + if (address <= UINT32_MAX) { + address = read_memory_uint32_t((uint64_t)address, &error); + if (error) + break; + } else { + address = read_memory_uint64_t(address, &error); + if (error) + break; + } + address += table->offsets[i]; + } + + // Set the old value to the current value + lua_getglobal(L, "current"); + if (lua_istable(L, -1)) { + lua_getfield(L, -1, table_name); + if (!lua_isnil(L, -1)) { + lua_getglobal(L, "old"); + lua_pushvalue(L, -2); + lua_setfield(L, -2, table_name); + lua_pop(L, 1); // Old table + } + lua_pop(L, 1); // current value or nil } - address += lua_tointeger(L, i); - } - if (strcmp(value_type, "sbyte") == 0) { - int8_t value = read_memory_int8_t(address, &error); - lua_pushinteger(L, (int)value); - } else if (strcmp(value_type, "byte") == 0) { - uint8_t value = read_memory_uint8_t(address, &error); - lua_pushinteger(L, (int)value); - } else if (strcmp(value_type, "short") == 0) { - short value = read_memory_int16_t(address, &error); - lua_pushinteger(L, (int)value); - } else if (strcmp(value_type, "ushort") == 0) { - unsigned short value = read_memory_uint16_t(address, &error); - lua_pushinteger(L, (int)value); - } else if (strcmp(value_type, "int") == 0) { - int value = read_memory_int32_t(address, &error); - lua_pushinteger(L, value); - } else if (strcmp(value_type, "uint") == 0) { - unsigned int value = read_memory_uint32_t(address, &error); - lua_pushinteger(L, (int)value); - } else if (strcmp(value_type, "long") == 0) { - long value = read_memory_int64_t(address, &error); - lua_pushinteger(L, (int)value); - } else if (strcmp(value_type, "ulong") == 0) { - unsigned long value = read_memory_uint64_t(address, &error); - lua_pushinteger(L, (int)value); - } else if (strcmp(value_type, "float") == 0) { - float value = read_memory_float(address, &error); - lua_pushnumber(L, (double)value); - } else if (strcmp(value_type, "double") == 0) { - double value = read_memory_double(address, &error); - lua_pushnumber(L, value); - } else if (strcmp(value_type, "bool") == 0) { - bool value = read_memory_bool(address, &error); - lua_pushboolean(L, value ? 1 : 0); - } else if (strstr(value_type, "string") != NULL) { - int buffer_size = atoi(value_type + 6); - if (buffer_size < 2) { - printf("Invalid string size, please read documentation"); + // Read the new value + if (strcmp(value_type, "sbyte") == 0) { + int8_t value = read_memory_int8_t(address, &error); + lua_pushinteger(L, (int)value); + } else if (strcmp(value_type, "byte") == 0) { + uint8_t value = read_memory_uint8_t(address, &error); + lua_pushinteger(L, (int)value); + } else if (strcmp(value_type, "short") == 0) { + short value = read_memory_int16_t(address, &error); + lua_pushinteger(L, (int)value); + } else if (strcmp(value_type, "ushort") == 0) { + unsigned short value = read_memory_uint16_t(address, &error); + lua_pushinteger(L, (int)value); + } else if (strcmp(value_type, "int") == 0) { + int value = read_memory_int32_t(address, &error); + lua_pushinteger(L, value); + } else if (strcmp(value_type, "uint") == 0) { + unsigned int value = read_memory_uint32_t(address, &error); + lua_pushinteger(L, (int)value); + } else if (strcmp(value_type, "long") == 0) { + long value = read_memory_int64_t(address, &error); + lua_pushinteger(L, (int)value); + } else if (strcmp(value_type, "ulong") == 0) { + unsigned long value = read_memory_uint64_t(address, &error); + lua_pushinteger(L, (int)value); + } else if (strcmp(value_type, "float") == 0) { + float value = read_memory_float(address, &error); + lua_pushnumber(L, (double)value); + } else if (strcmp(value_type, "double") == 0) { + double value = read_memory_double(address, &error); + lua_pushnumber(L, value); + } else if (strcmp(value_type, "bool") == 0) { + bool value = read_memory_bool(address, &error); + lua_pushboolean(L, value ? 1 : 0); + } else if (strstr(value_type, "string") != NULL) { + int buffer_size = atoi(value_type + 6); + if (buffer_size < 2) { + printf("Invalid string size, please read documentation"); + exit(1); + } + char* value = read_memory_string(address, buffer_size, &error); + lua_pushstring(L, value != NULL ? value : ""); + free(value); + } else { + printf("Invalid value type: %s\n", value_type); exit(1); } - char* value = read_memory_string(address, buffer_size); - lua_pushstring(L, value != NULL ? value : ""); - free(value); - return 1; - } else { - printf("Invalid value type: %s\n", value_type); - exit(1); + + if (error) { + handle_memory_error(error); + continue; + } + + lua_setfield(L, -2, table_name); + lua_pop(L, 1); // current table + } +} + +void store_memory_tables(lua_State* L) +{ + memory_table_count = 0; + + lua_getglobal(L, "memory"); + + if (!lua_istable(L, -1)) { + lua_pop(L, 1); + return; } - if (memory_error) { - lua_pushinteger(L, -1); - handle_memory_error(error); + // Iterate over each entry in the memory table + lua_pushnil(L); // Push nil to start iteration + while (lua_next(L, -2) != 0) { + if (lua_istable(L, -1)) { + // Get the name of the memory table (key) + const char* table_name; + if (lua_isstring(L, -2)) { + table_name = lua_tostring(L, -2); + } else { + // Invalid table name, skip this entry + lua_pop(L, 1); + continue; + } + + MemoryTable* table = &memory_tables[memory_table_count++]; + table->name = strdup(table_name); + table->module = NULL; + + if (lua_istable(L, -1)) { + // Type + lua_pushinteger(L, 1); + lua_gettable(L, -2); + const char* type = lua_tostring(L, -1); + lua_pop(L, 1); + + // Address/Module + lua_pushinteger(L, 2); + lua_gettable(L, -2); + if (lua_isnumber(L, -1)) { // Address + table->type = strdup(type); + table->address = lua_tointeger(L, -1); + } else if (lua_isstring(L, -1)) { // Module + table->module = strdup(lua_tostring(L, -1)); + + // Check if there's an address following the module + lua_pushinteger(L, 3); + lua_gettable(L, -3); + if (lua_isnumber(L, -1)) { + table->type = strdup(type); + table->address = lua_tointeger(L, -1); + } else { + table->type = NULL; + table->address = 0; + } + lua_pop(L, 1); // address/module value + } else { + // Invalid address/module + free(table->name); + memory_table_count--; + lua_pop(L, 1); + continue; + } + lua_pop(L, 1); // address/module value + } else { + // Invalid memory table + free(table->name); + memory_table_count--; + lua_pop(L, 1); + continue; + } + + // Offsets + table->offset_count = 0; + int i = 3; + if (table->module) { + i = 4; + } + for (; i <= MAX_OFFSETS + 2; i++) { + lua_pushinteger(L, i); + lua_gettable(L, -2); + if (!lua_isnil(L, -1)) { + table->offsets[table->offset_count++] = lua_tointeger(L, -1); + } + lua_pop(L, 1); // offset value + } + } + lua_pop(L, 1); } - return 1; + lua_pop(L, 1); } diff --git a/src/memory.h b/src/memory.h index 4173ed8..8ca390f 100644 --- a/src/memory.h +++ b/src/memory.h @@ -2,11 +2,24 @@ #define __MEMORY_H__ #include +#include #include #include -ssize_t process_vm_readv(int pid, struct iovec* mem_local, int liovcnt, struct iovec* mem_remote, int riovcnt, int flags); +#define MAX_OFFSETS 10 +#define MAX_MEMORY_TABLES 100 + +typedef struct { + char* name; + char* type; + char* module; + uint64_t address; + int offsets[MAX_OFFSETS]; + int offset_count; +} MemoryTable; -int read_address(lua_State* L); +ssize_t process_vm_readv(int pid, struct iovec* mem_local, int liovcnt, struct iovec* mem_remote, int riovcnt, int flags); +void store_memory_tables(lua_State* L); +void read_address(lua_State* L); #endif /* __MEMORY_H__ */ From 5713dab60e1e5b5e1c29054ec66f62e6975bbfe7 Mon Sep 17 00:00:00 2001 From: wins1ey Date: Thu, 6 Jun 2024 23:05:32 +0100 Subject: [PATCH 10/16] Push global table 'vars' to auto splitter script --- src/auto-splitter.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index 20f11c9..7d374b8 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -441,6 +441,8 @@ void run_auto_splitter() disable_functions(L, disabled_functions); lua_pushcfunction(L, getPid); lua_setglobal(L, "getPID"); + lua_newtable(L); + lua_setglobal(L, "vars"); char current_file[PATH_MAX]; strcpy(current_file, auto_splitter_file); From 086be29818f34fdd57dc4784e0391f05437786dc Mon Sep 17 00:00:00 2001 From: wins1ey Date: Fri, 7 Jun 2024 16:56:42 +0100 Subject: [PATCH 11/16] Define process and memory mappings in state table --- src/auto-splitter.c | 103 +++++++++++++++++++----------------- src/memory.c | 125 +++++++++++++++++++++----------------------- src/memory.h | 2 +- src/process.c | 63 +++++++++++++--------- src/process.h | 2 +- 5 files changed, 154 insertions(+), 141 deletions(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index 7d374b8..54bdd5a 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -246,7 +246,26 @@ bool call_va(lua_State* L, const char* func, const char* sig, ...) return true; } -bool startup(lua_State* L) +int get_process_names(lua_State* L, char process_names[100][256], int* num_process_names) +{ + lua_getglobal(L, "state"); + if (lua_istable(L, -1)) { + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_istable(L, -1)) { + const char* process_name = lua_tostring(L, -2); + + strncpy(process_names[*num_process_names], process_name, 255); + process_names[*num_process_names][255] = '\0'; + (*num_process_names)++; + } + lua_pop(L, 1); + } + } + return *num_process_names; +} + +void startup(lua_State* L) { call_va(L, "startup", ""); @@ -262,39 +281,6 @@ bool startup(lua_State* L) maps_cache_cycles_value = maps_cache_cycles; } lua_pop(L, 1); // Remove 'mapsCacheCycles' from the stack - - lua_getglobal(L, "process"); - if (lua_isstring(L, -1)) { - process.name = lua_tostring(L, -1); - if (process.name != NULL && find_process_id(L)) { - lua_pop(L, 1); - return true; - } - } - lua_pop(L, 1); // Remove 'process' from the stack - return false; -} - -bool state(lua_State* L) -{ - lua_newtable(L); - lua_setglobal(L, "memory"); - call_va(L, "state", ""); - lua_getglobal(L, "process"); - if (lua_isstring(L, -1)) { - process.name = lua_tostring(L, -1); - if (process.name != NULL && find_process_id(L)) { - lua_pop(L, 1); - lua_newtable(L); - lua_setglobal(L, "old"); - lua_newtable(L); - lua_setglobal(L, "current"); - store_memory_tables(L); - return true; - } - } - lua_pop(L, 1); // Remove 'process' from the stack - return false; } bool update(lua_State* L) @@ -360,6 +346,15 @@ bool reset(lua_State* L) return false; } +const char* version(lua_State* L) +{ + lua_getglobal(L, "version"); + if (lua_isstring(L, -1)) { + return lua_tostring(L, -1); + } + return NULL; +} + bool lua_function_exists(lua_State* L, const char* func_name) { lua_getglobal(L, func_name); @@ -370,7 +365,7 @@ bool lua_function_exists(lua_State* L, const char* func_name) void run_auto_splitter_cycle( lua_State* L, - bool state_exists, + bool memory_map_exists, bool start_exists, bool on_start_exists, bool split_exists, @@ -380,13 +375,11 @@ void run_auto_splitter_cycle( bool on_reset_exists, bool update_exists) { - if (update_exists && !update(L)) { + if ((update_exists && !update(L)) || !memory_map_exists) { return; } - if (state_exists) { - read_address(L); - } + read_address(L); if (is_loading_exists) { is_loading(L); @@ -447,7 +440,6 @@ void run_auto_splitter() char current_file[PATH_MAX]; strcpy(current_file, auto_splitter_file); - bool state_exists = lua_function_exists(L, "state"); bool start_exists = lua_function_exists(L, "start"); bool on_start_exists = lua_function_exists(L, "onStart"); bool split_exists = lua_function_exists(L, "split"); @@ -458,25 +450,40 @@ void run_auto_splitter() bool on_reset_exists = lua_function_exists(L, "onReset"); bool update_exists = lua_function_exists(L, "update"); bool init_exists = lua_function_exists(L, "init"); + bool memory_map_exists = false; - if (startup_exists && startup(L)) { - if (init_exists) { - call_va(L, "init", ""); - } + if (startup_exists) { + startup(L); } printf("Refresh rate: %d\n", refresh_rate); int rate = 1000000 / refresh_rate; - if (state_exists && !state(L)) { - state_exists = false; + char process_names[100][256]; + int num_process_names = 0; + const char* version_str; + if (get_process_names(L, process_names, &num_process_names)) { + if (find_process_id(process_names, num_process_names)) { + if (init_exists) { + call_va(L, "init", ""); + } + lua_newtable(L); + lua_setglobal(L, "old"); + lua_newtable(L); + lua_setglobal(L, "current"); + version_str = version(L); + if (version_str) { + printf("Version: %s\n", version_str); + } + memory_map_exists = store_memory_tables(L, version_str); + } } while (1) { struct timespec clock_start; clock_gettime(CLOCK_MONOTONIC, &clock_start); - if (!process_exists() && find_process_id(L) && init_exists) { + if (!process_exists() && find_process_id(process_names, num_process_names) && init_exists) { call_va(L, "init", ""); } else if (!atomic_load(&auto_splitter_enabled) || strcmp(current_file, auto_splitter_file) != 0) { break; @@ -484,7 +491,7 @@ void run_auto_splitter() run_auto_splitter_cycle( L, - state_exists, + memory_map_exists, start_exists, on_start_exists, split_exists, diff --git a/src/memory.c b/src/memory.c index 8e996fd..a7ab9a8 100644 --- a/src/memory.c +++ b/src/memory.c @@ -84,10 +84,11 @@ char* read_memory_string(uint64_t mem_address, int buffer_size, int32_t* err) True if the error was printed False if the error is unknown */ -bool handle_memory_error(uint32_t err) +bool handle_memory_error(uint32_t err, const char* table_name) { if (err == 0) return false; + printf("%s\t", table_name); switch (err) { case EFAULT: printf("EFAULT: Invalid memory space/address\n"); @@ -202,7 +203,7 @@ void read_address(lua_State* L) } if (error) { - handle_memory_error(error); + handle_memory_error(error, table_name); continue; } @@ -211,95 +212,89 @@ void read_address(lua_State* L) } } -void store_memory_tables(lua_State* L) +int store_memory_tables(lua_State* L, const char* version) { memory_table_count = 0; - lua_getglobal(L, "memory"); + lua_getglobal(L, "state"); + lua_getfield(L, -1, process.name); - if (!lua_istable(L, -1)) { - lua_pop(L, 1); - return; + if (version != NULL) { + lua_getfield(L, -1, version); + if (!lua_istable(L, -1)) { + lua_pop(L, 3); // Pop the non-tables + printf("Could not find version table %s for process %s\n", version, process.name); + return 0; + } } - // Iterate over each entry in the memory table - lua_pushnil(L); // Push nil to start iteration + lua_pushnil(L); while (lua_next(L, -2) != 0) { if (lua_istable(L, -1)) { - // Get the name of the memory table (key) const char* table_name; if (lua_isstring(L, -2)) { table_name = lua_tostring(L, -2); } else { - // Invalid table name, skip this entry lua_pop(L, 1); continue; } MemoryTable* table = &memory_tables[memory_table_count++]; table->name = strdup(table_name); + table->type = NULL; table->module = NULL; + table->address = 0; + table->offset_count = 0; - if (lua_istable(L, -1)) { - // Type - lua_pushinteger(L, 1); - lua_gettable(L, -2); - const char* type = lua_tostring(L, -1); - lua_pop(L, 1); - - // Address/Module - lua_pushinteger(L, 2); - lua_gettable(L, -2); - if (lua_isnumber(L, -1)) { // Address - table->type = strdup(type); - table->address = lua_tointeger(L, -1); - } else if (lua_isstring(L, -1)) { // Module - table->module = strdup(lua_tostring(L, -1)); - - // Check if there's an address following the module - lua_pushinteger(L, 3); - lua_gettable(L, -3); - if (lua_isnumber(L, -1)) { - table->type = strdup(type); - table->address = lua_tointeger(L, -1); - } else { - table->type = NULL; - table->address = 0; + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_istable(L, -1)) { + printf("No version defined in auto splitter\n"); + return 0; + } + if (lua_isnumber(L, -2)) { + int key = lua_tointeger(L, -2); + if (key == 1) { // Type + table->type = strdup(lua_tostring(L, -1)); + } else if (key == 2) { // Module or Address + if (lua_isnumber(L, -1)) { // Address + table->address = lua_tointeger(L, -1); + } else if (lua_isstring(L, -1)) { // Module + table->module = strdup(lua_tostring(L, -1)); + } + } else if (key == 3 && table->module != NULL) { // Address when module exists + if (lua_isnumber(L, -1)) { + table->address = lua_tointeger(L, -1); + } + } else if ((key > 2 && key <= MAX_OFFSETS + 2 && table->module == NULL) || (key > 3 && key <= MAX_OFFSETS + 3 && table->module != NULL)) { // Offsets + if (lua_isnumber(L, -1)) { + table->offsets[table->offset_count++] = lua_tointeger(L, -1); + } } - lua_pop(L, 1); // address/module value - } else { - // Invalid address/module - free(table->name); - memory_table_count--; - lua_pop(L, 1); - continue; } - lua_pop(L, 1); // address/module value - } else { - // Invalid memory table - free(table->name); - memory_table_count--; lua_pop(L, 1); - continue; - } - - // Offsets - table->offset_count = 0; - int i = 3; - if (table->module) { - i = 4; - } - for (; i <= MAX_OFFSETS + 2; i++) { - lua_pushinteger(L, i); - lua_gettable(L, -2); - if (!lua_isnil(L, -1)) { - table->offsets[table->offset_count++] = lua_tointeger(L, -1); - } - lua_pop(L, 1); // offset value } } lua_pop(L, 1); } - lua_pop(L, 1); + if (version != NULL) { + lua_pop(L, 3); // version, process, and state tables + } else { + lua_pop(L, 2); // process and state tables + } + + for (int i = 0; i < memory_table_count; i++) { + MemoryTable* table = &memory_tables[i]; + printf("Name: %s, ", table->name); + printf("Type: %s, ", table->type); + printf("Module: %s, ", table->module); + printf("Address: %ld, ", table->address); + printf("Offsets: "); + for (int j = 0; j < table->offset_count; j++) { + printf("%d ", table->offsets[j]); + } + printf("\n"); + } + return memory_table_count; } diff --git a/src/memory.h b/src/memory.h index 8ca390f..9891755 100644 --- a/src/memory.h +++ b/src/memory.h @@ -19,7 +19,7 @@ typedef struct { } MemoryTable; ssize_t process_vm_readv(int pid, struct iovec* mem_local, int liovcnt, struct iovec* mem_remote, int riovcnt, int flags); -void store_memory_tables(lua_State* L); +int store_memory_tables(lua_State* L, const char* version); void read_address(lua_State* L); #endif /* __MEMORY_H__ */ diff --git a/src/process.c b/src/process.c index 91bfd9a..7fa7de4 100644 --- a/src/process.c +++ b/src/process.c @@ -76,44 +76,55 @@ uintptr_t find_base_address(const char* module) return 0; } -int stock_process_id(const char* pid_command) +int stock_process_id(const char* pid_command, const char* process_name) { char pid_output[PATH_MAX + 100]; pid_output[0] = '\0'; - while (atomic_load(&auto_splitter_enabled)) { - execute_command(pid_command, pid_output); - process.pid = strtoul(pid_output, NULL, 10); - if (process.pid) { - size_t newlinePos = strcspn(pid_output, "\n"); - if (newlinePos != strlen(pid_output) - 1 && pid_output[0] != '\0') { - printf("Multiple PID's found for process: %s\n", process.name); - } - break; - } else { - printf("%s isn't running.\r", process.name); - fflush(stdout); - usleep(100000); // Sleep for 100ms - } - } - + execute_command(pid_command, pid_output); + process.pid = strtoul(pid_output, NULL, 10); if (process.pid) { - printf("\r\033[KProcess: %s\n", process.name); - printf("PID: %u\n", process.pid); - process.base_address = find_base_address(NULL); - process.dll_address = process.base_address; + process.name = process_name; return 1; } - printf("\nCouldn't find process: %s\n", process.name); return 0; } -int find_process_id(lua_State* L) +int find_process_id(char process_names[100][256], int num_process_names) { - char command[256]; - snprintf(command, sizeof(command), "pgrep \"%.*s\"", (int)strnlen(process.name, 15), process.name); + int found = 0; + + while (atomic_load(&auto_splitter_enabled)) { + printf("\r\033[KSearching for processes: "); + for (int i = 0; i < num_process_names; i++) { + printf("%s", process_names[i]); + if (i < num_process_names - 1) { + printf(", "); + } + } + fflush(stdout); + + for (int i = 0; i < num_process_names; i++) { + char command[256]; + snprintf(command, sizeof(command), "pgrep \"%.*s\"", (int)strnlen(process_names[i], 15), process_names[i]); + + if (stock_process_id(command, process_names[i])) { + found = 1; + break; + } + } - return stock_process_id(command); + if (found) { + printf("\r\033[KProcess: %s\n", process.name); + printf("PID: %u\n", process.pid); + process.base_address = find_base_address(NULL); + process.dll_address = process.base_address; + return 1; + } else { + usleep(100000); // Sleep for 100ms before retrying + } + } + return 0; } int getPid(lua_State* L) diff --git a/src/process.h b/src/process.h index 28000e9..db232ab 100644 --- a/src/process.h +++ b/src/process.h @@ -24,7 +24,7 @@ extern uint32_t p_maps_cache_size; uintptr_t find_base_address(const char* module); int process_exists(); -int find_process_id(lua_State* L); +int find_process_id(char process_names[100][256], int num_process_names); int getPid(lua_State* L); bool parseMapsLine(char* line, ProcessMap* map); From 1508096d6028484efeab4be563b2af8743281324 Mon Sep 17 00:00:00 2001 From: wins1ey Date: Sat, 8 Jun 2024 01:16:43 +0100 Subject: [PATCH 12/16] Clear lua stack properly --- src/auto-splitter.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index 54bdd5a..8abaa35 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -262,6 +262,7 @@ int get_process_names(lua_State* L, char process_names[100][256], int* num_proce lua_pop(L, 1); } } + lua_pop(L, 1); return *num_process_names; } @@ -350,8 +351,11 @@ const char* version(lua_State* L) { lua_getglobal(L, "version"); if (lua_isstring(L, -1)) { - return lua_tostring(L, -1); + const char* version = lua_tostring(L, -1); + lua_pop(L, 1); + return version; } + lua_pop(L, 1); return NULL; } From 4081c06339f33e3fd495ef541db81d06eb65ba48 Mon Sep 17 00:00:00 2001 From: wins1ey Date: Sat, 8 Jun 2024 01:25:41 +0100 Subject: [PATCH 13/16] Improve parsing of state table in auto splitters It now allows for it to be formatted like state = { ["AmidEvil-Win64-Shipping"] = { Start = { 'byte', 0x2BAFFD0 }, Loading = { 'byte', 0x2E76B0C }, MenuStage = { 'byte', 0x2F75F14 }, Paused = { 'byte', 0x2B95A68 }, ["Steam"] = { Start = { 'byte', 0x2BB3470 }, Loading = { 'byte', 0x2B7DDD4 }, MenuStage = { 'byte', 0x2F79424 }, Paused = { 'byte', 0x2B98F08 } } } } This allows you to set values for when no version is defined, alongside ones for specific versions --- src/memory.c | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/memory.c b/src/memory.c index a7ab9a8..0837095 100644 --- a/src/memory.c +++ b/src/memory.c @@ -230,7 +230,20 @@ int store_memory_tables(lua_State* L, const char* version) lua_pushnil(L); while (lua_next(L, -2) != 0) { + int has_nested_table = 0; if (lua_istable(L, -1)) { + lua_pushnil(L); + while (lua_next(L, -2) != 0) { + if (lua_istable(L, -1)) { + has_nested_table = 1; + lua_pop(L, 2); // Nested table and its key + break; + } + lua_pop(L, 1); + } + } + + if (!has_nested_table) { const char* table_name; if (lua_isstring(L, -2)) { table_name = lua_tostring(L, -2); @@ -238,7 +251,6 @@ int store_memory_tables(lua_State* L, const char* version) lua_pop(L, 1); continue; } - MemoryTable* table = &memory_tables[memory_table_count++]; table->name = strdup(table_name); table->type = NULL; @@ -248,10 +260,6 @@ int store_memory_tables(lua_State* L, const char* version) lua_pushnil(L); while (lua_next(L, -2) != 0) { - if (lua_istable(L, -1)) { - printf("No version defined in auto splitter\n"); - return 0; - } if (lua_isnumber(L, -2)) { int key = lua_tointeger(L, -2); if (key == 1) { // Type From c4a692a902a092c1674ff963ceb1f717ab2fa3d1 Mon Sep 17 00:00:00 2001 From: wins1ey Date: Sat, 8 Jun 2024 01:26:58 +0100 Subject: [PATCH 14/16] Clean up format of console output for memory tables --- src/memory.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/memory.c b/src/memory.c index 0837095..fbcb053 100644 --- a/src/memory.c +++ b/src/memory.c @@ -294,13 +294,16 @@ int store_memory_tables(lua_State* L, const char* version) for (int i = 0; i < memory_table_count; i++) { MemoryTable* table = &memory_tables[i]; - printf("Name: %s, ", table->name); - printf("Type: %s, ", table->type); - printf("Module: %s, ", table->module); - printf("Address: %ld, ", table->address); - printf("Offsets: "); - for (int j = 0; j < table->offset_count; j++) { - printf("%d ", table->offsets[j]); + printf("%s ", table->name); + printf("%s ", table->type); + if (table->module) { + printf("%s ", table->module); + } + printf("0x%lX ", table->address); + if (table->offset_count) { + for (int i = 0; i < table->offset_count; i++) { + printf("0x%X ", table->offsets[i]); + } } printf("\n"); } From e7cd28af5faf1ffa667ba7f61dc880a07cb42330 Mon Sep 17 00:00:00 2001 From: wins1ey Date: Sat, 8 Jun 2024 16:01:05 +0100 Subject: [PATCH 15/16] Update global function, variable and table capitalisations in auto splitters Functions such as 'split' must now be 'Split' Variables such as 'refreshRate' must now be 'RefreshRate' The 'state' table must now be 'State' --- src/auto-splitter.c | 62 ++++++++++++++++++++++----------------------- src/memory.c | 2 +- 2 files changed, 31 insertions(+), 33 deletions(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index 8abaa35..e8fc01c 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -248,7 +248,7 @@ bool call_va(lua_State* L, const char* func, const char* sig, ...) int get_process_names(lua_State* L, char process_names[100][256], int* num_process_names) { - lua_getglobal(L, "state"); + lua_getglobal(L, "State"); if (lua_istable(L, -1)) { lua_pushnil(L); while (lua_next(L, -2) != 0) { @@ -268,15 +268,15 @@ int get_process_names(lua_State* L, char process_names[100][256], int* num_proce void startup(lua_State* L) { - call_va(L, "startup", ""); + call_va(L, "Startup", ""); - lua_getglobal(L, "refreshRate"); + lua_getglobal(L, "RefreshRate"); if (lua_isnumber(L, -1)) { refresh_rate = lua_tointeger(L, -1); } lua_pop(L, 1); // Remove 'refreshRate' from the stack - lua_getglobal(L, "mapsCacheCycles"); + lua_getglobal(L, "MapsCacheCycles"); if (lua_isnumber(L, -1)) { maps_cache_cycles = lua_tointeger(L, -1); maps_cache_cycles_value = maps_cache_cycles; @@ -287,7 +287,7 @@ void startup(lua_State* L) bool update(lua_State* L) { bool ret; - if (call_va(L, "update", ">b", &ret)) { + if (call_va(L, "Update", ">b", &ret)) { lua_pop(L, 1); return ret; } @@ -298,9 +298,9 @@ bool update(lua_State* L) bool start(lua_State* L) { bool ret; - if (call_va(L, "start", ">b", &ret) && ret) { + if (call_va(L, "Start", ">b", &ret) && ret) { atomic_store(&call_start, true); - printf("start: true\n"); + printf("Start: true\n"); lua_pop(L, 1); return true; } @@ -311,9 +311,9 @@ bool start(lua_State* L) bool split(lua_State* L) { bool ret; - if (call_va(L, "split", ">b", &ret) && ret) { + if (call_va(L, "Split", ">b", &ret) && ret) { atomic_store(&call_split, true); - printf("split: true\n"); + printf("Split: true\n"); lua_pop(L, 1); return true; } @@ -324,10 +324,10 @@ bool split(lua_State* L) void is_loading(lua_State* L) { bool ret; - if (call_va(L, "isLoading", ">b", &ret)) { + if (call_va(L, "IsLoading", ">b", &ret)) { if (ret != prev_is_loading) { atomic_store(&toggle_loading, true); - printf("isLoading: %s\n", ret ? "true" : "false"); + printf("IsLoading: %s\n", ret ? "true" : "false"); prev_is_loading = !prev_is_loading; } } @@ -337,9 +337,9 @@ void is_loading(lua_State* L) bool reset(lua_State* L) { bool ret; - if (call_va(L, "reset", ">b", &ret) && ret) { + if (call_va(L, "Reset", ">b", &ret) && ret) { atomic_store(&call_reset, true); - printf("reset: true\n"); + printf("Reset: true\n"); lua_pop(L, 1); return true; } @@ -349,10 +349,11 @@ bool reset(lua_State* L) const char* version(lua_State* L) { - lua_getglobal(L, "version"); + lua_getglobal(L, "Version"); if (lua_isstring(L, -1)) { const char* version = lua_tostring(L, -1); lua_pop(L, 1); + printf("Version: %s\n", version); return version; } lua_pop(L, 1); @@ -392,16 +393,16 @@ void run_auto_splitter_cycle( if (!atomic_load(&timer_started)) { if (start_exists && start(L)) { if (on_start_exists) { - call_va(L, "onStart", ""); + call_va(L, "OnStart", ""); } } } else if (reset_exists && reset(L)) { if (on_reset_exists) { - call_va(L, "onReset", ""); + call_va(L, "OnReset", ""); } } else if (split_exists && split(L)) { if (on_split_exists) { - call_va(L, "onSplit", ""); + call_va(L, "OnSplit", ""); } } @@ -444,16 +445,16 @@ void run_auto_splitter() char current_file[PATH_MAX]; strcpy(current_file, auto_splitter_file); - bool start_exists = lua_function_exists(L, "start"); - bool on_start_exists = lua_function_exists(L, "onStart"); - bool split_exists = lua_function_exists(L, "split"); - bool on_split_exists = lua_function_exists(L, "onSplit"); - bool is_loading_exists = lua_function_exists(L, "isLoading"); - bool startup_exists = lua_function_exists(L, "startup"); - bool reset_exists = lua_function_exists(L, "reset"); - bool on_reset_exists = lua_function_exists(L, "onReset"); - bool update_exists = lua_function_exists(L, "update"); - bool init_exists = lua_function_exists(L, "init"); + bool start_exists = lua_function_exists(L, "Start"); + bool on_start_exists = lua_function_exists(L, "OnStart"); + bool split_exists = lua_function_exists(L, "Split"); + bool on_split_exists = lua_function_exists(L, "OnSplit"); + bool is_loading_exists = lua_function_exists(L, "IsLoading"); + bool startup_exists = lua_function_exists(L, "Startup"); + bool reset_exists = lua_function_exists(L, "Reset"); + bool on_reset_exists = lua_function_exists(L, "OnReset"); + bool update_exists = lua_function_exists(L, "Update"); + bool init_exists = lua_function_exists(L, "Init"); bool memory_map_exists = false; if (startup_exists) { @@ -469,16 +470,13 @@ void run_auto_splitter() if (get_process_names(L, process_names, &num_process_names)) { if (find_process_id(process_names, num_process_names)) { if (init_exists) { - call_va(L, "init", ""); + call_va(L, "Init", ""); } lua_newtable(L); lua_setglobal(L, "old"); lua_newtable(L); lua_setglobal(L, "current"); version_str = version(L); - if (version_str) { - printf("Version: %s\n", version_str); - } memory_map_exists = store_memory_tables(L, version_str); } } @@ -488,7 +486,7 @@ void run_auto_splitter() clock_gettime(CLOCK_MONOTONIC, &clock_start); if (!process_exists() && find_process_id(process_names, num_process_names) && init_exists) { - call_va(L, "init", ""); + call_va(L, "Init", ""); } else if (!atomic_load(&auto_splitter_enabled) || strcmp(current_file, auto_splitter_file) != 0) { break; } diff --git a/src/memory.c b/src/memory.c index fbcb053..eaa9845 100644 --- a/src/memory.c +++ b/src/memory.c @@ -216,7 +216,7 @@ int store_memory_tables(lua_State* L, const char* version) { memory_table_count = 0; - lua_getglobal(L, "state"); + lua_getglobal(L, "State"); lua_getfield(L, -1, process.name); if (version != NULL) { From 0bc15df466c26bd45ae3f670b06145dcda2df2be Mon Sep 17 00:00:00 2001 From: wins1ey Date: Mon, 10 Jun 2024 13:55:13 +0100 Subject: [PATCH 16/16] Update OnStart, OnSplit, OnReset to call when performed manually --- src/auto-splitter.c | 39 +++++++++++++++++++++++++++------------ src/auto-splitter.h | 3 +++ src/timer.c | 7 +++++++ 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/auto-splitter.c b/src/auto-splitter.c index e8fc01c..a73a61c 100644 --- a/src/auto-splitter.c +++ b/src/auto-splitter.c @@ -24,9 +24,12 @@ int maps_cache_cycles_value = 0; // same as `maps_cache_cycles` but this one rep atomic_bool timer_started = false; atomic_bool auto_splitter_enabled = true; atomic_bool call_start = false; +atomic_bool call_on_start = false; atomic_bool call_split = false; +atomic_bool call_on_split = false; atomic_bool toggle_loading = false; atomic_bool call_reset = false; +atomic_bool call_on_reset = false; bool prev_is_loading; static const char* disabled_functions[] = { @@ -391,19 +394,28 @@ void run_auto_splitter_cycle( } if (!atomic_load(&timer_started)) { - if (start_exists && start(L)) { - if (on_start_exists) { - call_va(L, "OnStart", ""); - } - } - } else if (reset_exists && reset(L)) { - if (on_reset_exists) { - call_va(L, "OnReset", ""); - } - } else if (split_exists && split(L)) { - if (on_split_exists) { - call_va(L, "OnSplit", ""); + if (start_exists) { + start(L); } + } else if (reset_exists) { + reset(L); + } else if (split_exists) { + split(L); + } + + if (on_start_exists && atomic_load(&call_on_start)) { + call_va(L, "OnStart", ""); + atomic_store(&call_on_start, false); + } + + if (on_split_exists && atomic_load(&call_on_split)) { + call_va(L, "OnSplit", ""); + atomic_store(&call_on_split, false); + } + + if (on_reset_exists && atomic_load(&call_on_reset)) { + call_va(L, "OnReset", ""); + atomic_store(&call_on_reset, false); } // Clear the memory maps cache if needed @@ -456,6 +468,9 @@ void run_auto_splitter() bool update_exists = lua_function_exists(L, "Update"); bool init_exists = lua_function_exists(L, "Init"); bool memory_map_exists = false; + atomic_store(&call_on_start, false); + atomic_store(&call_on_split, false); + atomic_store(&call_on_reset, false); if (startup_exists) { startup(L); diff --git a/src/auto-splitter.h b/src/auto-splitter.h index 11853b7..78f4751 100644 --- a/src/auto-splitter.h +++ b/src/auto-splitter.h @@ -7,9 +7,12 @@ extern atomic_bool timer_started; extern atomic_bool auto_splitter_enabled; extern atomic_bool call_start; +extern atomic_bool call_on_start; extern atomic_bool call_split; +extern atomic_bool call_on_split; extern atomic_bool toggle_loading; extern atomic_bool call_reset; +extern atomic_bool call_on_reset; extern char auto_splitter_file[PATH_MAX]; extern int maps_cache_cycles_value; diff --git a/src/timer.c b/src/timer.c index a3c4888..fcc1016 100644 --- a/src/timer.c +++ b/src/timer.c @@ -1,10 +1,13 @@ #include "timer.h" #include +#include #include #include #include #include +#include "auto-splitter.h" + long long ls_time_now(void) { struct timespec timespec; @@ -597,6 +600,7 @@ int ls_timer_start(ls_timer* timer) if (!timer->started) { ++*timer->attempt_count; timer->started = 1; + atomic_store(&call_on_start, true); } timer->running = 1; } @@ -641,6 +645,7 @@ int ls_timer_split(ls_timer* timer) ls_timer_stop(timer); ls_game_update_splits((ls_game*)timer->game, timer); } + atomic_store(&call_on_split, true); return timer->curr_split; } } @@ -691,9 +696,11 @@ int ls_timer_reset(ls_timer* timer) { if (!timer->running) { if (timer->started && timer->time <= 0) { + atomic_store(&call_on_reset, true); return ls_timer_cancel(timer); } reset_timer(timer); + atomic_store(&call_on_reset, true); return 1; } return 0;